diff --git a/.github/mergify.yml b/.github/mergify.yml index 990703547dd..0d6c6f0e0d7 100644 --- a/.github/mergify.yml +++ b/.github/mergify.yml @@ -4,21 +4,25 @@ pull_request_rules: - # - name: Run Azure CI job once ready to merge - # conditions: - # - "#approved-reviews-by>=1" - # - "#changes-requested-reviews-by=0" - # - "check-success=license/cla" - # - -closed - # - -conflict - # actions: - # comments: - # message: /azp run - - name: update all PRs conditions: - "#changes-requested-reviews-by=0" - -draft - - base=master + - base~=master|release/* + - check-success=iModel.js + - check-success=iModel.js Integration - GitHub + - check-success=iModelJs Docs + - check-success=license/cla actions: update: {} + + # - name: automatic merge of native addon updates + # conditions: + # - head~=native/* + # - base=master + # # Mergify requests required status checks so no need to check here + # actions: + # merge: + # method: merge + # strict: true + # commit_message: "Update @bentley/imodeljs-native" diff --git a/.gitignore b/.gitignore index b111e709226..3c302116572 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,5 @@ package-deps.json .DS_Store .env + +common/api/summary/summary.exports.csv diff --git a/clients/backend-application-insights/package.json b/clients/backend-application-insights/package.json index b000ed515b8..ae13e33fae4 100644 --- a/clients/backend-application-insights/package.json +++ b/clients/backend-application-insights/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/backend-application-insights-client", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js Backend Application Insights Client", "main": "lib/backend-application-insights-client.js", "typings": "lib/backend-application-insights-client", @@ -30,19 +30,19 @@ "url": "http://www.bentley.com" }, "dependencies": { - "@bentley/backend-itwin-client": "2.15.0-dev.6", - "@bentley/bentleyjs-core": "2.15.0-dev.6", + "@bentley/backend-itwin-client": "2.15.0-dev.13", + "@bentley/bentleyjs-core": "2.15.0-dev.13", "applicationinsights": "^1.7.5" }, "peerDependencies": { - "@bentley/itwin-client": "^2.15.0-dev.6" + "@bentley/itwin-client": "^2.15.0-dev.13" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/certa": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/certa": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", "@types/node": "10.14.1", diff --git a/clients/context-registry/package.json b/clients/context-registry/package.json index 0563dc09c73..f10f5c700f3 100644 --- a/clients/context-registry/package.json +++ b/clients/context-registry/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/context-registry-client", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js Context Registry Client", "main": "lib/context-registry-client.js", "typings": "lib/context-registry-client", @@ -39,16 +39,16 @@ "deep-assign": "^2.0.0" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/itwin-client": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/itwin-client": "^2.15.0-dev.13" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/certa": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/oidc-signin-tool": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/certa": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/oidc-signin-tool": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/deep-assign": "^0.1.0", "@types/mocha": "^5.2.5", diff --git a/clients/extension/package.json b/clients/extension/package.json index 87d11c4ec89..8491c960a9b 100644 --- a/clients/extension/package.json +++ b/clients/extension/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/extension-client", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "IModel.js Extension Client", "main": "lib/extension-client.js", "typings": "lib/extension-client", @@ -36,17 +36,17 @@ "url": "http://www.bentley.com" }, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6" + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/certa": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/context-registry-client": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/oidc-signin-tool": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/certa": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/context-registry-client": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/oidc-signin-tool": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", "@types/node": "10.14.1", diff --git a/clients/frontend-application-insights/package.json b/clients/frontend-application-insights/package.json index c5b1559e974..c397923a4a8 100644 --- a/clients/frontend-application-insights/package.json +++ b/clients/frontend-application-insights/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/frontend-application-insights-client", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js Frontend Application Insights Client", "main": "lib/frontend-application-insights-client.js", "typings": "lib/frontend-application-insights-client", @@ -31,19 +31,19 @@ "url": "http://www.bentley.com" }, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/telemetry-client": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/telemetry-client": "2.15.0-dev.13", "@microsoft/applicationinsights-web": "^2.5.5" }, "peerDependencies": { - "@bentley/itwin-client": "^2.15.0-dev.6" + "@bentley/itwin-client": "^2.15.0-dev.13" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/certa": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/certa": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", "@types/node": "10.14.1", diff --git a/clients/frontend-authorization/package.json b/clients/frontend-authorization/package.json index ec76b5cc69a..fb3b45d280d 100644 --- a/clients/frontend-authorization/package.json +++ b/clients/frontend-authorization/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/frontend-authorization-client", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js Frontend Authorization Client", "main": "lib/frontend-authorization-client.js", "typings": "lib/frontend-authorization-client", @@ -34,14 +34,14 @@ "oidc-client": "^1.9.1" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/itwin-client": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/itwin-client": "^2.15.0-dev.13" }, "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", "@types/node": "10.14.1", "eslint": "^6.8.0", "rimraf": "^3.0.2", diff --git a/clients/imodelhub/package.json b/clients/imodelhub/package.json index a9506412be6..4e9db445cd9 100644 --- a/clients/imodelhub/package.json +++ b/clients/imodelhub/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/imodelhub-client", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js iModelHub Client", "main": "lib/imodelhub-client.js", "typings": "lib/imodelhub-client", @@ -33,23 +33,23 @@ "url": "http://www.bentley.com" }, "dependencies": { - "@bentley/context-registry-client": "2.15.0-dev.6", + "@bentley/context-registry-client": "2.15.0-dev.13", "deep-assign": "^2.0.0", "js-base64": "^2.4.5" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/frontend-authorization-client": "^2.15.0-dev.6", - "@bentley/itwin-client": "^2.15.0-dev.6", - "@bentley/rbac-client": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/frontend-authorization-client": "^2.15.0-dev.13", + "@bentley/itwin-client": "^2.15.0-dev.13", + "@bentley/rbac-client": "^2.15.0-dev.13" }, "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/frontend-authorization-client": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/rbac-client": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/frontend-authorization-client": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/rbac-client": "2.15.0-dev.13", "@types/deep-assign": "^0.1.0", "@types/js-base64": "^2.3.1", "@types/node": "10.14.1", diff --git a/clients/imodelhub/src/imodelhub/Versions.ts b/clients/imodelhub/src/imodelhub/Versions.ts index cfd4bed1063..501721b2bb4 100644 --- a/clients/imodelhub/src/imodelhub/Versions.ts +++ b/clients/imodelhub/src/imodelhub/Versions.ts @@ -43,7 +43,7 @@ export class Version extends WsgInstance { /** Id of the [[ChangeSet]] that the named Version was created for. */ @ECJsonTypeMap.propertyToJson("wsg", "properties.ChangeSetId") - public changeSetId?: GuidString; + public changeSetId?: string; /** Set to true, if named Version is hidden. */ @ECJsonTypeMap.propertyToJson("wsg", "properties.Hidden") diff --git a/clients/itwin/package.json b/clients/itwin/package.json index 65efebe62ae..bb3922a785b 100644 --- a/clients/itwin/package.json +++ b/clients/itwin/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/itwin-client", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "Base client package for iTwin applications", "main": "lib/itwin-client.js", "typings": "lib/itwin-client", @@ -30,18 +30,18 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13" }, "//devDependencies": [ "NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install", "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/certa": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/certa": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/deep-assign": "^0.1.0", "@types/js-base64": "^2.3.1", @@ -50,7 +50,7 @@ "@types/node": "10.14.1", "@types/qs": "^6.5.0", "@types/superagent": "^4.1.7", - "@types/xmldom": "^0.1.29", + "@types/xmldom": "^0.1.30", "chai": "^4.1.2", "eslint": "^6.8.0", "mocha": "^5.2.0", @@ -66,7 +66,7 @@ "js-base64": "^2.4.5", "qs": "^6.5.1", "superagent": "^5.2.2", - "xmldom": "^0.1.27", + "xmldom": "^0.5.0", "xpath": "0.0.27" }, "nyc": { diff --git a/clients/itwin/src/Token.ts b/clients/itwin/src/Token.ts index c57708bb179..895bef0c6bf 100644 --- a/clients/itwin/src/Token.ts +++ b/clients/itwin/src/Token.ts @@ -105,7 +105,6 @@ export class AccessToken { * Convert this AccessToken to a string that can be passed across the wire * Users should overwrite this method in a subclass of AccessToken if their token is not converted to a string in this way. * @param includePrefix Include the token prefix to identify the type of token - "Bearer" for JSON Web Tokens (JWTs) - * @beta */ public toTokenString(includePrefix: IncludePrefix = IncludePrefix.Yes): string { const jwt = this._tokenString; @@ -131,7 +130,6 @@ export class AccessToken { * - The token is NOT validated in any way other than the basic prefix check described above * @param tokenStr String representation of the token * @throws [[BentleyError]] If the token does not have the required prefix - * @beta */ public static fromTokenString(tokenStr: string): AccessToken { const accessToken: AccessToken = AccessToken.generateProperTokenType(tokenStr); @@ -152,7 +150,6 @@ export class AccessToken { * Creates a strongly typed AccessToken object from an untyped JSON with the same properties as [[AccessToken]] * @param jsonObj * @throws [BentleyError]($bentley) if the supplied tokenResponse is undefined, or does not contain an "access_token" field - * @beta */ public static fromJson(jsonObj: AccessTokenProps): AccessToken { const jwt = jsonObj.tokenString; @@ -172,7 +169,7 @@ export class AccessToken { } /** - * Creates AccessToken from the typical token responses obtained from Authorization servers + * Creates an AccessToken from the typical token responses obtained from Authorization servers * - The fields from the token response to different names in AccessToken to keep with naming guidelines * - Only basic validation is done - the input tokenResponse must be defined, and have an "access_token" field in it * @param tokenResponse Response containing the token string as obtained from the authorization server diff --git a/clients/product-settings/package.json b/clients/product-settings/package.json index 8e5e439e07b..fba92b08091 100644 --- a/clients/product-settings/package.json +++ b/clients/product-settings/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/product-settings-client", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js Product Settings Client", "main": "lib/product-settings-client.js", "typings": "lib/product-settings-client", @@ -36,18 +36,18 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/itwin-client": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/itwin-client": "^2.15.0-dev.13" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/certa": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/context-registry-client": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/imodelhub-client": "2.15.0-dev.6", - "@bentley/oidc-signin-tool": "2.15.0-dev.6", - "@bentley/rbac-client": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/certa": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/context-registry-client": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/imodelhub-client": "2.15.0-dev.13", + "@bentley/oidc-signin-tool": "2.15.0-dev.13", + "@bentley/rbac-client": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", "@types/node": "10.14.1", diff --git a/clients/projectshare/package.json b/clients/projectshare/package.json index a7748e5bd20..cc217a3f3b8 100644 --- a/clients/projectshare/package.json +++ b/clients/projectshare/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/projectshare-client", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js ProjectShare Client", "main": "lib/projectshare-client.js", "typings": "lib/projectshare-client", @@ -36,18 +36,18 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/itwin-client": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/itwin-client": "^2.15.0-dev.13" }, "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/certa": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/context-registry-client": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/oidc-signin-tool": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/certa": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/context-registry-client": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/oidc-signin-tool": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", "@types/node": "10.14.1", diff --git a/clients/rbac/package.json b/clients/rbac/package.json index 96ef6a47650..4b63c937763 100644 --- a/clients/rbac/package.json +++ b/clients/rbac/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/rbac-client", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js RBAC Client", "main": "lib/rbac-client.js", "typings": "lib/rbac-client", @@ -36,19 +36,19 @@ "url": "http://www.bentley.com" }, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6" + "@bentley/bentleyjs-core": "2.15.0-dev.13" }, "peerDependencies": { - "@bentley/itwin-client": "^2.15.0-dev.6" + "@bentley/itwin-client": "^2.15.0-dev.13" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/certa": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/context-registry-client": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/oidc-signin-tool": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/certa": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/context-registry-client": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/oidc-signin-tool": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", "@types/node": "10.14.1", diff --git a/clients/reality-data/package.json b/clients/reality-data/package.json index 02a428b5936..64b44b35ad8 100644 --- a/clients/reality-data/package.json +++ b/clients/reality-data/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/reality-data-client", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js Reality Data Client", "main": "lib/reality-data-client.js", "typings": "lib/reality-data-client", @@ -36,20 +36,20 @@ "url": "http://www.bentley.com" }, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6" + "@bentley/bentleyjs-core": "2.15.0-dev.13" }, "peerDependencies": { - "@bentley/itwin-client": "^2.15.0-dev.6" + "@bentley/itwin-client": "^2.15.0-dev.13" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/certa": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/context-registry-client": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/oidc-signin-tool": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/certa": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/context-registry-client": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/oidc-signin-tool": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", "@types/node": "10.14.1", diff --git a/clients/telemetry/package.json b/clients/telemetry/package.json index 79b4592ede1..3968a305b6f 100644 --- a/clients/telemetry/package.json +++ b/clients/telemetry/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/telemetry-client", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js Telemetry Client", "main": "lib/telemetry-client.js", "typings": "lib/telemetry-client", @@ -31,17 +31,17 @@ "url": "http://www.bentley.com" }, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6" + "@bentley/bentleyjs-core": "2.15.0-dev.13" }, "peerDependencies": { - "@bentley/itwin-client": "^2.15.0-dev.6" + "@bentley/itwin-client": "^2.15.0-dev.13" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/certa": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/certa": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", "@types/node": "10.14.1", diff --git a/clients/usage-logging/package.json b/clients/usage-logging/package.json index 744a4b86425..96a082fa399 100644 --- a/clients/usage-logging/package.json +++ b/clients/usage-logging/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/usage-logging-client", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js Usage Logging Client", "main": "lib/usage-logging-client.js", "typings": "lib/usage-logging-client", @@ -36,20 +36,20 @@ "url": "http://www.bentley.com" }, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6" + "@bentley/bentleyjs-core": "2.15.0-dev.13" }, "peerDependencies": { - "@bentley/itwin-client": "^2.15.0-dev.6", - "@bentley/telemetry-client": "^2.15.0-dev.6" + "@bentley/itwin-client": "^2.15.0-dev.13", + "@bentley/telemetry-client": "^2.15.0-dev.13" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/certa": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/oidc-signin-tool": "2.15.0-dev.6", - "@bentley/telemetry-client": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/certa": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/oidc-signin-tool": "2.15.0-dev.13", + "@bentley/telemetry-client": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", "@types/node": "10.14.1", diff --git a/common/api/bentleyjs-core.api.md b/common/api/bentleyjs-core.api.md index 12b434c398a..2cd99203c17 100644 --- a/common/api/bentleyjs-core.api.md +++ b/common/api/bentleyjs-core.api.md @@ -17,7 +17,7 @@ export function areEqualPossiblyUndefined(t: T | undefined, u: U | undefin // @public export function asInstanceOf(obj: any, constructor: Constructor): T | undefined; -// @beta +// @public export function assert(condition: boolean, msg?: string): asserts condition; // @alpha @@ -70,7 +70,7 @@ export class BeEvent { removeListener(listener: T, scope?: any): boolean; } -// @beta +// @public export class BeEventList { get(name: string): BeEvent; remove(name: string): void; @@ -146,7 +146,7 @@ export enum BriefcaseStatus { VersionNotFound = 131077 } -// @beta +// @public export class ByteStream { constructor(buffer: ArrayBuffer | SharedArrayBuffer, subView?: { byteOffset: number; @@ -165,7 +165,6 @@ export class ByteStream { get nextInt32(): number; get nextUint16(): number; get nextUint32(): number; - // (undocumented) nextUint32s(numUint32s: number): Uint32Array; get nextUint8(): number; readBytes(readPos: number, numBytes: number): Uint8Array; @@ -266,19 +265,17 @@ export function compareStringsOrUndefined(lhs?: string, rhs?: string): number; // @public export function compareWithTolerance(a: number, b: number, tolerance?: number): number; -// @beta +// @public (undocumented) export type CompressedId64Set = string; -// @beta +// @public export namespace CompressedId64Set { export function compressArray(ids: Id64Array): CompressedId64Set; export function compressIds(ids: OrderedId64Iterable): CompressedId64Set; export function compressSet(ids: Id64Set): CompressedId64Set; export function decompressArray(compressedIds: CompressedId64Set, out?: Id64Array): Id64Array; export function decompressSet(compressedIds: CompressedId64Set, out?: Id64Set): Id64Set; - // @alpha export function iterable(ids: CompressedId64Set): OrderedId64Iterable; - // @alpha export function iterator(ids: CompressedId64Set): Iterator; } @@ -1144,7 +1141,7 @@ export class LRUMap extends LRUCache { constructor(limit: number); } -// @alpha +// @public export class MutableCompressedId64Set implements OrderedId64Iterable { [Symbol.iterator](): Iterator; constructor(ids?: CompressedId64Set); @@ -1160,7 +1157,7 @@ export class MutableCompressedId64Set implements OrderedId64Iterable { reset(ids?: CompressedId64Set): void; } -// @beta +// @public export class ObservableSet extends Set { constructor(elements?: Iterable | undefined); // @internal (undocumented) @@ -1191,17 +1188,16 @@ export enum OpenMode { // @public export type OrderedComparator = (lhs: T, rhs: U) => number; -// @alpha (undocumented) +// @public export class OrderedId64Array extends SortedArray { constructor(); - // (undocumented) get ids(): OrderedId64Iterable; } -// @beta +// @public (undocumented) export type OrderedId64Iterable = Iterable; -// @beta +// @public export namespace OrderedId64Iterable { export function areEqualSets(ids1: OrderedId64Iterable, ids2: OrderedId64Iterable): boolean; export function compare(lhs: Id64String, rhs: Id64String): number; @@ -1225,7 +1221,7 @@ export class OrderedSet extends ReadonlyOrderedSet { delete(value: T): boolean; } -// @beta +// @public export function partitionArray(array: T[], criterion: (element: T) => boolean): number; // @public @@ -1261,7 +1257,7 @@ export class PriorityQueue implements Iterable { protected _swap(a: number, b: number): void; } -// @beta +// @public export class ProcessDetector { static get isAndroidAppBackend(): boolean; static get isAndroidAppFrontend(): boolean; diff --git a/common/api/electron-manager.api.md b/common/api/electron-manager.api.md index 2bb3422a2d3..8df6c9bb76d 100644 --- a/common/api/electron-manager.api.md +++ b/common/api/electron-manager.api.md @@ -39,12 +39,14 @@ export class ElectronHost { static get electron(): typeof Electron; // (undocumented) static frontendURL: string; + static getWindowMaximizedSetting(windowName: string): boolean | undefined; + static getWindowSizeSetting(windowName: string): WindowSizeAndPositionProps | undefined; // (undocumented) static get ipcMain(): Electron.IpcMain; // (undocumented) static get isValid(): boolean; static get mainWindow(): BrowserWindow | undefined; - static openMainWindow(windowOptions?: BrowserWindowConstructorOptions): Promise; + static openMainWindow(windowOptions?: ElectronHostWindowOptions): Promise; // (undocumented) static rpcConfig: RpcConfiguration; static startup(opts?: ElectronHostOpts): Promise; @@ -54,6 +56,7 @@ export class ElectronHost { // @beta export interface ElectronHostOptions { + applicationName?: string; developmentServer?: boolean; frontendPort?: number; frontendURL?: string; @@ -69,6 +72,24 @@ export interface ElectronHostOpts extends NativeHostOpts { electronHost?: ElectronHostOptions; } +// @beta (undocumented) +export interface ElectronHostWindowOptions extends BrowserWindowConstructorOptions { + // (undocumented) + storeWindowName?: string; +} + +// @beta +export interface WindowSizeAndPositionProps { + // (undocumented) + height: number; + // (undocumented) + width: number; + // (undocumented) + x: number; + // (undocumented) + y: number; +} + // (No @packageDocumentation comment for this package) diff --git a/common/api/frontend-devtools.api.md b/common/api/frontend-devtools.api.md index 5e84363f40a..e51aa931b83 100644 --- a/common/api/frontend-devtools.api.md +++ b/common/api/frontend-devtools.api.md @@ -103,7 +103,7 @@ export class ApplyViewByIdTool extends Tool { // @beta export class ApplyViewTool extends Tool { // (undocumented) - static get maxArgs(): undefined; + static get maxArgs(): number; // (undocumented) static get minArgs(): number; // (undocumented) @@ -1584,6 +1584,14 @@ export class SaveRenderingStyleTool extends DisplayStyleTool { // @beta export class SaveViewTool extends Tool { + // (undocumented) + static get maxArgs(): number; + // (undocumented) + static get minArgs(): number; + // (undocumented) + parse(inputArgs: string[]): boolean; + // (undocumented) + parseAndRun(...args: string[]): boolean; // (undocumented) run(): boolean; // (undocumented) diff --git a/common/api/geometry-core.api.md b/common/api/geometry-core.api.md index f0dd96db176..d0a25114f31 100644 --- a/common/api/geometry-core.api.md +++ b/common/api/geometry-core.api.md @@ -5602,9 +5602,10 @@ export interface ViewportGraphicsGridLineIdentifier { // @internal export class ViewportGraphicsGridSpacingOptions { - clippingOption: 0 | 1; + clipIfCloseToNeighborLine: 0 | 1; + clipToViewFrustum: boolean; clone(): ViewportGraphicsGridSpacingOptions; - static create(distanceBetweenLines: number, cullingOption?: 0 | 1 | 2, clippingOption?: 0 | 1, gridMultiple?: number): ViewportGraphicsGridSpacingOptions; + static create(distanceBetweenLines: number, cullingOption?: 0 | 1 | 2, clippingOption?: 0 | 1, gridMultiple?: number, clipToViewFrustum?: boolean): ViewportGraphicsGridSpacingOptions; cullingOption: 0 | 1 | 2; distanceBetweenLines: number; gridMultiple: number; diff --git a/common/api/imodelhub-client.api.md b/common/api/imodelhub-client.api.md index 6b4bbcb3c3d..e0741623438 100644 --- a/common/api/imodelhub-client.api.md +++ b/common/api/imodelhub-client.api.md @@ -1183,7 +1183,7 @@ export class UserStatisticsQuery extends WsgQuery { // @public export class Version extends WsgInstance { - changeSetId?: GuidString; + changeSetId?: string; createdDate?: string; description?: string; hidden?: boolean; diff --git a/common/api/imodeljs-backend.api.md b/common/api/imodeljs-backend.api.md index bfe75cb4328..f559997cbef 100644 --- a/common/api/imodeljs-backend.api.md +++ b/common/api/imodeljs-backend.api.md @@ -181,6 +181,7 @@ import { SpatialViewDefinitionProps } from '@bentley/imodeljs-common'; import { StandaloneOpenOptions } from '@bentley/imodeljs-common'; import { StandardViewIndex } from '@bentley/geometry-core'; import { StatusCodeWithMessage } from '@bentley/bentleyjs-core'; +import { StorageValue } from '@bentley/imodeljs-common'; import { SubCategoryAppearance } from '@bentley/imodeljs-common'; import { SubCategoryProps } from '@bentley/imodeljs-common'; import { SubjectProps } from '@bentley/imodeljs-common'; @@ -490,7 +491,7 @@ export class BriefcaseManager { // @internal (undocumented) static getChangedElementsPathName(iModelId: GuidString): string; // @internal (undocumented) - static getChangeSetFolderNameFromId(changeSetId: GuidString): string; + static getChangeSetFolderNameFromId(changeSetId: string): string; // @internal (undocumented) static getChangeSetsPath(iModelId: GuidString): string; // @internal @deprecated @@ -591,11 +592,11 @@ export class ChangedElementsDb implements IDisposable { static createDb(briefcase: IModelDb, pathName: string): ChangedElementsDb; // (undocumented) dispose(): void; - getChangeData(startChangesetId: GuidString, endChangesetId: GuidString): ChangeData | undefined; - getChangedElements(startChangesetId: GuidString, endChangesetId: GuidString): ChangedElements | undefined; - getChangedModels(startChangesetId: GuidString, endChangesetId: GuidString): ChangedModels | undefined; + getChangeData(startChangesetId: string, endChangesetId: string): ChangeData | undefined; + getChangedElements(startChangesetId: string, endChangesetId: string): ChangedElements | undefined; + getChangedModels(startChangesetId: string, endChangesetId: string): ChangedModels | undefined; get isOpen(): boolean; - isProcessed(changesetId: GuidString): boolean; + isProcessed(changesetId: string): boolean; // (undocumented) get nativeDb(): IModelJsNative.ChangedElementsECDb; static openDb(pathName: string, openMode?: ECDbOpenMode): ChangedElementsDb; @@ -690,13 +691,14 @@ export class CheckpointManager { // (undocumented) static readonly onDownload: BeEvent<(job: DownloadJob) => void>; static tryOpenLocalFile(request: DownloadRequest): SnapshotDb | undefined; + static validateCheckpointGuids(checkpoint: CheckpointProps, nativeDb: IModelJsNative.DgnDb): void; // (undocumented) static verifyCheckpoint(checkpoint: CheckpointProps, fileName: string): boolean; } // @beta export interface CheckpointProps { - changeSetId: GuidString; + changeSetId: string; contextId: GuidString; // (undocumented) expectV2?: boolean; @@ -1340,7 +1342,7 @@ export interface DownloadJob { status: DownloadBriefcaseStatus; } -// @beta +// @internal export interface DownloadRequest { aliasFiles?: string[]; checkpoint: CheckpointProps; @@ -2714,7 +2716,7 @@ export class IModelExporter { excludeElementClass(classFullName: string): void; excludeRelationshipClass(classFullName: string): void; exportAll(): Promise; - exportChanges(requestContext: AuthorizedClientRequestContext, startChangeSetId?: GuidString): Promise; + exportChanges(requestContext: AuthorizedClientRequestContext, startChangeSetId?: string): Promise; exportChildElements(elementId: Id64String): Promise; exportCodeSpecById(codeSpecId: Id64String): Promise; exportCodeSpecByName(codeSpecName: string): Promise; @@ -2985,7 +2987,7 @@ export class IModelTransformer extends IModelExportHandler { protected onTransformModel(sourceModel: Model, targetModeledElementId: Id64String): ModelProps; protected onTransformRelationship(sourceRelationship: Relationship): RelationshipProps; processAll(): Promise; - processChanges(requestContext: AuthorizedClientRequestContext, startChangeSetId?: GuidString): Promise; + processChanges(requestContext: AuthorizedClientRequestContext, startChangeSetId?: string): Promise; processChildElements(sourceElementId: Id64String): Promise; processCodeSpec(codeSpecName: string): Promise; processCodeSpecs(): Promise; @@ -3446,6 +3448,28 @@ export abstract class NativeAppAuthorizationBackend extends ImsAuthorizationClie abstract signOut(): Promise; } +// @beta +export class NativeAppStorage { + close(deleteFile?: boolean): void; + // @internal + static closeAll(): void; + static find(name: string): NativeAppStorage; + getBoolean(key: string): boolean | undefined; + getData(key: string): StorageValue | undefined; + getKeys(): string[]; + getNumber(key: string): number | undefined; + // @internal (undocumented) + static getStorageNames(): string[]; + getString(key: string): string | undefined; + getUint8Array(key: string): Uint8Array | undefined; + // (undocumented) + readonly id: string; + static open(name: string): NativeAppStorage; + removeAll(): void; + removeData(key: string): void; + setData(key: string, value: StorageValue): void; + } + // @beta export class NativeHost { static get appSettingsCacheDir(): string; @@ -3460,12 +3484,19 @@ export class NativeHost { static readonly onInternetConnectivityChanged: BeEvent<(status: InternetConnectivityStatus) => void>; static readonly onUserStateChanged: BeEvent<(token?: AccessToken | undefined) => void>; static overrideInternetConnectivity(_overridenBy: OverriddenBy, status: InternetConnectivityStatus): void; + // (undocumented) + static get settingsStore(): NativeAppStorage; static shutdown(): Promise; static startup(opt?: NativeHostOpts): Promise; } // @beta (undocumented) -export type NativeHostOpts = IpcHostOpts; +export interface NativeHostOpts extends IpcHostOpts { + // (undocumented) + nativeHost?: { + applicationName?: string; + }; +} export { NativeLoggerCategory } @@ -4055,7 +4086,7 @@ export enum SqliteValueType { String = 3 } -// @internal +// @public export class StandaloneDb extends IModelDb { get changeSetId(): undefined; static createEmpty(filePath: string, args: CreateEmptyStandaloneIModelProps): StandaloneDb; diff --git a/common/api/imodeljs-common.api.md b/common/api/imodeljs-common.api.md index 33903926f37..d6109201870 100644 --- a/common/api/imodeljs-common.api.md +++ b/common/api/imodeljs-common.api.md @@ -625,7 +625,9 @@ export enum BriefcaseIdValue { Illegal = 4294967295, LastValid = 16777205, Max = 16777216, - Standalone = 0 + // @deprecated + Standalone = 0, + Unassigned = 0 } // @beta @@ -1959,7 +1961,7 @@ export interface DynamicGraphicsRequestProps extends GraphicsRequestProps { readonly modelId?: Id64String; } -// @beta +// @public export const Easing: { Linear: { None: (k: number) => number; @@ -2016,7 +2018,7 @@ export const Easing: { }; }; -// @beta (undocumented) +// @public (undocumented) export type EasingFunction = (k: number) => number; // @public @@ -3806,7 +3808,7 @@ export abstract class IModel implements IModelProps { toJSON(): IModelConnectionProps; } -// @alpha (undocumented) +// @public export type IModelConnectionProps = IModelProps & IModelRpcProps; // @beta @@ -3909,7 +3911,7 @@ export abstract class IModelReadRpcInterface extends RpcInterface { // @public export interface IModelRpcOpenProps { - changeSetId?: GuidString; + changeSetId?: string; readonly contextId?: GuidString; readonly iModelId?: GuidString; openMode?: OpenMode; @@ -3962,7 +3964,7 @@ export interface IModelTileTreeProps extends TileTreeProps { // @public export class IModelVersion { - static asOfChangeSet(changeSetId: GuidString): IModelVersion; + static asOfChangeSet(changeSetId: string): IModelVersion; evaluateChangeSet(requestContext: AuthorizedClientRequestContext, iModelId: GuidString, imodelClient: IModelClient): Promise; static first(): IModelVersion; static fromJSON(json: IModelVersionProps): IModelVersion; @@ -3990,7 +3992,7 @@ export type IModelVersionProps = { afterChangeSetId?: never; versionName?: never; } | { - afterChangeSetId: GuidString; + afterChangeSetId: string; first?: never; latest?: never; versionName?: never; @@ -4067,7 +4069,7 @@ export enum InternetConnectivityStatus { Online = 0 } -// @beta +// @public export const Interpolation: { Linear: (v: any, k: number) => number; Bezier: (v: any, k: number) => number; @@ -4080,7 +4082,7 @@ export const Interpolation: { }; }; -// @beta (undocumented) +// @public (undocumented) export type InterpolationFunction = (v: any, k: number) => number; // @internal (undocumented) @@ -4391,9 +4393,10 @@ export type LocalAlignedBox3d = Range3d; // @beta export interface LocalBriefcaseProps { briefcaseId: number; - changeSetId: GuidString; + changeSetId: string; contextId: GuidString; fileName: string; + fileSize: number; iModelId: GuidString; } @@ -5525,8 +5528,10 @@ export class QParams2d { // (undocumented) copyFrom(src: QParams2d): void; static fromNormalizedRange(rangeScale?: number): QParams2d; + static fromOriginAndScale(ox: number, oy: number, sx: number, sy: number): QParams2d; static fromRange(range: Range2d, out?: QParams2d, rangeScale?: number): QParams2d; static fromZeroToOne(rangeScale?: number): QParams2d; + isQuantizable(point: Point2d): boolean; // (undocumented) readonly origin: Point2d; // (undocumented) @@ -5534,6 +5539,7 @@ export class QParams2d { // (undocumented) readonly scale: Point2d; setFromRange(range: Range2d, rangeScale?: number): void; + unquantize(x: number, y: number, out?: Point2d): Point2d; } // @internal @@ -5546,6 +5552,7 @@ export class QParams3d { static fromOriginAndScale(origin: Point3d, scale: Point3d, out?: QParams3d): QParams3d; static fromRange(range: Range3d, out?: QParams3d, rangeScale?: number): QParams3d; static fromZeroToOne(rangeScale?: number): QParams3d; + isQuantizable(point: Point3d): boolean; // (undocumented) readonly origin: Point3d; // (undocumented) @@ -5554,6 +5561,7 @@ export class QParams3d { readonly scale: Point3d; setFromOriginAndScale(origin: Point3d, scale: Point3d): void; setFromRange(range: Range3d, rangeScale?: number): void; + unquantize(x: number, y: number, z: number, out?: Point3d): Point3d; } // @internal @@ -5756,7 +5764,7 @@ export interface RelatedElementProps { export interface RelationshipProps extends EntityProps, SourceAndTarget { } -// @beta +// @public export type RemoveFunction = () => void; // @beta @@ -7027,7 +7035,7 @@ export interface SpatialViewDefinitionProps extends ViewDefinition3dProps { modelSelectorId: Id64String; } -// @beta +// @public export type StandaloneOpenOptions = OpenDbKey; // @beta @@ -7334,7 +7342,7 @@ export interface TextureProps extends DefinitionElementProps { width: number; } -// @beta +// @public export class ThematicDisplay { readonly axis: Vector3d; readonly displayMode: ThematicDisplayMode; @@ -7344,34 +7352,31 @@ export class ThematicDisplay { static fromJSON(json?: ThematicDisplayProps): ThematicDisplay; readonly gradientSettings: ThematicGradientSettings; readonly range: Range1d; - // @alpha readonly sensorSettings: ThematicDisplaySensorSettings; readonly sunDirection: Vector3d; // (undocumented) toJSON(): ThematicDisplayProps; } -// @beta +// @public export enum ThematicDisplayMode { Height = 0, HillShade = 3, - // @alpha InverseDistanceWeightedSensors = 1, Slope = 2 } -// @beta +// @public export interface ThematicDisplayProps { axis?: XYZProps; displayMode?: ThematicDisplayMode; gradientSettings?: ThematicGradientSettingsProps; range?: Range1dProps; - // @alpha sensorSettings?: ThematicDisplaySensorSettingsProps; sunDirection?: XYZProps; } -// @alpha +// @public export class ThematicDisplaySensor { // (undocumented) equals(other: ThematicDisplaySensor): boolean; @@ -7383,13 +7388,13 @@ export class ThematicDisplaySensor { readonly value: number; } -// @alpha +// @public export interface ThematicDisplaySensorProps { position?: XYZProps; value?: number; } -// @alpha +// @public export class ThematicDisplaySensorSettings { readonly distanceCutoff: number; // (undocumented) @@ -7401,29 +7406,23 @@ export class ThematicDisplaySensorSettings { toJSON(): ThematicDisplaySensorSettingsProps; } -// @alpha +// @public export interface ThematicDisplaySensorSettingsProps { distanceCutoff?: number; sensors?: ThematicDisplaySensorProps[]; } -// @beta (undocumented) +// @public export enum ThematicGradientColorScheme { - // (undocumented) BlueRed = 0, - // (undocumented) Custom = 5, - // (undocumented) Monochrome = 2, - // (undocumented) RedBlue = 1, - // (undocumented) SeaMountain = 4, - // (undocumented) Topographic = 3 } -// @beta (undocumented) +// @public export enum ThematicGradientMode { IsoLines = 3, Smooth = 0, @@ -7431,7 +7430,7 @@ export enum ThematicGradientMode { SteppedWithDelimiter = 2 } -// @beta +// @public export class ThematicGradientSettings { clone(changedProps?: ThematicGradientSettingsProps): ThematicGradientSettings; readonly colorMix: number; @@ -7456,7 +7455,7 @@ export class ThematicGradientSettings { toJSON(): ThematicGradientSettingsProps; } -// @beta (undocumented) +// @public export interface ThematicGradientSettingsProps { colorMix?: number; colorScheme?: ThematicGradientColorScheme; @@ -7655,7 +7654,7 @@ export enum TreeFlags { UseProjectExtents = 1 } -// @beta +// @public export class Tween { constructor(_group: Tweens, _object: any); // (undocumented) @@ -7710,10 +7709,10 @@ export class Tween { yoyo(yoyo: boolean): this; } -// @beta (undocumented) +// @public (undocumented) export type TweenCallback = (obj: any) => void; -// @beta +// @public export class Tweens { // (undocumented) add(tween: Tween): void; @@ -7793,7 +7792,7 @@ export enum TypeOfChange { Property = 1 } -// @beta (undocumented) +// @public (undocumented) export type UpdateCallback = (obj: any, t: number) => void; // @beta diff --git a/common/api/imodeljs-frontend.api.md b/common/api/imodeljs-frontend.api.md index 79a48fbc9aa..ffa13da8430 100644 --- a/common/api/imodeljs-frontend.api.md +++ b/common/api/imodeljs-frontend.api.md @@ -146,8 +146,6 @@ import { IpcAppChannel } from '@bentley/imodeljs-common'; import { IpcAppFunctions } from '@bentley/imodeljs-common'; import { IpcListener } from '@bentley/imodeljs-common'; import { IpcSocketFrontend } from '@bentley/imodeljs-common'; -import { LDClient } from 'ldclient-js'; -import { LDFlagValue } from 'ldclient-js'; import { LightSettings } from '@bentley/imodeljs-common'; import { LinePixels } from '@bentley/imodeljs-common'; import { LocalBriefcaseProps } from '@bentley/imodeljs-common'; @@ -209,7 +207,6 @@ import { PropertyDescription } from '@bentley/ui-abstract'; import { QParams2d } from '@bentley/imodeljs-common'; import { QParams3d } from '@bentley/imodeljs-common'; import { QPoint2d } from '@bentley/imodeljs-common'; -import { QPoint2dList } from '@bentley/imodeljs-common'; import { QPoint3d } from '@bentley/imodeljs-common'; import { QPoint3dList } from '@bentley/imodeljs-common'; import { QuantityParseResult } from '@bentley/imodeljs-quantity'; @@ -943,7 +940,9 @@ export class AccuSnap implements Decorator { readonly toolState: AccuSnap.ToolState; // @internal (undocumented) touchCursor?: TouchCursor; - } + // @internal (undocumented) + get wantVirtualCursor(): boolean; +} // @public (undocumented) export namespace AccuSnap { @@ -1210,10 +1209,10 @@ export function areaToEyeHeight(view3d: ViewState3d, area: GlobalLocationArea, o // @internal export function areaToEyeHeightFromGcs(view3d: ViewState3d, area: GlobalLocationArea, offset?: number): Promise; -// @beta +// @public export type AsyncFunction = (...args: any) => Promise; -// @beta +// @public export type AsyncMethodsOf = { [P in keyof T]: T[P] extends AsyncFunction ? P : never; }[keyof T]; @@ -1649,7 +1648,6 @@ export class BriefcaseConnection extends IModelConnection { // (undocumented) protected _isClosed?: boolean; static openFile(briefcaseProps: OpenBriefcaseProps): Promise; - // @internal static openStandalone(filePath: string, openMode?: OpenMode, opts?: StandaloneOpenOptions): Promise; // @beta pullAndMergeChanges(version?: IModelVersionProps): Promise; @@ -3388,7 +3386,6 @@ export interface FrameRenderData { export enum FrontendLoggerCategory { Authorization = "imodeljs-frontend.Authorization", EventSource = "imodeljs-frontend.EventSource", - FeatureToggle = "imodeljs-frontend.FeatureToggles", // @alpha FeatureTracking = "imodeljs-frontend.FeatureTracking", FrontendRequestContext = "imodeljs-frontend.FrontendRequestContext", @@ -3636,6 +3633,29 @@ export class GlobeAnimator implements Animator { protected _viewport: ScreenViewport; } +// @internal +export class GltfMeshData { + constructor(props: Mesh); + // (undocumented) + indices?: Uint16Array | Uint32Array; + // (undocumented) + normals?: Uint16Array; + // (undocumented) + pointQParams?: QParams3d; + // (undocumented) + pointRange?: Range3d; + // (undocumented) + points?: Uint16Array; + // (undocumented) + primitive: Mesh; + // (undocumented) + uvQParams?: QParams2d; + // (undocumented) + uvRange?: Range2d; + // (undocumented) + uvs?: Uint16Array; +} + // @internal export abstract class GltfReader { protected constructor(props: GltfReaderProps, iModel: IModelConnection, modelId: Id64String, is3d: boolean, system: RenderSystem, type?: BatchType, isCanceled?: ShouldAbortReadGltf); @@ -3705,18 +3725,14 @@ export abstract class GltfReader { // (undocumented) protected readIndices(json: any, accessorName: string): number[] | undefined; // (undocumented) - protected readMeshIndices(mesh: Mesh, json: any): boolean; + protected readMeshIndices(mesh: GltfMeshData, json: any): boolean; // (undocumented) - protected readMeshPrimitive(primitive: any, featureTable?: FeatureTable, pseudoRtcBias?: Vector3d): Mesh | undefined; + protected readMeshPrimitive(primitive: any, featureTable?: FeatureTable, pseudoRtcBias?: Vector3d): GltfMeshData | undefined; // (undocumented) - protected readNormals(normals: OctEncodedNormal[], json: any, accessorName: string): boolean; + protected readNormals(mesh: GltfMeshData, json: any, accessorName: string): boolean; // (undocumented) protected readPolylines(polylines: MeshPolylineList, json: any, accessorName: string, disjoint: boolean): boolean; // (undocumented) - protected readUVParams(params: Point2d[], json: any, accessorName: string): boolean; - // (undocumented) - protected readVertices(positions: QPoint3dList, primitive: any, pseudoRtcBias?: Vector3d): boolean; - // (undocumented) protected readonly _renderMaterials: any; // (undocumented) protected readonly _returnToCenter: number[] | undefined; @@ -3926,6 +3942,7 @@ export enum GraphicType { // @internal (undocumented) export class GridDisplaySettings { static clippingOption: 0 | 1; + static clipToViewFrustum: boolean; static cullingOption: 0 | 1 | 2; static cullingPerspectiveOption: 0 | 1 | 2; static lineLimiter: number; @@ -4322,8 +4339,6 @@ export class IModelApp { static createRenderSys(opts?: RenderSystem.Options): RenderSystem; // @beta static get extensionAdmin(): ExtensionAdmin; - // @internal - static get featureToggles(): FeatureToggleClient; // @alpha static formatElementToolTip(msg: string[]): HTMLElement; // @internal (undocumented) @@ -4402,8 +4417,6 @@ export interface IModelAppOptions { authorizationClient?: FrontendAuthorizationClient; // @beta extensionAdmin?: ExtensionAdmin; - // @internal - featureToggles?: FeatureToggleClient; i18n?: I18N | I18NOptions; imodelClient?: IModelClient; // @internal (undocumented) @@ -5437,9 +5450,9 @@ export class MapTile extends RealityTile { // (undocumented) freeMemory(): void; // (undocumented) - get geometry(): RenderTerrainMeshGeometry | undefined; + get geometry(): RenderRealityMeshGeometry | undefined; // (undocumented) - protected _geometry?: RenderTerrainMeshGeometry; + protected _geometry?: RenderRealityMeshGeometry; // (undocumented) getClipShape(): Point3d[]; // (undocumented) @@ -5673,7 +5686,7 @@ export class MapTileTreeReference extends TileTreeReference { // (undocumented) getLayerImageryTreeRef(index: number): ImageryMapLayerTreeReference | undefined; // (undocumented) - protected getSymbologyOverrides(_tree: TileTree): FeatureSymbology.Overrides; + protected getSymbologyOverrides(_tree: TileTree): FeatureSymbology.Overrides | undefined; // (undocumented) getToolTip(hit: HitDetail): Promise; // (undocumented) @@ -5937,15 +5950,7 @@ export class MeasureAreaTool extends MeasureElementTool { // @alpha (undocumented) export class MeasureDistanceTool extends PrimitiveTool { // (undocumented) - protected readonly _acceptedSegments: { - distance: number; - slope: number; - start: Point3d; - end: Point3d; - delta: Vector3d; - refAxes: Matrix3d; - marker: MeasureMarker; - }[]; + protected readonly _acceptedSegments: Segment[]; // (undocumented) protected acceptNewSegments(): Promise; // (undocumented) @@ -5959,7 +5964,7 @@ export class MeasureDistanceTool extends PrimitiveTool { // (undocumented) protected displayDelta(context: DecorateContext, seg: any): void; // (undocumented) - protected displayDynamicDistance(context: DecorateContext, points: Point3d[]): void; + protected displayDynamicDistance(context: DecorateContext, points: Point3d[], adjustedPoints: Point3d[]): void; // (undocumented) getDecorationGeometry(_hit: HitDetail): GeometryStreamProps | undefined; // (undocumented) @@ -5975,12 +5980,11 @@ export class MeasureDistanceTool extends PrimitiveTool { // (undocumented) isValidLocation(_ev: BeButtonEvent, _isButtonEvent: boolean): boolean; // (undocumented) + protected _lastMotionAdjustedPt?: Point3d; + // (undocumented) protected _lastMotionPt?: Point3d; // (undocumented) - protected readonly _locationData: { - point: Point3d; - refAxes: Matrix3d; - }[]; + protected readonly _locationData: Location[]; // (undocumented) onDataButtonDown(ev: BeButtonEvent): Promise; // (undocumented) @@ -6411,13 +6415,13 @@ export enum ModifyElementSource { Unknown = 0 } -// @alpha +// @beta export class NativeApp { // (undocumented) static callNativeHost>(methodName: T, ...args: Parameters): Promise>; // (undocumented) static checkInternetConnectivity(): Promise; - static closeStorage(storage: Storage, deleteId: boolean): Promise; + static closeStorage(storage: Storage, deleteStorage?: boolean): Promise; static deleteBriefcase(fileName: string): Promise; // (undocumented) static getBriefcaseFileName(props: BriefcaseProps): Promise; @@ -6438,7 +6442,7 @@ export class NativeApp { static startup(ipc: IpcSocketFrontend, opts?: NativeAppOpts): Promise; } -// @alpha +// @beta export class NativeAppAuthorization { constructor(config: NativeAppAuthorizationConfiguration); // (undocumented) @@ -6471,7 +6475,7 @@ export class NativeAppLogger { static logWarning(category: string, message: string, getMetaData?: GetMetaDataFunction): void; } -// @alpha +// @beta export interface NativeAppOpts extends IpcAppOptions { // (undocumented) nativeApp?: { @@ -7105,7 +7109,7 @@ export enum PrimitiveVisibility { Uninstanced = 2 } -// @beta +// @public export type PromiseReturnType = T extends (...args: any) => Promise ? R : any; // @internal (undocumented) @@ -7655,6 +7659,8 @@ export namespace RenderMemory { // (undocumented) get polylines(): Consumers; // (undocumented) + get reality(): Consumers; + // (undocumented) get silhouetteEdges(): Consumers; // (undocumented) get surfaces(): Consumers; @@ -7666,7 +7672,7 @@ export namespace RenderMemory { // (undocumented) export enum BufferType { // (undocumented) - COUNT = 9, + COUNT = 10, // (undocumented) Instances = 7, // (undocumented) @@ -7678,6 +7684,8 @@ export namespace RenderMemory { // (undocumented) Polylines = 4, // (undocumented) + RealityMesh = 9, + // (undocumented) SilhouetteEdges = 2, // (undocumented) Surfaces = 0, @@ -7752,6 +7760,8 @@ export namespace RenderMemory { // (undocumented) addPolylineEdges(numBytes: number): void; // (undocumented) + addRealityMesh(numBytes: number): void; + // (undocumented) addShadowMap(numBytes: number): void; // (undocumented) addSilhouetteEdges(numBytes: number): void; @@ -7833,14 +7843,10 @@ export interface RenderPlan { // (undocumented) readonly lights?: LightSettings; // (undocumented) - readonly locatableTerrain: boolean; - // (undocumented) readonly monochromeMode: MonochromeMode; // (undocumented) readonly monoColor: ColorDef; // (undocumented) - readonly terrainTransparency: number; - // (undocumented) readonly thematic?: ThematicDisplay; // (undocumented) readonly upVector: Vector3d; @@ -7858,6 +7864,14 @@ export abstract class RenderPlanarClassifier implements IDisposable { abstract setSource(classifierTreeRef?: SpatialClassifierTileTreeReference, planarClipMask?: PlanarClipMaskState): void; } +// @internal (undocumented) +export abstract class RenderRealityMeshGeometry implements IDisposable, RenderMemory.Consumer { + // (undocumented) + abstract collectStatistics(stats: RenderMemory.Statistics): void; + // (undocumented) + abstract dispose(): void; +} + // @internal (undocumented) export namespace RenderScheduleState { // (undocumented) @@ -8072,15 +8086,17 @@ export abstract class RenderSystem implements IDisposable { createPointString(_params: PointStringParams, _instances?: InstancedGraphicParams | Point3d): RenderGraphic | undefined; // @internal (undocumented) createPolyline(_params: PolylineParams, _instances?: InstancedGraphicParams | Point3d): RenderGraphic | undefined; + // @internal (undocumented) + createRealityMesh(_realityMesh: RealityMeshPrimitive): RenderGraphic | undefined; + // @internal (undocumented) + createRealityMeshFromTerrain(_terrainMesh: TerrainMeshPrimitive, _transform?: Transform): RenderRealityMeshGeometry | undefined; + // @internal (undocumented) + createRealityMeshGraphic(_terrainGeometry: RenderRealityMeshGeometry, _featureTable: PackedFeatureTable, _tileId: string | undefined, _baseColor: ColorDef | undefined, _baseTransparent: boolean, _textures?: TerrainTexture[]): RenderGraphic | undefined; // @beta createScreenSpaceEffectBuilder(_params: ScreenSpaceEffectBuilderParams): ScreenSpaceEffectBuilder | undefined; createSkyBox(_params: SkyBox.CreateParams): RenderGraphic | undefined; // @internal (undocumented) abstract createTarget(canvas: HTMLCanvasElement): RenderTarget; - // @internal (undocumented) - createTerrainMeshGeometry(_terrainMesh: TerrainMeshPrimitive, _transform: Transform): RenderTerrainMeshGeometry | undefined; - // @internal (undocumented) - createTerrainMeshGraphic(_terrainGeometry: RenderTerrainMeshGeometry, _featureTable: PackedFeatureTable, _tileId: string, _baseColor: ColorDef | undefined, _baseTransparent: boolean, _textures?: TerrainTexture[]): RenderGraphic | undefined; // @internal createTextureFromCubeImages(_posX: HTMLImageElement, _negX: HTMLImageElement, _posY: HTMLImageElement, _negY: HTMLImageElement, _posZ: HTMLImageElement, _negZ: HTMLImageElement, _imodel: IModelConnection, _params: RenderTexture.Params): RenderTexture | undefined; createTextureFromElement(_id: Id64String, _imodel: IModelConnection, _params: RenderTexture.Params, _format: ImageSourceFormat): RenderTexture | undefined; @@ -8113,7 +8129,7 @@ export abstract class RenderSystem implements IDisposable { // @internal loadTextureImage(id: Id64String, iModel: IModelConnection): Promise; // @internal (undocumented) - get maxTerrainImageryLayers(): number; + get maxRealityImageryLayers(): number; // @internal (undocumented) get maxTextureSize(): number; // @internal (undocumented) @@ -8124,6 +8140,8 @@ export abstract class RenderSystem implements IDisposable { get supportsInstancing(): boolean; // @internal (undocumented) get supportsLogZBuffer(): boolean; + // @internal (undocumented) + get supportsNonuniformScaledInstancing(): boolean; } // @public @@ -8269,14 +8287,6 @@ export interface RenderTargetDebugControl { vcSupportIntersectingVolumes: boolean; } -// @internal (undocumented) -export abstract class RenderTerrainMeshGeometry implements IDisposable, RenderMemory.Consumer { - // (undocumented) - abstract collectStatistics(stats: RenderMemory.Statistics): void; - // (undocumented) - abstract dispose(): void; -} - // @internal export abstract class RenderTextureDrape implements IDisposable { // (undocumented) @@ -8519,6 +8529,8 @@ export class ScreenViewport extends Viewport { source: DepthPointSource; sourceId?: string; }; + // @internal (undocumented) + picker: ElementPicker; pickNearestVisibleGeometry(pickPoint: Point3d, radius?: number, allowNonLocatable?: boolean, out?: Point3d): Point3d | undefined; // @internal static removeAllChildren(el: HTMLDivElement): void; @@ -9305,19 +9317,15 @@ export enum StartOrResume { Start = 1 } -// @alpha +// @beta export class Storage { - constructor(id: string, _isOpen?: boolean); - close(deleteIt?: boolean): Promise; - // @internal + constructor(id: string); getData(key: string): Promise; getKeys(): Promise; // (undocumented) readonly id: string; - get isOpen(): boolean; removeAll(): Promise; removeData(key: string): Promise; - // @internal setData(key: string, value: StorageValue): Promise; } @@ -9574,8 +9582,6 @@ export abstract class Target extends RenderTarget implements RenderTargetDebugCo // (undocumented) modelToView(modelPt: XYZ, result?: Point3d): Point3d; // (undocumented) - nonLocatableTerrain: boolean; - // (undocumented) onBatchDisposed(batch: Batch): void; // (undocumented) onBeforeRender(viewport: Viewport, setSceneNeedRedraw: (redraw: boolean) => void): void; @@ -9634,8 +9640,6 @@ export abstract class Target extends RenderTarget implements RenderTargetDebugCo // (undocumented) get techniques(): Techniques; // (undocumented) - terrainTransparency: number; - // (undocumented) readonly uniforms: TargetUniforms; // (undocumented) updateSolarShadows(context: SceneContext | undefined): void; @@ -9765,7 +9769,7 @@ export class TerrainTexture { export interface TerrainTileContent extends TileContent { // (undocumented) terrain?: { - geometry?: RenderTerrainMeshGeometry; + geometry?: RenderRealityMeshGeometry; mesh?: TerrainMeshPrimitive; }; } @@ -10212,6 +10216,8 @@ export class TileDrawArgs { // @internal (undocumented) parentsAndChildrenExclusive: boolean; // @internal (undocumented) + readonly pixelSizeScaleFactor: number; + // @internal (undocumented) planarClassifier?: RenderPlanarClassifier; // @internal processSelectedTiles(_tiles: Tile[]): void; @@ -10868,6 +10874,8 @@ export class ToolSettings { static doubleClickTimeout: BeDuration; static doubleClickToleranceInches: number; static doubleTapTimeout: BeDuration; + // @beta + static enableVirtualCursorForLocate: boolean; static preserveWorldUp: boolean; static scrollSpeed: number; static startDragDelay: BeDuration; @@ -11090,7 +11098,7 @@ export type UnitSystemKey = "metric" | "imperial" | "usCustomary" | "usSurvey"; // @internal (undocumented) export class UpsampledMapTile extends MapTile { // (undocumented) - get geometry(): RenderTerrainMeshGeometry | undefined; + get geometry(): RenderRealityMeshGeometry | undefined; // (undocumented) get isEmpty(): boolean; // (undocumented) @@ -11539,9 +11547,7 @@ export class ViewClipTool extends PrimitiveTool { // @beta export class ViewCreator2d { constructor(_imodel: IModelConnection); - createViewForModel(modelId: Id64String, modelType: string, options?: ViewCreator2dOptions): Promise; - static isDrawingModelClass(modelType: string): boolean; - static isSheetModelClass(modelType: string): boolean; + createViewForModel(modelId: Id64String, options?: ViewCreator2dOptions): Promise; } // @beta @@ -12688,6 +12694,10 @@ export abstract class ViewState extends ElementState { // (undocumented) toJSON(): ViewDefinitionProps; toProps(): ViewStateProps; + // @internal (undocumented) + transformNormalByModelDisplayTransform(modelId: string | undefined, normal: Vector3d): void; + // @internal (undocumented) + transformPointByModelDisplayTransform(modelId: string | undefined, pnt: Point3d, inverse: boolean): void; // (undocumented) protected _updateMaxGlobalScopeFactor(): void; get viewFlags(): ViewFlags; diff --git a/common/api/mobile-manager.api.md b/common/api/mobile-manager.api.md index 40012e636a7..f9569821052 100644 --- a/common/api/mobile-manager.api.md +++ b/common/api/mobile-manager.api.md @@ -74,16 +74,20 @@ export class IOSApp { // (undocumented) static get isValid(): boolean; // (undocumented) - static startup(opts?: { - iModelApp?: IModelAppOptions; - }): Promise; + static startup(opts?: IOSAppOpts): Promise; } +// @beta (undocumented) +export type IOSAppOpts = NativeAppOpts; + // @beta (undocumented) export class IOSHost extends MobileHost { - static startup(opt?: MobileHostOpts): Promise; + static startup(opt?: IOSHostOpts): Promise; } +// @beta (undocumented) +export type IOSHostOpts = MobileHostOpts; + // @beta (undocumented) export class MobileApp { // (undocumented) @@ -172,6 +176,7 @@ export interface MobileHostOpts extends NativeHostOpts { // (undocumented) mobileHost?: { device?: MobileDevice; + rpcInterfaces?: RpcInterfaceDefinition[]; }; } diff --git a/common/api/summary/README.md b/common/api/summary/README.md index 191d6e69b10..f6b385b3e09 100644 --- a/common/api/summary/README.md +++ b/common/api/summary/README.md @@ -1,4 +1,6 @@ -# Using Excel to Sort Exports Summary Report +# Managing Summary Reports + +## Using Excel to Sort Exports of Individual Packages 1. Open the appropriate `.csv` report file in Excel 2. Double-click on the line between the **A** and **B** columns to auto-resize column **A** @@ -9,3 +11,9 @@ 7. Press *Add Level* 8. In *Then by* option button, select *API Item* 9. Press *OK* + +## Creating the Full Summary API Report + +1. Delete any existing `common/api/summary/summary.exports.csv` + > The script will not overwrite any existing file +2. Set the `GENERATE_FULL_API_REPORT` environment variable to create a `summary.exports.csv` file diff --git a/common/api/summary/backend-itwin-client.exports.csv b/common/api/summary/backend-itwin-client.exports.csv index 4e5b2cf5b1c..edc9f94f846 100644 --- a/common/api/summary/backend-itwin-client.exports.csv +++ b/common/api/summary/backend-itwin-client.exports.csv @@ -25,8 +25,12 @@ alpha;class IntrospectionResponseCache internal;LocalhostHandler alpha;MemoryIntrospectionResponseCache beta;OidcAgentClient = AgentAuthorizationClient +deprecated;OidcAgentClient = AgentAuthorizationClient beta;OidcAgentClientConfiguration = AgentAuthorizationClientConfiguration +deprecated;OidcAgentClientConfiguration = AgentAuthorizationClientConfiguration beta;OidcDelegationClient = DelegationAuthorizationClient +deprecated;OidcDelegationClient = DelegationAuthorizationClient beta;OidcDelegationClientConfiguration = DelegationAuthorizationClientConfiguration +deprecated;OidcDelegationClientConfiguration = DelegationAuthorizationClientConfiguration internal;StorageServiceFileHandler internal;UrlFileHandler \ No newline at end of file diff --git a/common/api/summary/bentleyjs-core.exports.csv b/common/api/summary/bentleyjs-core.exports.csv index 21452f563d4..4c949bb3190 100644 --- a/common/api/summary/bentleyjs-core.exports.csv +++ b/common/api/summary/bentleyjs-core.exports.csv @@ -4,21 +4,21 @@ beta;AbandonedError internal;addClientRequestContext: (metaData: any) => void public;areEqualPossiblyUndefined public;asInstanceOf -beta;assert(condition: boolean, msg?: string): asserts condition +public;assert(condition: boolean, msg?: string): asserts condition alpha;AsyncMutex alpha;AsyncMutexUnlockFnType = () => void beta;AuthStatus public;base64StringToUint8Array(base64: string): Uint8Array public;BeDuration public;BeEvent -beta;BeEventList +public;BeEventList public;BentleyError public;BentleyLoggerCategory public;BentleyStatus public;BeTimePoint beta;BeUiEvent beta;BriefcaseStatus -beta;ByteStream +public;ByteStream public;ChangeSetApplyOption beta;ChangeSetStatus public;ClientRequestContext @@ -32,10 +32,8 @@ public;comparePossiblyUndefined public;compareStrings(a: string, b: string): number public;compareStringsOrUndefined(lhs?: string, rhs?: string): number public;compareWithTolerance(a: number, b: number, tolerance?: number): number -beta;CompressedId64Set = string -beta;CompressedId64Set -alpha;iterable(ids: CompressedId64Set): OrderedId64Iterable -alpha;iterator(ids: CompressedId64Set): Iterator +public;CompressedId64Set = string +public;CompressedId64Set public;ComputePriorityFunction public;Config public;Constructor @@ -68,7 +66,9 @@ public;IModelStatus public;IndexedValue public;IndexMap internal;isElectronMain: boolean +deprecated;isElectronMain: boolean internal;isElectronRenderer: boolean +deprecated;isElectronRenderer: boolean public;isIDisposable(obj: unknown): obj is IDisposable public;isInstanceOf public;JsonUtils @@ -82,19 +82,19 @@ public;lowerBound public;LRUCache public;LRUDictionary public;LRUMap -alpha;MutableCompressedId64Set -beta;ObservableSet +public;MutableCompressedId64Set +public;ObservableSet beta;OneAtATimeAction public;OpenMode public;OrderedComparator -alpha;OrderedId64Array -beta;OrderedId64Iterable = Iterable -beta;OrderedId64Iterable +public;OrderedId64Array +public;OrderedId64Iterable = Iterable +public;OrderedId64Iterable public;OrderedSet -beta;partitionArray +public;partitionArray public;PerfLogger public;PriorityQueue -beta;ProcessDetector +public;ProcessDetector public;ReadonlyOrderedSet public;ReadonlySortedArray beta;RepositoryStatus diff --git a/common/api/summary/imodelhub-client.exports.csv b/common/api/summary/imodelhub-client.exports.csv index fd9f884c972..c77edd7d25e 100644 --- a/common/api/summary/imodelhub-client.exports.csv +++ b/common/api/summary/imodelhub-client.exports.csv @@ -55,6 +55,7 @@ public;EventSAS public;EventSubscription public;EventSubscriptionHandler internal;EventType = "LockEvent" | "AllLocksDeletedEvent" | "ChangeSetPostPushEvent" | "ChangeSetPrePushEvent" | "CodeEvent" | "AllCodesDeletedEvent" | "BriefcaseDeletedEvent" | "iModelDeletedEvent" | "VersionEvent" | "CheckpointCreatedEvent" +deprecated;EventType = "LockEvent" | "AllLocksDeletedEvent" | "ChangeSetPostPushEvent" | "ChangeSetPrePushEvent" | "CodeEvent" | "AllCodesDeletedEvent" | "BriefcaseDeletedEvent" | "iModelDeletedEvent" | "VersionEvent" | "CheckpointCreatedEvent" internal;GetEventOperationType internal;GlobalCheckpointCreatedEvent internal;GlobalEventHandler diff --git a/common/api/summary/imodeljs-backend.exports.csv b/common/api/summary/imodeljs-backend.exports.csv index 1b8933e0179..c6b0a56874f 100644 --- a/common/api/summary/imodeljs-backend.exports.csv +++ b/common/api/summary/imodeljs-backend.exports.csv @@ -21,6 +21,7 @@ public;BisCoreSchema public;BriefcaseDb public;BriefcaseId = number public;BriefcaseIdValue +deprecated;BriefcaseIdValue beta;BriefcaseManager public;CachedECSqlStatement internal;CachedSqliteStatement @@ -76,10 +77,11 @@ public;DisplayStyle3d public;DisplayStyleCreationOptions public;class Document internal;class DocumentCarrier +deprecated;class DocumentCarrier public;DocumentListModel public;DocumentPartition internal;DownloadJob -beta;DownloadRequest +internal;DownloadRequest internal;Downloads public;Drawing public;DrawingCategory @@ -203,6 +205,7 @@ alpha;IModelSchemaLoader beta;IModelTransformer beta;IModelTransformOptions internal;class InformationCarrierElement +deprecated;class InformationCarrierElement public;class InformationContentElement public;class InformationModel public;class InformationPartitionElement @@ -226,8 +229,9 @@ internal;MetaDataRegistry public;Model public;ModelSelector internal;class NativeAppAuthorizationBackend +beta;NativeAppStorage beta;NativeHost -beta;NativeHostOpts = IpcHostOpts +beta;NativeHostOpts public;OrthographicViewDefinition public;class PhysicalElement public;PhysicalElementAssemblesElements @@ -264,6 +268,7 @@ public;SectionDrawing beta;SectionDrawingLocation public;SectionDrawingModel alpha;SectionLocation +deprecated;SectionLocation internal;setMaxEntitiesPerEvent(max: number): number public;Sheet public;SheetBorderTemplate @@ -285,7 +290,7 @@ internal;SqliteStatement internal;SqliteStatementCache internal;SqliteValue internal;SqliteValueType -internal;StandaloneDb +public;StandaloneDb internal;StringParam public;SubCategory public;Subject @@ -305,6 +310,7 @@ public;Texture internal;TextureCreateProps public;TitleText public;TxnAction +deprecated;TxnAction beta;TxnChangedEntities public;TxnIdString = string beta;TxnManager diff --git a/common/api/summary/imodeljs-common.exports.csv b/common/api/summary/imodeljs-common.exports.csv index 03a5d70b6fa..1338ed483cd 100644 --- a/common/api/summary/imodeljs-common.exports.csv +++ b/common/api/summary/imodeljs-common.exports.csv @@ -126,8 +126,8 @@ beta;DPoint2dProps = number[] beta;DynamicGraphicsRequest2dProps beta;DynamicGraphicsRequest3dProps beta;DynamicGraphicsRequestProps -beta;Easing: -beta;EasingFunction = (k: number) => number +public;Easing: +public;EasingFunction = (k: number) => number public;EcefLocation public;EcefLocationProps public;ECJsNames @@ -254,7 +254,7 @@ public;ImageSourceFormat internal;ImdlFlags internal;ImdlHeader public;class IModel -alpha;IModelConnectionProps = IModelProps & IModelRpcProps +public;IModelConnectionProps = IModelProps & IModelRpcProps beta;IModelCoordinatesRequestProps beta;IModelCoordinatesResponseProps public;IModelEncryptionProps @@ -275,8 +275,8 @@ public;InformationPartitionElementProps internal;initializeRpcRequest: () => void internal;INSTANCE: unique symbol beta;InternetConnectivityStatus -beta;Interpolation: -beta;InterpolationFunction = (v: any, k: number) => number +public;Interpolation: +public;InterpolationFunction = (v: any, k: number) => number internal;IpcAppChannel internal;IpcAppFunctions internal;IpcAppNotifications @@ -419,7 +419,7 @@ internal;REGISTRY: unique symbol public;RelatedElement public;RelatedElementProps public;RelationshipProps -beta;RemoveFunction = () => void +public;RemoveFunction = () => void beta;class RenderMaterial beta;RenderMaterial beta;RenderMaterialProps @@ -495,6 +495,7 @@ beta;SectionDrawingLocationProps beta;SectionDrawingProps beta;SectionDrawingViewProps alpha;SectionLocationProps +deprecated;SectionLocationProps public;SectionType public;SerializedRpcOperation public;SerializedRpcRequest @@ -519,7 +520,7 @@ public;SolarShadowSettingsProps public;SourceAndTarget beta;SpatialClassificationProps public;SpatialViewDefinitionProps -beta;StandaloneOpenOptions = OpenDbKey +public;StandaloneOpenOptions = OpenDbKey beta;StorageValue = string | number | boolean | null | Uint8Array public;SubCategoryAppearance public;SubCategoryAppearance @@ -544,17 +545,17 @@ beta;TextureMapping beta;TextureMapProps beta;TextureMapUnits beta;TextureProps -beta;ThematicDisplay -beta;ThematicDisplayMode -beta;ThematicDisplayProps -alpha;ThematicDisplaySensor -alpha;ThematicDisplaySensorProps -alpha;ThematicDisplaySensorSettings -alpha;ThematicDisplaySensorSettingsProps -beta;ThematicGradientColorScheme -beta;ThematicGradientMode -beta;ThematicGradientSettings -beta;ThematicGradientSettingsProps +public;ThematicDisplay +public;ThematicDisplayMode +public;ThematicDisplayProps +public;ThematicDisplaySensor +public;ThematicDisplaySensorProps +public;ThematicDisplaySensorSettings +public;ThematicDisplaySensorSettingsProps +public;ThematicGradientColorScheme +public;ThematicGradientMode +public;ThematicGradientSettings +public;ThematicGradientSettingsProps alpha;ThumbnailFormatProps alpha;ThumbnailProps internal;TileContentDescription @@ -574,15 +575,15 @@ internal;TileTreeMetadata internal;TileTreeProps alpha;TileVersionInfo alpha;TreeFlags -beta;Tween -beta;TweenCallback = (obj: any) => void -beta;Tweens +public;Tween +public;TweenCallback = (obj: any) => void +public;Tweens public;TxnAction internal;TxnNotifications public;TypeDefinition public;TypeDefinitionElementProps internal;TypeOfChange -beta;UpdateCallback = (obj: any, t: number) => void +public;UpdateCallback = (obj: any, t: number) => void beta;UpgradeOptions public;UrlLinkProps public;ViewAttachmentLabelProps diff --git a/common/api/summary/imodeljs-frontend.exports.csv b/common/api/summary/imodeljs-frontend.exports.csv index ac246c85399..4d913b4fae1 100644 --- a/common/api/summary/imodeljs-frontend.exports.csv +++ b/common/api/summary/imodeljs-frontend.exports.csv @@ -43,8 +43,8 @@ internal;ArcGisTokenManager internal;ArcGisUtilities internal;areaToEyeHeight(view3d: ViewState3d, area: GlobalLocationArea, offset?: number): number internal;areaToEyeHeightFromGcs(view3d: ViewState3d, area: GlobalLocationArea, offset?: number): Promise -beta;AsyncFunction = (...args: any) => Promise -beta;AsyncMethodsOf +public;AsyncFunction = (...args: any) => Promise +public;AsyncMethodsOf public;AuthorizedFrontendRequestContext public;AuxCoordSystem2dState public;AuxCoordSystem3dState @@ -131,9 +131,9 @@ public;DrawingViewState internal;class DynamicIModelTile public;DynamicsContext alpha;EditingFunctions +deprecated;EditingFunctions alpha;EditingFunctions -deprecated;CategoryEditor -deprecated;ModelEditor +deprecated;EditingFunctions alpha;EditManipulator internal;ELEMENT_MARKED_FOR_REMOVAL: unique symbol alpha;ElementAgenda @@ -165,8 +165,6 @@ alpha;FeatureLogBatchOptions public;FeatureOverrideProvider public;FeatureOverrideType public;FeatureSymbology -deprecated;Appearance -deprecated;AppearanceProps alpha;FeatureTrackingManager alpha;FeatureTrackingProps alpha;findAvailableRealityModels(contextId: GuidString, modelCartographicRange?: CartographicRange | undefined): Promise @@ -207,6 +205,7 @@ beta;getQuantityTypeKey(type: QuantityTypeArg): QuantityTypeKey alpha;GlobalLocation alpha;GlobalLocationArea internal;GlobeAnimator +internal;GltfMeshData internal;class GltfReader internal;GltfReaderProps internal;GltfReaderResult @@ -342,10 +341,10 @@ alpha;ModelDisplayTransformProvider public;ModelSelectorState public;ModelState alpha;ModifyElementSource -alpha;NativeApp -alpha;NativeAppAuthorization +beta;NativeApp +beta;NativeAppAuthorization internal;NativeAppLogger -alpha;NativeAppOpts +beta;NativeAppOpts internal;NoRenderApp beta;class NotificationHandler public;NotificationManager @@ -355,6 +354,7 @@ internal;NullTarget internal;OffScreenTarget internal;OffScreenViewport beta;OidcBrowserClient +deprecated;OidcBrowserClient internal;OnScreenTarget beta;openImageDataUrlInNewWindow(url: string, title?: string): void internal;OrbitGtTileTree @@ -387,7 +387,7 @@ beta;PlanarClipMaskState internal;PlanarTilePatch public;class PrimitiveTool alpha;PrimitiveVisibility -beta;PromiseReturnType +public;PromiseReturnType internal;QuadId beta;QuantityFormatOverridesChangedArgs beta;QuantityFormatsChangedArgs @@ -412,6 +412,7 @@ internal;RealityTileParams internal;RealityTileTree internal;RealityTileTreeParams public;RemoteBriefcaseConnection +deprecated;RemoteBriefcaseConnection beta;class RenderClipVolume public;RenderContext internal;RenderDiagnostics @@ -420,6 +421,7 @@ public;class RenderGraphicOwner internal;RenderMemory internal;RenderPlan internal;class RenderPlanarClassifier +internal;class RenderRealityMeshGeometry internal;RenderScheduleState public;class RenderSystem public;RenderSystem @@ -427,7 +429,6 @@ beta;Options beta;RenderSystemDebugControl internal;class RenderTarget beta;RenderTargetDebugControl -internal;class RenderTerrainMeshGeometry internal;class RenderTextureDrape internal;RequestTileTreePropsFunc = (iModel: IModelConnection, treeId: string) => Promise internal;RootIModelTile = Tile & @@ -489,7 +490,7 @@ public;StandardView public;StandardViewId public;StandardViewTool public;StartOrResume -alpha;Storage +beta;Storage internal;SubCategoriesCache internal;SubCategoriesCache internal;SubCategoriesRequest diff --git a/common/api/summary/itwin-client.exports.csv b/common/api/summary/itwin-client.exports.csv index f8ea5921299..151f0ff395b 100644 --- a/common/api/summary/itwin-client.exports.csv +++ b/common/api/summary/itwin-client.exports.csv @@ -41,8 +41,11 @@ beta;RequestTimeoutOptions beta;Response beta;ResponseError internal;SamlAccessToken +deprecated;SamlAccessToken internal;SamlAuthorizationToken +deprecated;SamlAuthorizationToken internal;class SamlToken +deprecated;class SamlToken beta;SasUrlExpired internal;TokenPrefix(prefix: string): (constructor: any) => void internal;UploadError diff --git a/common/api/summary/presentation-backend.exports.csv b/common/api/summary/presentation-backend.exports.csv index 19608c7e7a9..8bed6d14019 100644 --- a/common/api/summary/presentation-backend.exports.csv +++ b/common/api/summary/presentation-backend.exports.csv @@ -17,6 +17,7 @@ public;PresentationManagerMode public;PresentationManagerProps public;PresentationProps = PresentationPropsDeprecated | PresentationPropsNew public;PresentationPropsDeprecated +deprecated;PresentationPropsDeprecated public;PresentationPropsNew beta;RulesetEmbedder public;RulesetEmbedderProps diff --git a/common/api/summary/presentation-common.exports.csv b/common/api/summary/presentation-common.exports.csv index 82338d88a94..816e11a7a05 100644 --- a/common/api/summary/presentation-common.exports.csv +++ b/common/api/summary/presentation-common.exports.csv @@ -46,13 +46,21 @@ public;DefaultContentDisplayTypes public;DefaultGroupingPropertiesContainer public;DefaultPropertyCategoryOverride public;DEPRECATED_AllInstanceNodesSpecification +deprecated;DEPRECATED_AllInstanceNodesSpecification public;DEPRECATED_AllRelatedInstanceNodesSpecification +deprecated;DEPRECATED_AllRelatedInstanceNodesSpecification public;DEPRECATED_ContentRelatedInstancesSpecification +deprecated;DEPRECATED_ContentRelatedInstancesSpecification public;DEPRECATED_PropertiesDisplaySpecification +deprecated;DEPRECATED_PropertiesDisplaySpecification public;DEPRECATED_PropertyEditorsSpecification +deprecated;DEPRECATED_PropertyEditorsSpecification public;DEPRECATED_RelatedInstanceNodesSpecification +deprecated;DEPRECATED_RelatedInstanceNodesSpecification public;DEPRECATED_RelatedInstanceSpecification +deprecated;DEPRECATED_RelatedInstanceSpecification public;DEPRECATED_RelatedPropertiesSpecification +deprecated;DEPRECATED_RelatedPropertiesSpecification public;Descriptor public;DescriptorJSON public;DescriptorOverrides @@ -165,6 +173,7 @@ public;LabelRawValueJSON = string | number | boolean | LabelCompositeValueJSON public;LabelRequestOptions public;LabelRpcRequestOptions = PresentationRpcRequestOptions public;LoggingNamespaces +deprecated;LoggingNamespaces public;MultiSchemaClassesSpecification beta;NamedFieldDescriptor public;NavigationRule = RootNodeRule | ChildNodeRule diff --git a/common/api/summary/presentation-components.exports.csv b/common/api/summary/presentation-components.exports.csv index ebad739e33a..dde0d1eeb09 100644 --- a/common/api/summary/presentation-components.exports.csv +++ b/common/api/summary/presentation-components.exports.csv @@ -7,14 +7,20 @@ public;ContentDataProvider public;ContentDataProviderProps beta;ControlledTreeFilteringProps beta;ControlledTreeWithFilteringSupportProps +deprecated;ControlledTreeWithFilteringSupportProps beta;ControlledTreeWithVisibleNodesProps +deprecated;ControlledTreeWithVisibleNodesProps public;DataProvidersFactory public;DataProvidersFactoryProps beta;DEFAULT_PROPERTY_GRID_RULESET: Ruleset beta;DEPRECATED_controlledTreeWithFilteringSupport +deprecated;DEPRECATED_controlledTreeWithFilteringSupport beta;DEPRECATED_controlledTreeWithVisibleNodes +deprecated;DEPRECATED_controlledTreeWithVisibleNodes public;DEPRECATED_treeWithFilteringSupport +deprecated;DEPRECATED_treeWithFilteringSupport public;DEPRECATED_treeWithUnifiedSelection +deprecated;DEPRECATED_treeWithUnifiedSelection alpha;FavoritePropertiesDataFilterer alpha;FavoritePropertiesDataFiltererProps beta;FavoritePropertiesDataProvider @@ -41,11 +47,15 @@ public;PresentationTreeDataProviderProps beta;PresentationTreeNodeLoaderProps beta;PropertyDataProviderWithUnifiedSelectionProps public;propertyGridWithUnifiedSelection +deprecated;propertyGridWithUnifiedSelection public;PropertyGridWithUnifiedSelectionProps +deprecated;PropertyGridWithUnifiedSelectionProps public;tableWithUnifiedSelection public;TableWithUnifiedSelectionProps public;TreeWithFilteringSupportProps +deprecated;TreeWithFilteringSupportProps public;TreeWithUnifiedSelectionProps +deprecated;TreeWithUnifiedSelectionProps beta;UnifiedSelectionContext beta;UnifiedSelectionContextProvider(props: UnifiedSelectionContextProviderProps): React.ReactElement beta;UnifiedSelectionContextProviderProps diff --git a/common/api/summary/rbac-client.exports.csv b/common/api/summary/rbac-client.exports.csv index 7daeef9c68a..f63c2ac453e 100644 --- a/common/api/summary/rbac-client.exports.csv +++ b/common/api/summary/rbac-client.exports.csv @@ -1,5 +1,6 @@ sep=; Release Tag;API Item internal;IModelHubPermission +deprecated;IModelHubPermission internal;Permission internal;RbacClient \ No newline at end of file diff --git a/common/api/summary/ui-abstract.exports.csv b/common/api/summary/ui-abstract.exports.csv index 571898380b3..096649385cc 100644 --- a/common/api/summary/ui-abstract.exports.csv +++ b/common/api/summary/ui-abstract.exports.csv @@ -2,13 +2,13 @@ sep=; Release Tag;API Item beta;AbstractActionItemProps beta;AbstractMenuItemProps -beta;AbstractStatusBarActionItem -beta;AbstractStatusBarCustomItem -beta;AbstractStatusBarItem -beta;AbstractStatusBarItemUtilities -beta;AbstractStatusBarLabelItem +public;AbstractStatusBarActionItem +public;AbstractStatusBarCustomItem +public;AbstractStatusBarItem +public;AbstractStatusBarItemUtilities +public;AbstractStatusBarLabelItem beta;AbstractToolbarProps -beta;AbstractWidgetProps +public;AbstractWidgetProps alpha;AccuDrawField alpha;AccuDrawGrabInputFocusEvent alpha;AccuDrawMode @@ -23,16 +23,16 @@ alpha;AccuDrawSetFieldValueToUiEventArgs alpha;AccuDrawSetModeEvent alpha;AccuDrawSetModeEventArgs alpha;AccuDrawUiAdmin -beta;ActionButton +public;ActionButton beta;AlternateDateFormats beta;ArrayValue -beta;BackstageActionItem -beta;BackstageItem = BackstageActionItem | BackstageStageLauncher +public;BackstageActionItem +public;BackstageItem = BackstageActionItem | BackstageStageLauncher internal;BackstageItemsChangedArgs internal;BackstageItemsManager -beta;BackstageItemType -beta;BackstageItemUtilities -beta;BackstageStageLauncher +public;BackstageItemType +public;BackstageItemUtilities +public;BackstageStageLauncher public;BadgeType beta;BaseDialogItem beta;BasePropertyEditorParams @@ -42,15 +42,15 @@ beta;ButtonGroupEditorParams internal;enum CharCode beta;ColorEditorParams beta;CommandHandler -beta;CommonBackstageItem +public;CommonBackstageItem beta;CommonItemProps -beta;CommonStatusBarItem = AbstractStatusBarActionItem | AbstractStatusBarLabelItem | AbstractStatusBarCustomItem -beta;CommonToolbarItem = ActionButton | GroupButton | CustomButtonDefinition +public;CommonStatusBarItem = AbstractStatusBarActionItem | AbstractStatusBarLabelItem | AbstractStatusBarCustomItem +public;CommonToolbarItem = ActionButton | GroupButton | CustomButtonDefinition public;ConditionalBooleanValue -beta;ConditionalStringValue +public;ConditionalStringValue internal;convertSimple2RegExpPattern(pattern: string): string internal;createMatches(score: undefined | FuzzyScore): IMatch[] -beta;CustomButtonDefinition +public;CustomButtonDefinition beta;CustomFormattedNumberParams beta;DateFormatter public;DialogButtonDef @@ -78,7 +78,7 @@ internal;FuzzyScorer = (pattern: string, lowPattern: string, patternPos: number, beta;GenericUiEvent beta;GenericUiEventArgs internal;getClassName: (obj: any) => string -beta;GroupButton +public;GroupButton beta;IconDefinition beta;IconEditorParams beta;IconListEditorParams @@ -87,10 +87,10 @@ internal;IFilter = (word: string, wordToMatchAgainst: string) => IMatch[] | null beta;ImageCheckBoxParams public;IMatch beta;InputEditorSizeParams -beta;isAbstractStatusBarActionItem: (item: CommonStatusBarItem) => item is AbstractStatusBarActionItem -beta;isAbstractStatusBarCustomItem: (item: CommonStatusBarItem) => item is AbstractStatusBarCustomItem -beta;isAbstractStatusBarLabelItem: (item: CommonStatusBarItem) => item is AbstractStatusBarLabelItem -beta;isActionItem: (item: BackstageItem) => item is BackstageActionItem +public;isAbstractStatusBarActionItem: (item: CommonStatusBarItem) => item is AbstractStatusBarActionItem +public;isAbstractStatusBarCustomItem: (item: CommonStatusBarItem) => item is AbstractStatusBarCustomItem +public;isAbstractStatusBarLabelItem: (item: CommonStatusBarItem) => item is AbstractStatusBarLabelItem +public;isActionItem: (item: BackstageItem) => item is BackstageActionItem public;isArrowKey(key: string): boolean beta;isButtonGroupEditorParams: (item: BasePropertyEditorParams) => item is ButtonGroupEditorParams beta;isColorEditorParams: (item: BasePropertyEditorParams) => item is ColorEditorParams @@ -100,7 +100,7 @@ beta;isInputEditorSizeParams: (item: BasePropertyEditorParams) => item is InputE alpha;isLetter(char: string): boolean internal;isLowerAsciiLetter(code: number): boolean internal;isPatternInWord(patternLow: string, patternPos: number, patternLen: number, wordLow: string, wordPos: number, wordLen: number): boolean -beta;isStageLauncher: (item: BackstageItem) => item is BackstageStageLauncher +public;isStageLauncher: (item: BackstageItem) => item is BackstageStageLauncher beta;isSuppressLabelEditorParams: (item: BasePropertyEditorParams) => item is SuppressLabelEditorParams internal;isUpperAsciiLetter(code: number): boolean beta;LinkElementsInfo @@ -133,45 +133,45 @@ beta;PropertyRecord beta;PropertyRendererInfo beta;PropertyValue = PrimitiveValue | StructValue | ArrayValue beta;PropertyValueFormat -beta;ProvidedItem +public;ProvidedItem beta;RangeEditorParams public;RelativePosition beta;SliderEditorParams public;SpecialKey -beta;StagePanelLocation -beta;StagePanelSection +public;StagePanelLocation +public;StagePanelSection beta;StageUsage beta;StandardEditorNames beta;StandardTypeNames internal;startsWithIgnoreCase(str: string, candidate: string): boolean -beta;StatusBarItemId = CommonStatusBarItem["id"] +public;StatusBarItemId = CommonStatusBarItem["id"] internal;StatusBarItemsChangedArgs internal;StatusBarItemsManager -beta;StatusBarLabelSide -beta;StatusBarSection +public;StatusBarLabelSide +public;StatusBarSection beta;StringGetter = () => string beta;StructValue beta;SuppressLabelEditorParams beta;SyncPropertiesChangeEvent beta;SyncPropertiesChangeEventArgs beta;TimeDisplay -beta;ToolbarItem -beta;ToolbarItemId = CommonToolbarItem["id"] +public;ToolbarItem +public;ToolbarItemId = CommonToolbarItem["id"] internal;ToolbarItemsChangedArgs internal;ToolbarItemsManager -beta;ToolbarItemUtilities -beta;ToolbarOrientation -beta;ToolbarUsage +public;ToolbarItemUtilities +public;ToolbarOrientation +public;ToolbarUsage public;UiAbstract beta;UiAdmin beta;class UiDataProvider public;UiError beta;UiFlags -beta;UiItemProviderRegisteredEventArgs +public;UiItemProviderRegisteredEventArgs beta;UiItemsApplication -beta;UiItemsApplicationAction +public;UiItemsApplicationAction beta;UiItemsArbiter -beta;UiItemsManager -beta;UiItemsProvider +public;UiItemsManager +public;UiItemsProvider beta;class UiLayoutDataProvider -beta;WidgetState \ No newline at end of file +public;WidgetState \ No newline at end of file diff --git a/common/api/summary/ui-components.exports.csv b/common/api/summary/ui-components.exports.csv index 593d3c53dd0..34cef9c8341 100644 --- a/common/api/summary/ui-components.exports.csv +++ b/common/api/summary/ui-components.exports.csv @@ -20,29 +20,47 @@ alpha;BaseSolarDataProvider alpha;BaseTimelineDataProvider beta;BasicPropertyEditor beta;BeDragDropContext(props: +deprecated;BeDragDropContext(props: public;BeInspireTree +deprecated;BeInspireTree public;BeInspireTreeDataProvider +deprecated;BeInspireTreeDataProvider public;BeInspireTreeDataProviderInterface +deprecated;BeInspireTreeDataProviderInterface public;BeInspireTreeDataProviderMethod +deprecated;BeInspireTreeDataProviderMethod public;BeInspireTreeDataProviderPromise +deprecated;BeInspireTreeDataProviderPromise public;BeInspireTreeDataProviderRaw +deprecated;BeInspireTreeDataProviderRaw public;BeInspireTreeEvent +deprecated;BeInspireTreeEvent public;BeInspireTreeNode +deprecated;BeInspireTreeNode public;BeInspireTreeNodeConfig +deprecated;BeInspireTreeNodeConfig public;BeInspireTreeNodeITree +deprecated;BeInspireTreeNodeITree public;BeInspireTreeNodePayloadConfig +deprecated;BeInspireTreeNodePayloadConfig public;BeInspireTreeNodes +deprecated;BeInspireTreeNodes public;BeInspireTreeProps +deprecated;BeInspireTreeProps public;BeInspireTreeRenderer +deprecated;BeInspireTreeRenderer beta;BooleanEditor beta;BooleanPropertyEditor public;BooleanTypeConverter beta;Breadcrumb beta;BreadcrumbDetails beta;BreadcrumbDetailsDragDropProps +deprecated;BreadcrumbDetailsDragDropProps beta;BreadcrumbDetailsDragDropType = {} | TreeNodeItem | TableDataProvider +deprecated;BreadcrumbDetailsDragDropType = {} | TreeNodeItem | TableDataProvider beta;BreadcrumbDetailsProps beta;BreadcrumbDragDropProps +deprecated;BreadcrumbDragDropProps internal;BreadcrumbInput internal;BreadcrumbInputProps internal;BreadcrumbInputState @@ -59,6 +77,7 @@ alpha;CategorizedPropertyItem alpha;CategorizedPropertyTypes = FlatGridItemType.Array | FlatGridItemType.Primitive | FlatGridItemType.Struct alpha;CategoryRecordsDict beta;CellEditingEngine +deprecated;CellEditingEngine public;CellItem public;CellProps beta;CheckBoxInfo @@ -118,8 +137,11 @@ public;class DateTimeTypeConverterBase beta;DEFAULT_LINKS_HANDLER: LinkElementsInfo public;DelayLoadedTreeNodeItem public;DEPRECATED_FilteringInputProps +deprecated;DEPRECATED_FilteringInputProps public;DEPRECATED_Tree +deprecated;DEPRECATED_Tree beta;DEPRECATED_withTreeDragDrop +deprecated;DEPRECATED_withTreeDragDrop beta;Direction internal;DirectionHelpers alpha;DisplayValuePropertyDataFilterer @@ -128,9 +150,13 @@ beta;DistinctValuesFilterDescriptor public;DoublePropertyValueRenderer internal;DragAction beta;DragDropArguments +deprecated;DragDropArguments beta;DragLayerProps +deprecated;DragLayerProps beta;DragSourceArguments +deprecated;DragSourceArguments beta;DragSourceProps +deprecated;DragSourceProps beta;DrawingNavigationAid beta;DrawingNavigationAidProps internal;DrawingNavigationCanvas @@ -138,11 +164,16 @@ internal;DrawingNavigationCanvasProps public;DrawingViewportChangeEvent public;DrawingViewportChangeEventArgs beta;DropEffects +deprecated;DropEffects beta;DropStatus +deprecated;DropStatus beta;DropTargetArguments +deprecated;DropTargetArguments beta;DropTargetProps +deprecated;DropTargetProps public;EditableTreeDataProvider beta;EditableTreeProps +deprecated;EditableTreeProps beta;EditorContainer beta;EditorContainerProps beta;EnumButtonGroupEditor @@ -152,6 +183,7 @@ beta;EnumPropertyEditor public;EnumTypeConverter public;ErrorObserver public;EventsMuteContext +deprecated;EventsMuteContext beta;ExtendedTreeNodeRendererProps internal;FaceCell internal;FaceCellProps @@ -193,12 +225,14 @@ alpha;FormatUnitsProps public;from internal;getCSSColorFromDef(colorDef: ColorDef): string beta;GetCurrentlyEditedNode = () => BeInspireTreeNode +deprecated;GetCurrentlyEditedNode = () => BeInspireTreeNode internal;getToolbarDirection: (expandsTo: Direction) => OrthogonalDirection alpha;GridCategoryItem internal;GroupPopupItem({ item, addGroupSeparator }: internal;handleLoadedNodeHierarchy(modelSource: TreeModelSource, loadedHierarchy: LoadedNodeHierarchy): void public;hasChildren: (node: TreeNodeItem) => boolean public;hasFlag: (selectionMode: SelectionMode, flag: SelectionModeFlags) => boolean +deprecated;hasFlag: (selectionMode: SelectionMode, flag: SelectionModeFlags) => boolean public;hasSelectionModeFlag: (selectionMode: SelectionMode, flag: SelectionModeFlags) => boolean public;HexadecimalTypeConverter beta;HighlightableTreeNodeProps @@ -262,6 +296,7 @@ beta;LoadedNodeHierarchy beta;LoadedNodeHierarchyItem internal;MapMode public;MapPayloadToInspireNodeCallback +deprecated;MapPayloadToInspireNodeCallback internal;MenuItem alpha;Milestone alpha;MilestoneRange @@ -289,9 +324,13 @@ public;NEW_FilteringInputProps public;NextObserver internal;Node public;NodeCheckboxProps +deprecated;NodeCheckboxProps internal;NodeRenderer = (item: BeInspireTreeNode +deprecated;NodeRenderer = (item: BeInspireTreeNode internal;NodesDeselectedCallback = OnItemsDeselectedCallback +deprecated;NodesDeselectedCallback = OnItemsDeselectedCallback internal;NodesSelectedCallback = OnItemsSelectedCallback +deprecated;NodesSelectedCallback = OnItemsSelectedCallback public;NonPrimitivePropertyLabelRenderer public;NonPrimitivePropertyLabelRendererProps public;NonPrimitivePropertyRenderer @@ -352,6 +391,7 @@ beta;PropertyFilterChangeEvent beta;PropertyFilterChangesListener = () => void public;PropertyGrid public;PropertyGridCategory +deprecated;PropertyGridCategory internal;PropertyGridCommons public;PropertyGridContextMenuArgs alpha;PropertyGridEventHandler @@ -394,6 +434,7 @@ internal;SelectionHandler public;SelectionMode public;SelectionModeFlags beta;SetCurrentlyEditedNode = (currentlyEditedNode?: BeInspireTreeNode +deprecated;SetCurrentlyEditedNode = (currentlyEditedNode?: BeInspireTreeNode public;SharedRendererProps public;SharedTableNonPrimitiveValueRendererProps internal;ShortDateTimePropertyEditor @@ -420,10 +461,13 @@ public;SortComparer public;SparseArray internal;SparseTree beta;StandardEditorNames +deprecated;StandardEditorNames public;StandardRotationChangeEvent public;StandardRotationChangeEventArgs beta;StandardTypeConverterTypeNames +deprecated;StandardTypeConverterTypeNames beta;StandardTypeNames +deprecated;StandardTypeNames public;StringOperatorProcessor public;StringTypeConverter public;StructPropertyValueRenderer @@ -440,8 +484,11 @@ public;TableDataChangesListener = () => void public;TableDataProvider beta;TableDistinctValue beta;TableDragDropProps +deprecated;TableDragDropProps beta;TableDragDropType = {} | RowItem | TableDataProvider +deprecated;TableDragDropType = {} | RowItem | TableDataProvider beta;TableDropTargetProps +deprecated;TableDropTargetProps public;TableNonPrimitiveValueRenderer public;TableNonPrimitiveValueRendererProps public;TableProps @@ -486,9 +533,11 @@ beta;ToolbarProps beta;ToolbarWithOverflow(props: ToolbarWithOverflowProps): JSX.Element internal;ToolbarWithOverflowDirectionContext: React.Context beta;ToolbarWithOverflowProps +internal;toRxjsObservable internal;toToolbarPopupRelativePosition(expandsTo: Direction, alignment: ToolbarPanelAlignment): RelativePosition beta;TreeActions beta;TreeCellUpdatedArgs +deprecated;TreeCellUpdatedArgs beta;TreeCheckboxStateChangeEventArgs public;TreeDataChangesListener = (nodes: Array public;TreeDataProvider = TreeDataProviderRaw | TreeDataProviderPromise | TreeDataProviderMethod | ITreeDataProvider @@ -497,7 +546,9 @@ public;TreeDataProviderPromise = Promise public;TreeDataProviderRaw = ImmediatelyLoadedTreeNodeItem[] internal;TreeDataSource beta;TreeDragDropProps +deprecated;TreeDragDropProps beta;TreeDragDropType = {} | TreeNodeItem | TreeDataProvider +deprecated;TreeDragDropType = {} | TreeNodeItem | TreeDataProvider beta;TreeEditingParams internal;TreeEventDispatcher beta;TreeEventHandler @@ -514,17 +565,22 @@ beta;TreeModelNodeType = TreeModelNode | TreeModelNodePlaceholder | TreeModelRoo beta;TreeModelRootNode beta;TreeModelSource public;TreeNode +deprecated;TreeNode beta;TreeNodeEventArgs public;TreeNodeIcon(props: TreeNodeIconProps): JSX.Element | null +deprecated;TreeNodeIcon(props: TreeNodeIconProps): JSX.Element | null public;TreeNodeIconProps +deprecated;TreeNodeIconProps public;TreeNodeItem beta;TreeNodeItemData = ImmediatelyLoadedTreeNodeItem & DelayLoadedTreeNodeItem beta;TreeNodeLoader beta;TreeNodeLoadResult public;TreeNodeProps +deprecated;TreeNodeProps beta;TreeNodeRenderer: React.MemoExoticComponent beta;TreeNodeRendererProps public;TreeProps +deprecated;TreeProps beta;TreeRenderer beta;TreeRendererAttributes beta;TreeRendererContext @@ -573,9 +629,16 @@ beta;WeightPickerButton beta;WeightPickerProps beta;WeightPropertyEditor beta;withBreadcrumbDetailsDragDrop +deprecated;withBreadcrumbDetailsDragDrop beta;withBreadcrumbDragDrop +deprecated;withBreadcrumbDragDrop beta;withDragSource: +deprecated;withDragSource: beta;WithDragSourceProps +deprecated;WithDragSourceProps beta;withDropTarget: +deprecated;withDropTarget: beta;WithDropTargetProps -beta;withTableDragDrop \ No newline at end of file +deprecated;WithDropTargetProps +beta;withTableDragDrop +deprecated;withTableDragDrop \ No newline at end of file diff --git a/common/api/summary/ui-core.exports.csv b/common/api/summary/ui-core.exports.csv index b44df36f757..8f5256adce8 100644 --- a/common/api/summary/ui-core.exports.csv +++ b/common/api/summary/ui-core.exports.csv @@ -15,6 +15,7 @@ internal;BetaBadge(props: CommonProps): JSX.Element public;BlockText(props: TextProps): JSX.Element public;BodyText(props: TextProps): JSX.Element beta;BoundsFunctionProp = number | (() => number | undefined) +deprecated;BoundsFunctionProp = number | (() => number | undefined) public;Button public;ButtonProps public;ButtonSize @@ -155,6 +156,7 @@ public;LoadingSpinner public;LoadingSpinnerProps public;LoadingStatus public;LoadingStatusProps +public;LocalSettingsStorage beta;LocalUiSettings public;MainTabsProps internal;mergeRefs @@ -178,8 +180,11 @@ beta;NodeCheckboxRenderProps = Omit beta;NumberInput: (props: NumberInputProps) => JSX.Element | null beta;NumberInputProps beta;NumericInput +deprecated;NumericInput internal;NumericInputDefaultProps = Pick +deprecated;NumericInputDefaultProps = Pick beta;NumericInputProps +deprecated;NumericInputProps public;Omit public;OmitChildrenProp beta;OptionsType = Array @@ -212,8 +217,11 @@ public;RadioProps public;RatioChangeResult public;ReactMessage internal;ReactNumericInput +deprecated;ReactNumericInput beta;ReactNumericInputProps +deprecated;ReactNumericInputProps internal;ReactStepFunctionProp = number | ((component: ReactNumericInput, direction: string) => number | undefined) +deprecated;ReactStepFunctionProp = number | ((component: ReactNumericInput, direction: string) => number | undefined) internal;Rectangle public;RectangleProps internal;ResizeObserver: ResizeObserverType @@ -225,14 +233,15 @@ public;SearchBoxProps public;Select: (props: SelectProps) => JSX.Element | null public;SelectOption public;SelectProps +public;SessionSettingsStorage beta;SessionUiSettings beta;SettingsContainer: ({ tabs, onSettingsTabSelected, currentSettingsTab, settingsManager, showHeader }: SettingsContainerProps) => JSX.Element beta;SettingsContainerProps beta;SettingsManager -beta;SettingsProvider beta;SettingsProvidersChangedEvent beta;SettingsProvidersChangedEventArgs beta;SettingsTabEntry +beta;SettingsTabsProvider internal;shallowDiffers: (a: internal;Size public;SizeProps @@ -289,10 +298,11 @@ public;TreeNodeProps public;TreeProps public;UiCore public;UiEvent -beta;UiSetting -public;UiSettings +public;UiSetting +public;UiSettings = UiSettingsStorage public;UiSettingsResult public;UiSettingsStatus +public;UiSettingsStorage public;UnderlinedButton(props: UnderlinedButtonProps): JSX.Element public;UnderlinedButtonProps public;useDisposable diff --git a/common/api/summary/ui-framework.exports.csv b/common/api/summary/ui-framework.exports.csv index 284a74947af..009a7e63dd1 100644 --- a/common/api/summary/ui-framework.exports.csv +++ b/common/api/summary/ui-framework.exports.csv @@ -37,28 +37,37 @@ public;AnyItemDef = GroupItemDef | CommandItemDef | ToolItemDef | ActionButtonIt public;AnyWidgetProps = WidgetProps | ToolWidgetProps | NavigationWidgetProps internal;appendWidgets(state: NineZoneState, widgetDefs: ReadonlyArray public;AppNotificationManager +beta;AppUiSettings beta;areNoFeatureOverridesActive(): boolean public;Backstage +deprecated;Backstage beta;BackstageActionItem -beta;BackstageAppButton(props: BackstageAppButtonProps): JSX.Element -beta;BackstageAppButtonProps -beta;BackstageComposer(props: BackstageComposerProps): JSX.Element +deprecated;BackstageActionItem +public;BackstageAppButton(props: BackstageAppButtonProps): JSX.Element +public;BackstageAppButtonProps +public;BackstageComposer(props: BackstageComposerProps): JSX.Element internal;BackstageComposerActionItem({ item }: BackstageComposerActionItemProps): JSX.Element internal;BackstageComposerActionItemProps beta;BackstageComposerItem({ item }: BackstageComposerItemProps): JSX.Element beta;BackstageComposerItemProps -beta;BackstageComposerProps +public;BackstageComposerProps internal;BackstageComposerStageLauncher({ item }: BackstageComposerStageLauncherProps): JSX.Element internal;BackstageComposerStageLauncherProps public;BackstageEvent public;BackstageEventArgs +deprecated;BackstageEventArgs public;BackstageItemProps +deprecated;BackstageItemProps public;BackstageItemState +deprecated;BackstageItemState beta;BackstageItemType +deprecated;BackstageItemType beta;BackstageItemUtilities beta;BackstageManager public;BackstageProps +deprecated;BackstageProps beta;BackstageStageLauncher +deprecated;BackstageStageLauncher beta;BackstageToggledArgs public;BaseItemState beta;BasicNavigationWidget(props: BasicNavigationWidgetProps): JSX.Element @@ -88,6 +97,7 @@ beta;ClassGroupingOption beta;ClearEmphasisStatusField(props: ClearEmphasisStatusFieldProps): JSX.Element internal;clearKeyinPaletteHistory(): void public;COLOR_THEME_DEFAULT = ColorTheme.Light +deprecated;COLOR_THEME_DEFAULT = ColorTheme.Light public;ColorTheme public;CombinedReducerState public;combineReducers: CombineReducersFunction @@ -110,7 +120,7 @@ public;ConfigurableUiControlConstructor = new (info: ConfigurableCreateInfo, opt public;ConfigurableUiControlType public;ConfigurableUiElement public;ConfigurableUiManager -public;ConfigurableUiReducer(state: ConfigurableUiState | undefined, _action: ConfigurableUiActionsUnion): ConfigurableUiState +public;ConfigurableUiReducer(state: ConfigurableUiState | undefined, action: ConfigurableUiActionsUnion): ConfigurableUiState public;ConfigurableUiState beta;connectIModelConnection: (mapStateToProps?: any, mapDispatchToProps?: any) => import("react-redux").InferableComponentEnhancerWithProps beta;connectIModelConnectionAndViewState: (mapStateToProps?: any, mapDispatchToProps?: any) => import("react-redux").InferableComponentEnhancerWithProps @@ -139,7 +149,7 @@ public;CubeNavigationAidControl public;CursorDirection public;CursorDirectionParts public;CursorInformation -beta;CursorMenuData +public;CursorMenuData public;CursorPopup public;CursorPopupContent(props: CommonDivProps): JSX.Element internal;CursorPopupFadeOutEvent @@ -155,14 +165,16 @@ internal;CursorPopupUpdatePositionEventArgs internal;CursorPrompt public;CursorUpdatedEvent public;CursorUpdatedEventArgs -beta;CustomItemDef -beta;CustomItemProps +public;CustomItemDef +public;CustomItemProps public;DeepReadonly public;DeepReadonlyArray public;DeepReadonlyObject beta;DefaultDialogGridContainer({ componentGenerator, isToolSettings }: beta;DefaultNavigationProps +deprecated;DefaultNavigationProps beta;DefaultNavigationWidget +deprecated;DefaultNavigationWidget internal;DefaultToolSettingsProvider alpha;DefaultViewOverlay public;DialogChangedEvent @@ -174,11 +186,17 @@ internal;DialogRendererBase internal;DialogRendererProps internal;DockedStatusBarItem(props: StatusBarItemProps): JSX.Element beta;DragDropLayerChangedEvent +deprecated;DragDropLayerChangedEvent beta;DragDropLayerChangedEventArgs +deprecated;DragDropLayerChangedEventArgs beta;DragDropLayerManager +deprecated;DragDropLayerManager beta;DragDropLayerRenderer: typeof DragDropLayerRendererComponent & DndComponentClass +deprecated;DragDropLayerRenderer: typeof DragDropLayerRendererComponent & DndComponentClass beta;DragDropLayerRendererComponent +deprecated;DragDropLayerRendererComponent beta;DragDropLayerRendererProps +deprecated;DragDropLayerRendererProps beta;DrawingNavigationAidControl public;ElementTooltip public;ElementTooltipChangedEvent @@ -186,8 +204,8 @@ public;ElementTooltipChangedEventArgs alpha;EmphasizeElementsChangedArgs beta;ExpandableSection beta;ExpandableSectionProps -internal;expandWidget: -beta;ExtensibleToolbarProps +internal;expandWidget: (base: +public;ExtensibleToolbarProps beta;featureOverridesActiveStateFunc(state: Readonly alpha;FocusToolSettings public;FooterModeField @@ -196,9 +214,9 @@ internal;FrameworkAccuDraw beta;FrameworkRootState internal;FrameworkStagePanel internal;FrameworkStagePanelProps -beta;FrameworkState +public;FrameworkState alpha;FrameworkToolAdmin -beta;FrameworkUiAdmin +public;FrameworkUiAdmin alpha;FrameworkVersion(props: FrameworkVersionProps): JSX.Element alpha;FrameworkVersion = "1" | "2" internal;FrameworkVersionChangedEvent @@ -227,8 +245,10 @@ public;FrontstageReadyEvent public;FrontstageReadyEventArgs internal;FrontstageRuntimeProps public;FunctionKey +deprecated;FunctionKey public;FunctionType = (...args: any[]) => any public;getBackstageItemStateFromProps: (props: BackstageItemProps) => BackstageItemState +deprecated;getBackstageItemStateFromProps: (props: BackstageItemProps) => BackstageItemState internal;getBadgeClassName(badgeType: BadgeType | undefined): "uifw-badge-new" | "uifw-badge-tp" | undefined alpha;getCategories(imodel: IModelConnection, viewport?: Viewport, filteredProvider?: IPresentationTreeDataProvider): Promise internal;getExtendedZone: (zoneId: WidgetZoneId, zones: ZonesManagerProps, defProvider: ZoneDefProvider) => ZoneManagerProps @@ -245,6 +265,7 @@ beta;getQuantityFormatsSettingsManagerEntry(itemPriority: number, opts?: Partial beta;getSelectionContextSyncEventIds(): string[] internal;getStableWidgetProps(widgetProps: WidgetProps, stableId: string): WidgetProps internal;getStagePanelType: (location: StagePanelLocation_2) => StagePanelType +beta;getUiSettingsManagerEntry(itemPriority: number, allowSettingUiFrameworkVersion?: boolean): SettingsTabEntry internal;getWidgetId(side: PanelSide, key: StagePanelZoneDefKeys): WidgetIdTypes public;GroupButton(props: GroupButtonProps): JSX.Element internal;GroupButtonItem(props: GroupButtonProps_2): JSX.Element @@ -259,9 +280,11 @@ alpha;HideIsolateEmphasizeManager alpha;HTMLElementPopup alpha;HTMLElementPopupProps beta;IModelAppUiSettings +deprecated;IModelAppUiSettings beta;IModelConnectedCategoryTree: import("react-redux").ConnectedComponent alpha;IModelConnectedModelsTree: import("react-redux").ConnectedComponent beta;IModelConnectedNavigationWidget: import("react-redux").ConnectedComponent +deprecated;IModelConnectedNavigationWidget: import("react-redux").ConnectedComponent beta;IModelConnectedSpatialContainmentTree: import("react-redux").ConnectedComponent beta;IModelConnectedViewport: import("react-redux").ConnectedComponent beta;IModelConnectedViewSelector: import("react-redux").ConnectedComponent @@ -274,6 +297,7 @@ beta;IModelViewportControl beta;IModelViewportControlOptions internal;INACTIVITY_TIME_DEFAULT = 3500 beta;Indicator +beta;InitialAppUiSettings internal;initializeNineZoneState(frontstageDef: FrontstageDef): NineZoneState internal;initializePanel(nineZone: NineZoneState, frontstageDef: FrontstageDef, panelSide: PanelSide): NineZoneState alpha;InputEditorCommitHandler @@ -290,7 +314,7 @@ beta;isNoSelectionActive(): boolean internal;isPanelCollapsed(zoneStates: ReadonlyArray internal;isReactContent: (content: PopupContentType) => content is ReactContent internal;isReactNotifyMessageDetails: (details: any) => details is ReactNotifyMessageDetails -alpha;isStatusBarItem: (item: CommonStatusBarItem) => item is StatusBarItem +public;isStatusBarItem: (item: CommonStatusBarItem) => item is StatusBarItem internal;isToolSettingsWidgetManagerProps: (props: WidgetManagerProps | undefined) => props is ToolSettingsWidgetManagerProps public;class ItemDefBase public;ItemList @@ -307,8 +331,8 @@ public;KeyboardShortcutProps beta;KeyinBrowser beta;KeyinBrowserExecuteArgs beta;KeyinBrowserProps -beta;KeyinEntry -beta;KeyinFieldLocalization +public;KeyinEntry +public;KeyinFieldLocalization internal;KeyinPalettePanel({ keyins, onKeyinExecuted, historyLength: allowedHistoryLength }: KeyinPalettePanelProps): JSX.Element alpha;KeyinPalettePopup({ el, id, keyins, onCancel, onItemExecuted }: KeyinPalettePopupProps): JSX.Element alpha;KeyinPalettePopupProps @@ -332,7 +356,7 @@ alpha;MenuButtonPopupProps alpha;MenuButtonProps alpha;MenuItem alpha;MenuItemHelpers -beta;MenuItemProps = AbstractMenuItemProps +public;MenuItemProps = AbstractMenuItemProps public;MessageAddedEvent public;MessageAddedEventArgs public;MessageCenterField @@ -360,6 +384,7 @@ public;ModelessDialogManager public;ModelessDialogProps public;ModelessDialogRenderer internal;ModelSelectorWidget +deprecated;ModelSelectorWidget internal;ModelSelectorWidgetControl public;ModelsTree(props: ModelsTreeProps): JSX.Element beta;ModelsTreeNodeType @@ -369,18 +394,21 @@ alpha;ModelsVisibilityHandler alpha;ModelsVisibilityHandlerProps public;MouseDownChangedEvent public;MouseDownChangedEventArgs -beta;NameToReducerMap +public;NameToReducerMap public;NavigationAidActivatedEvent public;NavigationAidActivatedEventArgs public;NavigationAidControl -beta;NavigationAidHost(props: NavigationAidHostProps): JSX.Element -beta;NavigationAidHostProps +public;NavigationAidHost(props: NavigationAidHostProps): JSX.Element +public;NavigationAidHostProps public;NavigationWidget -beta;NavigationWidgetComposer(props: NavigationWidgetComposerProps): JSX.Element -beta;NavigationWidgetComposerProps +deprecated;NavigationWidget +public;NavigationWidgetComposer(props: NavigationWidgetComposerProps): JSX.Element +public;NavigationWidgetComposerProps public;NavigationWidgetDef +deprecated;NavigationWidgetDef public;NavigationWidgetProps public;NavigationWidgetPropsEx +deprecated;NavigationWidgetPropsEx public;NestedFrontstage public;NineZoneChangeHandler public;NotifyMessageDetailsType = NotifyMessageDetails | ReactNotifyMessageDetails @@ -396,9 +424,13 @@ public;PointerMessageChangedEvent public;PointerMessageChangedEventArgs public;PointerMessageProps public;PopupButton +deprecated;PopupButton public;PopupButtonChildrenRenderProp = (args: PopupButtonChildrenRenderPropArgs) => React.ReactNode +deprecated;PopupButtonChildrenRenderProp = (args: PopupButtonChildrenRenderPropArgs) => React.ReactNode public;PopupButtonChildrenRenderPropArgs +deprecated;PopupButtonChildrenRenderPropArgs public;PopupButtonProps +deprecated;PopupButtonProps alpha;PopupContentType = HTMLElement | ReactContent alpha;PopupInfo alpha;PopupManager @@ -409,17 +441,19 @@ alpha;PopupsChangedEventArgs alpha;PositionPopup alpha;PositionPopupContent(props: CommonDivProps): JSX.Element alpha;PositionPopupProps -beta;PresentationSelectionScope +public;PresentationSelectionScope internal;ProjectInfo internal;ProjectReadStatus internal;ProjectScope internal;ProjectServices public;PromptField: import("react-redux").ConnectedComponent +deprecated;PromptField: import("react-redux").ConnectedComponent public;PropsHelper -beta;QuantityFormatSettingsPanel({ initialQuantityType, availableUnitSystems }: QuantityFormatterSettingsOptions): JSX.Element +beta;QuantityFormatSettingsPage({ initialQuantityType, availableUnitSystems }: QuantityFormatterSettingsOptions): JSX.Element beta;QuantityFormatterSettingsOptions alpha;ReactContent public;ReactMessage = ReactMessage_2 +deprecated;ReactMessage = ReactMessage_2 public;ReactNotifyMessageDetails public;Reducer public;ReducerActions @@ -450,18 +484,18 @@ beta;SelectionContextToolDefinitions public;SelectionInfoField: import("react-redux").ConnectedComponent public;SelectionScopeField: import("react-redux").ConnectedComponent public;SeparatorBackstageItem -beta;SessionState -beta;SessionStateActionId -beta;SessionStateActions: +public;SessionState +public;SessionStateActionId +public;SessionStateActions: beta;SessionStateActionsProps -beta;SessionStateActionsUnion = ActionsUnion +public;SessionStateActionsUnion = ActionsUnion beta;sessionStateMapDispatchToProps: -beta;SessionStateReducer(state: SessionState | undefined, action: SessionStateActionsUnion): DeepReadonly -internal;setPanelSize: +public;SessionStateReducer(state: SessionState | undefined, action: SessionStateActionsUnion): DeepReadonly +internal;setPanelSize: (base: beta;SettingsModalFrontstage internal;settingsStatusToUiSettingsStatus(status: SettingsStatus): UiSettingsStatus -internal;setWidgetLabel: -internal;setWidgetState: +internal;setWidgetLabel: (base: +internal;setWidgetState: (base: alpha;SheetCard alpha;SheetCardProps alpha;SheetData @@ -469,7 +503,7 @@ alpha;SheetNavigationAid alpha;SheetNavigationAidControl alpha;SheetNavigationProps alpha;SheetsModalFrontstage -internal;showWidget: +internal;showWidget: (base: public;SignIn public;SignInProps public;SignOutModalFrontstage @@ -478,41 +512,43 @@ alpha;SolarTimelineDataProvider public;SpatialContainmentTree(props: SpatialContainmentTreeProps): JSX.Element public;SpatialContainmentTreeProps public;SpecialKey +deprecated;SpecialKey internal;SplitterPaneTarget internal;SplitterPaneTargetProps -beta;StagePanel +public;StagePanel public;StagePanelChangeHandler -beta;StagePanelDef -beta;StagePanelDefaultProps = Pick +public;StagePanelDef +public;StagePanelDefaultProps = Pick alpha;StagePanelHeader alpha;StagePanelHeaderProps alpha;StagePanelLocation -beta;StagePanelMaxSizeSpec = number | -beta;StagePanelProps +public;StagePanelMaxSizeSpec = number | +public;StagePanelProps internal;StagePanelRuntimeProps alpha;StagePanelSection -beta;StagePanelState +public;StagePanelState internal;StagePanelZoneDef internal;StagePanelZoneDefKeys = keyof Pick -beta;StagePanelZoneProps +public;StagePanelZoneProps internal;StagePanelZonesDef -beta;StagePanelZonesProps +public;StagePanelZonesProps public;StandardMessageBox public;StandardMessageBoxProps alpha;StandardRotationNavigationAid alpha;StandardRotationNavigationAidControl -beta;StateManager +public;StateManager public;StateType public;StatusBar public;StatusBarCenterSection(props: CommonDivProps): JSX.Element -beta;StatusBarComposer(props: StatusBarComposerProps): JSX.Element -beta;StatusBarComposerProps +public;StatusBarComposer(props: StatusBarComposerProps): JSX.Element +public;StatusBarComposerProps internal;StatusBarContext: React.Context public;StatusBarFieldId = string | null -beta;StatusBarItem +public;StatusBarItem internal;StatusBarItemProps beta;StatusBarItemsManager -beta;StatusBarItemUtilities +deprecated;StatusBarItemsManager +public;StatusBarItemUtilities public;StatusBarLeftSection(props: CommonDivProps): JSX.Element public;StatusBarProps public;StatusBarRightSection(props: CommonDivProps): JSX.Element @@ -556,10 +592,10 @@ internal;ToolAssistanceFieldDefaultProps = Pick public;ToolAssistanceFieldProps internal;Toolbar beta;ToolbarButtonHelper -beta;ToolbarComposer(props: ExtensibleToolbarProps): JSX.Element +public;ToolbarComposer(props: ExtensibleToolbarProps): JSX.Element beta;ToolbarDragInteractionContext: React.Context internal;ToolbarGroupItem -beta;ToolbarHelper +public;ToolbarHelper alpha;ToolbarPopup alpha;ToolbarPopupProps internal;ToolbarProps @@ -585,11 +621,14 @@ internal;ToolSettingsZone internal;ToolSettingsZoneProps public;ToolUiProvider public;ToolWidget -beta;ToolWidgetComposer(props: ToolWidgetComposerProps): JSX.Element -beta;ToolWidgetComposerProps +deprecated;ToolWidget +public;ToolWidgetComposer(props: ToolWidgetComposerProps): JSX.Element +public;ToolWidgetComposerProps public;ToolWidgetDef +deprecated;ToolWidgetDef public;ToolWidgetProps public;ToolWidgetPropsEx +deprecated;ToolWidgetPropsEx internal;toPanelSide(location: StagePanelLocation_2): PanelSide internal;TrackingTime internal;UiActivityEvent @@ -600,9 +639,11 @@ public;UiFramework internal;UiIntervalEvent internal;UiIntervalEventArgs internal;UiSettingsContext: React.Context -alpha;UiSettingsProvider(props: UiSettingsProviderProps): JSX.Element -alpha;UiSettingsProviderProps +beta;UiSettingsPage({ allowSettingUiFrameworkVersion }: +beta;UiSettingsProvider(props: UiSettingsProviderProps): JSX.Element +beta;UiSettingsProviderProps public;UiShowHideManager +internal;UiShowHideSettingsProvider public;UiVisibilityChangedEvent public;UiVisibilityEventArgs internal;useActiveFrontstageDef(): FrontstageDef | undefined @@ -626,6 +667,8 @@ internal;useNineZoneDispatch(frontstageDef: FrontstageDef): NineZoneDispatch internal;useNineZoneState(frontstageDef: FrontstageDef): NineZoneState | undefined public;UserProfileBackstageItem public;UserProfileBackstageItemProps +beta;UserSettingsProvider +public;UserSettingsStorage internal;useSavedFrontstageState(frontstageDef: FrontstageDef): void internal;useSaveFrontstageSettings(frontstageDef: FrontstageDef): void internal;useStatusBarEntry(): DockedStatusBarEntryContextArg @@ -633,7 +676,7 @@ internal;useSyncDefinitions(frontstageDef: FrontstageDef): void internal;useToolSettingsNode(): React.ReactNode beta;useUiItemsProviderStatusBarItems: (manager: StatusBarItemsManager_2) => readonly CommonStatusBarItem[] beta;useUiItemsProviderToolbarItems: (manager: ToolbarItemsManager, toolbarUsage: ToolbarUsage, toolbarOrientation: ToolbarOrientation) => readonly CommonToolbarItem[] -internal;useUiSettingsContext(): UiSettings +beta;useUiSettingsStorageContext(): UiSettingsStorage internal;useUiVisibility(): boolean internal;useUpdateNineZoneSize(frontstageDef: FrontstageDef): void alpha;useVisibilityTreeFiltering: (nodeLoader: AbstractTreeNodeLoaderWithProvider @@ -698,15 +741,16 @@ internal;WidgetStackTabProps internal;WidgetStackTabs internal;WidgetStackTabsProps public;WidgetState +deprecated;WidgetState public;WidgetStateChangedEvent public;WidgetStateChangedEventArgs public;WidgetStateFunc = (state: Readonly internal;WidgetTab internal;WidgetTabs = public;WidgetType -beta;withMessageCenterFieldProps: +public;withMessageCenterFieldProps: alpha;withSafeArea: -beta;withStatusFieldProps: +public;withStatusFieldProps: public;Workflow public;WorkflowActivatedEvent public;WorkflowActivatedEventArgs diff --git a/common/api/ui-abstract.api.md b/common/api/ui-abstract.api.md index d2d7fa78611..805f1f4b410 100644 --- a/common/api/ui-abstract.api.md +++ b/common/api/ui-abstract.api.md @@ -25,7 +25,7 @@ export interface AbstractMenuItemProps extends CommonItemProps { submenu?: AbstractMenuItemProps[]; } -// @beta +// @public export interface AbstractStatusBarActionItem extends AbstractStatusBarItem { readonly execute: () => void; readonly icon?: string | ConditionalStringValue; @@ -33,13 +33,13 @@ export interface AbstractStatusBarActionItem extends AbstractStatusBarItem { readonly tooltip?: string | ConditionalStringValue; } -// @beta +// @public export interface AbstractStatusBarCustomItem extends AbstractStatusBarItem { // (undocumented) readonly isCustom: true; } -// @beta +// @public export interface AbstractStatusBarItem extends ProvidedItem { applicationData?: any; readonly badgeType?: BadgeType; @@ -51,13 +51,13 @@ export interface AbstractStatusBarItem extends ProvidedItem { readonly section: StatusBarSection; } -// @beta +// @public export class AbstractStatusBarItemUtilities { static createActionItem: (id: string, section: StatusBarSection, itemPriority: number, icon: string | ConditionalStringValue, tooltip: string | ConditionalStringValue, execute: () => void, overrides?: Partial | undefined) => AbstractStatusBarActionItem; static createLabelItem: (id: string, section: StatusBarSection, itemPriority: number, icon: string | ConditionalStringValue, label: string | ConditionalStringValue, labelSide?: StatusBarLabelSide, overrides?: Partial | undefined) => AbstractStatusBarLabelItem; } -// @beta +// @public export interface AbstractStatusBarLabelItem extends AbstractStatusBarItem { readonly icon?: string | ConditionalStringValue; readonly label: string | ConditionalStringValue; @@ -70,7 +70,7 @@ export interface AbstractToolbarProps { toolbarId?: string; } -// @beta +// @public export interface AbstractWidgetProps extends ProvidedItem { readonly applicationData?: any; readonly badgeType?: BadgeType; @@ -196,7 +196,7 @@ export class AccuDrawUiAdmin { setMode(mode: AccuDrawMode): void; } -// @beta +// @public export interface ActionButton extends ToolbarItem { readonly execute: () => void; readonly icon: string | ConditionalStringValue; @@ -231,13 +231,13 @@ export interface ArrayValue extends BasePropertyValue { valueFormat: PropertyValueFormat.Array; } -// @beta +// @public export interface BackstageActionItem extends CommonBackstageItem { // (undocumented) readonly execute: () => void; } -// @beta +// @public export type BackstageItem = BackstageActionItem | BackstageStageLauncher; // @internal @@ -264,19 +264,19 @@ export class BackstageItemsManager { remove(itemIdOrItemIds: BackstageItem["id"] | ReadonlyArray): void; } -// @beta +// @public export enum BackstageItemType { ActionItem = 1, StageLauncher = 2 } -// @beta +// @public export class BackstageItemUtilities { static createActionItem: (itemId: string, groupPriority: number, itemPriority: number, execute: () => void, label: string | ConditionalStringValue, subtitle?: string | ConditionalStringValue | undefined, icon?: string | ConditionalStringValue | undefined, overrides?: Partial | undefined) => BackstageActionItem; static createStageLauncher: (frontstageId: string, groupPriority: number, itemPriority: number, label: string | ConditionalStringValue, subtitle?: string | ConditionalStringValue | undefined, icon?: string | ConditionalStringValue | undefined, overrides?: Partial | undefined) => BackstageStageLauncher; } -// @beta +// @public export interface BackstageStageLauncher extends CommonBackstageItem { // (undocumented) readonly stageId: string; @@ -891,7 +891,7 @@ export interface CommandHandler { parameters?: any; } -// @beta +// @public export interface CommonBackstageItem extends ProvidedItem { applicationData?: any; readonly badgeType?: BadgeType; @@ -922,10 +922,10 @@ export interface CommonItemProps { tooltip?: string | ConditionalStringValue; } -// @beta +// @public export type CommonStatusBarItem = AbstractStatusBarActionItem | AbstractStatusBarLabelItem | AbstractStatusBarCustomItem; -// @beta +// @public export type CommonToolbarItem = ActionButton | GroupButton | CustomButtonDefinition; // @public @@ -941,7 +941,7 @@ export class ConditionalBooleanValue { get value(): boolean; } -// @beta +// @public export class ConditionalStringValue { constructor(stringGetter: () => string, syncEventIds: string[], value?: string); static getValue(conditionalValue: ConditionalStringValue | string | undefined): string | undefined; @@ -960,7 +960,7 @@ export function convertSimple2RegExpPattern(pattern: string): string; // @internal (undocumented) export function createMatches(score: undefined | FuzzyScore): IMatch[]; -// @beta +// @public export interface CustomButtonDefinition extends ToolbarItem { readonly icon?: string | ConditionalStringValue; readonly isCustom: true; @@ -1207,7 +1207,7 @@ export interface GenericUiEventArgs { // @internal export const getClassName: (obj: any) => string; -// @beta +// @public export interface GroupButton extends ToolbarItem { readonly icon: string | ConditionalStringValue; readonly items: ReadonlyArray; @@ -1274,16 +1274,16 @@ export interface InputEditorSizeParams extends BasePropertyEditorParams { type: PropertyEditorParamTypes.InputEditorSize; } -// @beta +// @public export const isAbstractStatusBarActionItem: (item: CommonStatusBarItem) => item is AbstractStatusBarActionItem; -// @beta +// @public export const isAbstractStatusBarCustomItem: (item: CommonStatusBarItem) => item is AbstractStatusBarCustomItem; -// @beta +// @public export const isAbstractStatusBarLabelItem: (item: CommonStatusBarItem) => item is AbstractStatusBarLabelItem; -// @beta +// @public export const isActionItem: (item: BackstageItem) => item is BackstageActionItem; // @public @@ -1313,7 +1313,7 @@ export function isLowerAsciiLetter(code: number): boolean; // @internal (undocumented) export function isPatternInWord(patternLow: string, patternPos: number, patternLen: number, wordLow: string, wordPos: number, wordLen: number): boolean; -// @beta +// @public export const isStageLauncher: (item: BackstageItem) => item is BackstageStageLauncher; // @beta @@ -1489,6 +1489,8 @@ export class PropertyDescriptionHelper { // @alpha static buildImageCheckBoxDescription(name: string, label: string, imageOff: string, imageOn: string, additionalParams?: BasePropertyEditorParams[]): PropertyDescription; // @alpha + static buildNumberEditorDescription(name: string, label: string, overrideParams?: RangeEditorParams, additionalParams?: BasePropertyEditorParams[]): PropertyDescription; + // @alpha static buildTextEditorDescription(name: string, label: string, additionalParams?: BasePropertyEditorParams[]): PropertyDescription; // @alpha static buildToggleDescription(name: string, label: string, additionalParams?: BasePropertyEditorParams[]): PropertyDescription; @@ -1572,7 +1574,7 @@ export enum PropertyValueFormat { Struct = 2 } -// @beta +// @public export interface ProvidedItem { readonly providerId?: string; } @@ -1682,7 +1684,7 @@ export enum SpecialKey { Tab = "Tab" } -// @beta +// @public export enum StagePanelLocation { // (undocumented) Bottom = 105, @@ -1698,7 +1700,7 @@ export enum StagePanelLocation { TopMost = 102 } -// @beta +// @public export enum StagePanelSection { // (undocumented) End = 2, @@ -1803,7 +1805,7 @@ export enum StandardTypeNames { // @internal (undocumented) export function startsWithIgnoreCase(str: string, candidate: string): boolean; -// @beta +// @public export type StatusBarItemId = CommonStatusBarItem["id"]; // @internal @@ -1830,15 +1832,16 @@ export class StatusBarItemsManager { removeAll(): void; } -// @beta +// @public export enum StatusBarLabelSide { Left = 0, Right = 1 } -// @beta +// @public export enum StatusBarSection { Center = 1, + // @beta Context = 3, Left = 0, Message = 0, @@ -1889,7 +1892,7 @@ export enum TimeDisplay { H24MS = "hh:mm:ss" } -// @beta +// @public export interface ToolbarItem extends ProvidedItem { readonly applicationData?: any; readonly badgeType?: BadgeType; @@ -1905,7 +1908,7 @@ export interface ToolbarItem extends ProvidedItem { readonly parentToolGroupId?: string; } -// @beta +// @public export type ToolbarItemId = CommonToolbarItem["id"]; // @internal @@ -1936,7 +1939,7 @@ export class ToolbarItemsManager { setActiveToolId(toolId: string): void; } -// @beta +// @public export class ToolbarItemUtilities { static createActionButton: (id: string, itemPriority: number, icon: string | ConditionalStringValue, label: string | ConditionalStringValue, execute: () => void, overrides?: Partial | undefined) => ActionButton; static createGroupButton: (id: string, itemPriority: number, icon: string | ConditionalStringValue, label: string | ConditionalStringValue, items: ReadonlyArray, overrides?: Partial | undefined) => GroupButton; @@ -1945,13 +1948,13 @@ export class ToolbarItemUtilities { static isGroupButton(item: CommonToolbarItem): item is GroupButton; } -// @beta +// @public export enum ToolbarOrientation { Horizontal = 0, Vertical = 1 } -// @beta +// @public export enum ToolbarUsage { ContentManipulation = 0, ViewNavigation = 1 @@ -2036,7 +2039,7 @@ export interface UiFlags { allowKeyinPalette?: boolean; } -// @beta +// @public export interface UiItemProviderRegisteredEventArgs { // (undocumented) providerId: string; @@ -2062,7 +2065,7 @@ export interface UiItemsApplication { }; } -// @beta +// @public export enum UiItemsApplicationAction { Allow = 0, Disallow = 1, @@ -2085,7 +2088,7 @@ export class UiItemsArbiter { static updateWidgets(widgets: ReadonlyArray): ReadonlyArray; } -// @beta +// @public export class UiItemsManager { static getBackstageItems(): BackstageItem[]; static getStatusBarItems(stageId: string, stageUsage: string): CommonStatusBarItem[]; @@ -2099,7 +2102,7 @@ export class UiItemsManager { static unregister(uiProviderId: string): void; } -// @beta +// @public export interface UiItemsProvider { readonly id: string; onBackstageItemArbiterChange?: (item: BackstageItem, action: UiItemsApplicationAction) => void; @@ -2132,7 +2135,7 @@ export abstract class UiLayoutDataProvider extends UiDataProvider { supplyDialogItems(): DialogItem[] | undefined; } -// @beta +// @public export enum WidgetState { Closed = 1, Floating = 3, diff --git a/common/api/ui-components.api.md b/common/api/ui-components.api.md index 0d40aaeb546..4fb529318f5 100644 --- a/common/api/ui-components.api.md +++ b/common/api/ui-components.api.md @@ -73,6 +73,7 @@ import { TimeDisplay } from '@bentley/ui-abstract'; import { TimeFormat } from '@bentley/ui-core'; import { UiEvent } from '@bentley/ui-core'; import { UiSettings } from '@bentley/ui-core'; +import { UiSettingsStorage } from '@bentley/ui-core'; import { UnitProps } from '@bentley/imodeljs-quantity'; import { UnitsProvider } from '@bentley/imodeljs-quantity'; import { Vector3d } from '@bentley/geometry-core'; @@ -3107,7 +3108,7 @@ export class NumericInputEditor extends React.PureComponent; // (undocumented) - get hasFocus(): boolean; + hasFocus: boolean; // (undocumented) get htmlElement(): HTMLElement | null; // @internal (undocumented) @@ -3118,6 +3119,8 @@ export class NumericInputEditor extends React.PureComponent(observable: Observable): Observable_2; + // @internal (undocumented) export function toToolbarPopupRelativePosition(expandsTo: Direction, alignment: ToolbarPanelAlignment): RelativePosition; diff --git a/common/api/ui-core.api.md b/common/api/ui-core.api.md index 7cfad92565f..6420e016e9f 100644 --- a/common/api/ui-core.api.md +++ b/common/api/ui-core.api.md @@ -1247,8 +1247,8 @@ export interface LoadingStatusProps extends CommonProps { percent: number; } -// @beta -export class LocalUiSettings implements UiSettings { +// @public +export class LocalSettingsStorage implements UiSettingsStorage { constructor(w?: Window); // (undocumented) deleteSetting(settingNamespace: string, settingName: string): Promise; @@ -1260,6 +1260,11 @@ export class LocalUiSettings implements UiSettings { w: Window; } +// @beta @deprecated +export class LocalUiSettings extends LocalSettingsStorage { + constructor(w?: Window); +} + // @public export interface MainTabsProps extends TabsProps { mainClassName: string; @@ -1886,8 +1891,8 @@ export interface SelectProps extends React.SelectHTMLAttributes; @@ -1899,6 +1904,11 @@ export class SessionUiSettings implements UiSettings { w: Window; } +// @beta @deprecated +export class SessionUiSettings extends SessionSettingsStorage { + constructor(w?: Window); +} + // @beta export const SettingsContainer: ({ tabs, onSettingsTabSelected, currentSettingsTab, settingsManager, showHeader }: SettingsContainerProps) => JSX.Element; @@ -1920,7 +1930,7 @@ export interface SettingsContainerProps { export class SettingsManager { activateSettingsTab(settingsTabId: string): void; // (undocumented) - addSettingsProvider(settingsProvider: SettingsProvider): void; + addSettingsProvider(settingsProvider: SettingsTabsProvider): void; closeSettingsContainer(closeFunc: (args: any) => void, closeFuncArgs?: any): void; getSettingEntries(stageId: string, stageUsage: string): Array; // @internal @@ -1931,19 +1941,12 @@ export class SettingsManager { readonly onProcessSettingsTabActivation: ProcessSettingsTabActivationEvent; readonly onSettingsProvidersChanged: SettingsProvidersChangedEvent; // (undocumented) - get providers(): ReadonlyArray; - set providers(p: ReadonlyArray); + get providers(): ReadonlyArray; + set providers(p: ReadonlyArray); // (undocumented) removeSettingsProvider(providerId: string): boolean; } -// @beta -export interface SettingsProvider { - // (undocumented) - getSettingEntries(stageId: string, stageUsage: string): ReadonlyArray | undefined; - readonly id: string; -} - // @beta export class SettingsProvidersChangedEvent extends BeUiEvent { } @@ -1951,7 +1954,7 @@ export class SettingsProvidersChangedEvent extends BeUiEvent; + readonly providers: ReadonlyArray; } // @beta @@ -1967,6 +1970,13 @@ export interface SettingsTabEntry { readonly tooltip?: string | JSX.Element; } +// @beta +export interface SettingsTabsProvider { + // (undocumented) + getSettingEntries(stageId: string, stageUsage: string): ReadonlyArray | undefined; + readonly id: string; +} + // @internal export const shallowDiffers: (a: { [key: string]: any; @@ -2477,17 +2487,19 @@ export class UiCore { export class UiEvent extends BeUiEvent { } -// @beta +// @public export class UiSetting { - constructor(settingNamespace: string, settingName: string, getValue: () => T, applyValue?: ((v: T) => void) | undefined); + constructor(settingNamespace: string, settingName: string, getValue: () => T, applyValue?: ((v: T) => void) | undefined, defaultValue?: T | undefined); // (undocumented) applyValue?: ((v: T) => void) | undefined; - deleteSetting(uiSettings: UiSettings): Promise; - getSetting(uiSettings: UiSettings): Promise; - getSettingAndApplyValue(uiSettings: UiSettings): Promise; + // (undocumented) + defaultValue?: T | undefined; + deleteSetting(storage: UiSettingsStorage): Promise; + getSetting(storage: UiSettingsStorage): Promise; + getSettingAndApplyValue(storage: UiSettingsStorage): Promise; // (undocumented) getValue: () => T; - saveSetting(uiSettings: UiSettings): Promise; + saveSetting(storage: UiSettingsStorage): Promise; // (undocumented) settingName: string; // (undocumented) @@ -2495,14 +2507,7 @@ export class UiSetting { } // @public -export interface UiSettings { - // (undocumented) - deleteSetting(settingNamespace: string, settingName: string): Promise; - // (undocumented) - getSetting(settingNamespace: string, settingName: string): Promise; - // (undocumented) - saveSetting(settingNamespace: string, settingName: string, setting: any): Promise; -} +export type UiSettings = UiSettingsStorage; // @public export interface UiSettingsResult { @@ -2526,6 +2531,16 @@ export enum UiSettingsStatus { UnknownError = 2 } +// @public +export interface UiSettingsStorage { + // (undocumented) + deleteSetting(settingNamespace: string, settingName: string): Promise; + // (undocumented) + getSetting(settingNamespace: string, settingName: string): Promise; + // (undocumented) + saveSetting(settingNamespace: string, settingName: string, setting: any): Promise; +} + // @public export function UnderlinedButton(props: UnderlinedButtonProps): JSX.Element; diff --git a/common/api/ui-framework.api.md b/common/api/ui-framework.api.md index c97b89f6a1f..0853f6319e2 100644 --- a/common/api/ui-framework.api.md +++ b/common/api/ui-framework.api.md @@ -164,9 +164,11 @@ import { UiAdmin } from '@bentley/ui-abstract'; import { UiDataProvider } from '@bentley/ui-abstract'; import { UiEvent } from '@bentley/ui-core'; import { UiLayoutDataProvider } from '@bentley/ui-abstract'; +import { UiSetting } from '@bentley/ui-core'; import { UiSettings } from '@bentley/ui-core'; import { UiSettingsResult } from '@bentley/ui-core'; import { UiSettingsStatus } from '@bentley/ui-core'; +import { UiSettingsStorage } from '@bentley/ui-core'; import { UnifiedSelectionTreeEventHandler } from '@bentley/presentation-components'; import { UnifiedSelectionTreeEventHandlerParams } from '@bentley/presentation-components'; import { UnitSystemKey } from '@bentley/imodeljs-frontend'; @@ -486,6 +488,25 @@ export class AppNotificationManager extends NotificationManager { updatePointerMessage(displayPoint: XAndY, relativePosition: RelativePosition): void; } +// @beta +export class AppUiSettings implements UserSettingsProvider { + constructor(defaults: Partial); + // (undocumented) + apply(storage: UiSettingsStorage): Promise; + // (undocumented) + colorTheme: UiSetting; + // (undocumented) + dragInteraction: UiSetting; + // (undocumented) + frameworkVersion: UiSetting; + // (undocumented) + loadUserSettings(storage: UiSettingsStorage): Promise; + // (undocumented) + readonly providerId = "AppUiSettingsProvider"; + // (undocumented) + widgetOpacity: UiSetting; +} + // @beta export function areNoFeatureOverridesActive(): boolean; @@ -518,15 +539,15 @@ export interface BackstageActionItem extends BackstageActionItem_2 { readonly type: BackstageItemType.ActionItem; } -// @beta +// @public export function BackstageAppButton(props: BackstageAppButtonProps): JSX.Element; -// @beta +// @public export interface BackstageAppButtonProps { icon?: string; } -// @beta +// @public export function BackstageComposer(props: BackstageComposerProps): JSX.Element; // @internal (undocumented) @@ -546,7 +567,7 @@ export interface BackstageComposerItemProps { readonly item: BackstageItem; } -// @beta +// @public export interface BackstageComposerProps extends CommonProps { readonly header?: React.ReactNode; readonly items: BackstageItem[]; @@ -1014,6 +1035,10 @@ export class ConfigurableCreateInfo { // @public export enum ConfigurableUiActionId { + // (undocumented) + SetDragInteraction = "configurableui:set-drag-interaction", + // (undocumented) + SetFrameworkVersion = "configurableui:set-framework-version", // (undocumented) SetSnapMode = "configurableui:set_snapmode", // (undocumented) @@ -1030,6 +1055,8 @@ export const ConfigurableUiActions: { setTheme: (theme: string) => import("../redux/redux-ts").ActionWithPayload; setToolPrompt: (toolPrompt: string) => import("../redux/redux-ts").ActionWithPayload; setWidgetOpacity: (opacity: number) => import("../redux/redux-ts").ActionWithPayload; + setDragInteraction: (dragInteraction: boolean) => import("../redux/redux-ts").ActionWithPayload; + setFrameworkVersion: (frameworkVersion: string) => import("../redux/redux-ts").ActionWithPayload; }; // @public @@ -1120,10 +1147,12 @@ export class ConfigurableUiManager { } // @public -export function ConfigurableUiReducer(state: ConfigurableUiState | undefined, _action: ConfigurableUiActionsUnion): ConfigurableUiState; +export function ConfigurableUiReducer(state: ConfigurableUiState | undefined, action: ConfigurableUiActionsUnion): ConfigurableUiState; // @public export interface ConfigurableUiState { + // (undocumented) + frameworkVersion: string; // (undocumented) snapMode: number; // (undocumented) @@ -1131,6 +1160,8 @@ export interface ConfigurableUiState { // (undocumented) toolPrompt: string; // (undocumented) + useDragInteraction: boolean; + // (undocumented) widgetOpacity: number; } @@ -1431,7 +1462,7 @@ export class CursorInformation { static readonly onCursorUpdatedEvent: CursorUpdatedEvent; } -// @beta +// @public export interface CursorMenuData { // (undocumented) items: MenuItemProps[]; @@ -1578,7 +1609,7 @@ export interface CursorUpdatedEventArgs { oldPt: PointProps; } -// @beta +// @public export class CustomItemDef extends ActionButtonItemDef { constructor(props: CustomItemProps); // (undocumented) @@ -1595,7 +1626,7 @@ export class CustomItemDef extends ActionButtonItemDef { toolbarReactNode(index?: number): React.ReactNode; } -// @beta +// @public export interface CustomItemProps extends ItemProps { // (undocumented) customId?: string; @@ -1869,7 +1900,7 @@ export interface ExpandableSectionProps extends CommonProps { } // @internal (undocumented) -export const expandWidget: (base: Base, id: string) => Base; +}, id: string) => import("immer/dist/internal").WritableDraft; -// @beta +// @public export interface ExtensibleToolbarProps { // (undocumented) items: CommonToolbarItem[]; @@ -2016,7 +2047,7 @@ export interface FooterModeFieldProps extends StatusFieldProps { } // @internal (undocumented) -export class FrameworkAccuDraw extends AccuDraw { +export class FrameworkAccuDraw extends AccuDraw implements UserSettingsProvider { constructor(); static get displayNotifications(): boolean; static set displayNotifications(v: boolean); @@ -2032,6 +2063,8 @@ export class FrameworkAccuDraw extends AccuDraw { static readonly isSideRotationConditional: ConditionalBooleanValue; static readonly isTopRotationConditional: ConditionalBooleanValue; static readonly isViewRotationConditional: ConditionalBooleanValue; + // (undocumented) + loadUserSettings(storage: UiSettings): Promise; static readonly onAccuDrawUiSettingsChangedEvent: AccuDrawUiSettingsChangedEvent; // (undocumented) onCompassModeChange(): void; @@ -2043,6 +2076,8 @@ export class FrameworkAccuDraw extends AccuDraw { // (undocumented) onRotationModeChange(): void; // (undocumented) + readonly providerId = "FrameworkAccuDraw"; + // (undocumented) setFocusItem(index: ItemField): void; // (undocumented) static translateFromItemField(item: ItemField): AccuDrawField; @@ -2052,11 +2087,11 @@ export class FrameworkAccuDraw extends AccuDraw { static set uiSettings(v: AccuDrawUiSettings | undefined); } -// @beta +// @public export const FrameworkReducer: (state: import("./redux-ts").CombinedReducerState<{ configurableUiState: typeof ConfigurableUiReducer; sessionState: typeof SessionStateReducer; -}>, action: import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject>> | import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject>> | import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject>>) => import("./redux-ts").CombinedReducerState<{ +}>, action: import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject>> | import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject>> | import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject> | import("./redux-ts").DeepReadonlyObject>>) => import("./redux-ts").CombinedReducerState<{ configurableUiState: typeof ConfigurableUiReducer; sessionState: typeof SessionStateReducer; }>; @@ -2119,7 +2154,7 @@ export interface FrameworkStagePanelProps { widgetTabs: WidgetTabs; } -// @beta +// @public export interface FrameworkState { // (undocumented) configurableUiState: ConfigurableUiState; @@ -2132,7 +2167,7 @@ export class FrameworkToolAdmin extends ToolAdmin { processShortcutKey(e: KeyboardEvent, wentDown: boolean): boolean; } -// @beta +// @public export class FrameworkUiAdmin extends UiAdmin { closeDialog(dialogId: string): boolean; closeToolSettingsPopup(): boolean; @@ -2723,6 +2758,9 @@ export function getStableWidgetProps(widgetProps: WidgetProps, stableId: string) // @internal (undocumented) export const getStagePanelType: (location: StagePanelLocation_2) => StagePanelType; +// @beta +export function getUiSettingsManagerEntry(itemPriority: number, allowSettingUiFrameworkVersion?: boolean): SettingsTabEntry; + // @internal (undocumented) export function getWidgetId(side: PanelSide, key: StagePanelZoneDefKeys): WidgetIdTypes; @@ -2908,14 +2946,8 @@ export interface HTMLElementPopupProps extends PopupPropsBase { relativePosition: RelativePosition; } -// @beta -export class IModelAppUiSettings implements UiSettings { - // (undocumented) - deleteSetting(namespace: string, name: string): Promise; - // (undocumented) - getSetting(namespace: string, name: string): Promise; - // (undocumented) - saveSetting(namespace: string, name: string, setting: any): Promise; +// @beta @deprecated +export class IModelAppUiSettings extends UserSettingsStorage { } // @beta @@ -3038,6 +3070,18 @@ export class Indicator extends React.Component { render(): JSX.Element; } +// @beta +export interface InitialAppUiSettings { + // (undocumented) + colorTheme: string; + // (undocumented) + dragInteraction: boolean; + // (undocumented) + frameworkVersion: string; + // (undocumented) + widgetOpacity: number; +} + // @internal (undocumented) export function initializeNineZoneState(frontstageDef: FrontstageDef): NineZoneState; @@ -3130,7 +3174,7 @@ export const isReactContent: (content: PopupContentType) => content is ReactCont // @internal export const isReactNotifyMessageDetails: (details: any) => details is ReactNotifyMessageDetails; -// @alpha +// @public export const isStatusBarItem: (item: CommonStatusBarItem) => item is StatusBarItem; // @internal (undocumented) @@ -3343,14 +3387,14 @@ export interface KeyinBrowserProps extends CommonProps { onExecute?: (args: KeyinBrowserExecuteArgs) => void; } -// @beta +// @public export interface KeyinEntry { isHistory?: boolean; matches?: IMatch[]; value: string; } -// @beta +// @public export enum KeyinFieldLocalization { Both = 2, Localized = 1, @@ -3589,7 +3633,7 @@ export class MenuItemHelpers { static createMenuItems(itemPropsList: MenuItemProps[], onSelection?: () => void): MenuItem[]; } -// @beta +// @public export type MenuItemProps = AbstractMenuItemProps; // @public @@ -3935,7 +3979,7 @@ export interface MouseDownChangedEventArgs { mouseDown: boolean; } -// @beta +// @public export interface NameToReducerMap { // (undocumented) [name: string]: (state: any, action: any) => any; @@ -3965,10 +4009,10 @@ export class NavigationAidControl extends ConfigurableUiControl { set reactNode(r: React.ReactNode); } -// @beta +// @public export function NavigationAidHost(props: NavigationAidHostProps): JSX.Element; -// @beta +// @public export interface NavigationAidHostProps { // (undocumented) minHeight?: string; @@ -3989,10 +4033,10 @@ export class NavigationWidget extends React.Component; } -// @beta +// @public export function NavigationWidgetComposer(props: NavigationWidgetComposerProps): JSX.Element; -// @beta +// @public export interface NavigationWidgetComposerProps extends CommonProps { horizontalToolbar?: React.ReactNode; navigationAidHost?: React.ReactNode; @@ -4269,7 +4313,7 @@ export interface PositionPopupProps extends CommonProps { point: PointProps; } -// @beta +// @public export interface PresentationSelectionScope { // (undocumented) id: string; @@ -4328,7 +4372,7 @@ export class PropsHelper { } // @beta -export function QuantityFormatSettingsPanel({ initialQuantityType, availableUnitSystems }: QuantityFormatterSettingsOptions): JSX.Element; +export function QuantityFormatSettingsPage({ initialQuantityType, availableUnitSystems }: QuantityFormatterSettingsOptions): JSX.Element; // @beta export interface QuantityFormatterSettingsOptions { @@ -4552,7 +4596,7 @@ export class SeparatorBackstageItem extends React.PureComponent import("./redux-ts").ActionWithPayload>; setActiveIModelId: (iModelId: string) => import("./redux-ts").ActionWithPayload; @@ -4638,7 +4682,7 @@ export interface SessionStateActionsProps { updateCursorMenu: (typeof SessionStateActions.updateCursorMenu); } -// @beta +// @public export type SessionStateActionsUnion = ActionsUnion; // @beta @@ -4655,11 +4699,11 @@ export const sessionStateMapDispatchToProps: { updateCursorMenu: (cursorMenuData: CursorMenuData) => import("./redux-ts").ActionWithPayload>; }; -// @beta +// @public export function SessionStateReducer(state: SessionState | undefined, action: SessionStateActionsUnion): DeepReadonly; // @internal (undocumented) -export const setPanelSize: (base: Base, side: PanelSide, size: number | undefined) => Base; +}, side: PanelSide, size: number | undefined) => import("immer/dist/internal").WritableDraft; // @beta export class SettingsModalFrontstage implements ModalFrontstageInfo { @@ -4797,7 +4841,7 @@ export class SettingsModalFrontstage implements ModalFrontstageInfo { export function settingsStatusToUiSettingsStatus(status: SettingsStatus): UiSettingsStatus; // @internal (undocumented) -export const setWidgetLabel: (base: Base, id: string, label: string) => Base; +}, id: string, label: string) => import("immer/dist/internal").WritableDraft; // @internal (undocumented) -export const setWidgetState: (base: Base, widgetDef: WidgetDef, state: WidgetState_2) => Base; +}, widgetDef: WidgetDef, state: WidgetState_2) => import("immer/dist/internal").WritableDraft; // @alpha export class SheetCard extends React.Component { @@ -5093,7 +5137,7 @@ export class SheetsModalFrontstage implements ModalFrontstageInfo { } // @internal (undocumented) -export const showWidget: (base: Base, id: string) => Base; +}, id: string) => import("immer/dist/internal").WritableDraft; // @public export class SignIn extends React.PureComponent { @@ -5318,7 +5362,7 @@ export interface SplitterPaneTargetProps { paneIndex: number; } -// @beta +// @public export class StagePanel extends React.Component { constructor(props: StagePanelProps); // (undocumented) @@ -5349,7 +5393,7 @@ export interface StagePanelChangeHandler { handleTogglePanelCollapse(panelLocation: StagePanelLocation_2): void; } -// @beta +// @public export class StagePanelDef extends WidgetHost { constructor(); get applicationData(): any | undefined; @@ -5381,7 +5425,7 @@ export class StagePanelDef extends WidgetHost { get widgetDefs(): ReadonlyArray; } -// @beta +// @public export type StagePanelDefaultProps = Pick; // @alpha @@ -5414,12 +5458,12 @@ export enum StagePanelLocation { TopMost = 102 } -// @beta +// @public export type StagePanelMaxSizeSpec = number | { percentage: number; }; -// @beta +// @public export interface StagePanelProps { allowedZones?: ZoneLocation[]; applicationData?: any; @@ -5427,6 +5471,7 @@ export interface StagePanelProps { header?: React.ReactNode; maxSize?: StagePanelMaxSizeSpec; minSize?: number; + // @beta panelZones?: StagePanelZonesProps; pinned?: boolean; resizable: boolean; @@ -5472,7 +5517,7 @@ export enum StagePanelSection { Start = 0 } -// @beta +// @public export enum StagePanelState { // (undocumented) Minimized = 1, @@ -5493,7 +5538,7 @@ export class StagePanelZoneDef extends WidgetHost { // @internal (undocumented) export type StagePanelZoneDefKeys = keyof Pick; -// @beta +// @public export interface StagePanelZoneProps { applicationData?: any; widgets: Array>; @@ -5513,7 +5558,7 @@ export class StagePanelZonesDef { get start(): StagePanelZoneDef; } -// @beta +// @public export interface StagePanelZonesProps { end?: StagePanelZoneProps; middle?: StagePanelZoneProps; @@ -5554,7 +5599,7 @@ export class StandardRotationNavigationAidControl extends NavigationAidControl { static navigationAidId: string; } -// @beta +// @public export class StateManager { constructor(defaultReducers?: NameToReducerMap); // @internal @@ -5584,10 +5629,10 @@ export class StatusBar extends React.Component { // @public export function StatusBarCenterSection(props: CommonDivProps): JSX.Element; -// @beta +// @public export function StatusBarComposer(props: StatusBarComposerProps): JSX.Element; -// @beta +// @public export interface StatusBarComposerProps extends CommonProps { centerClassName?: string; items: CommonStatusBarItem[]; @@ -5602,7 +5647,7 @@ export const StatusBarContext: React.Context; // @public export type StatusBarFieldId = string | null; -// @beta +// @public export interface StatusBarItem extends AbstractStatusBarCustomItem { readonly reactNode: React.ReactNode; } @@ -5616,7 +5661,7 @@ export interface StatusBarItemProps extends CommonProps { export class StatusBarItemsManager extends StatusBarItemsManager_2 { } -// @beta +// @public export class StatusBarItemUtilities { static createStatusBarItem: (id: string, section: StatusBarSection, itemPriority: number, reactNode: React.ReactNode, itemProps?: Partial | undefined) => StatusBarItem; } @@ -5762,6 +5807,8 @@ export enum SyncUiEventId { NavigationAidActivated = "navigationaidactivated", SelectionSetChanged = "selectionsetchanged", SettingsProvidersChanged = "settingsproviderschanged", + // (undocumented) + ShowHideManagerSettingChange = "show-hide-setting-change", TaskActivated = "taskactivated", ToolActivated = "toolactivated", UiSettingsChanged = "uisettingschanged", @@ -5939,7 +5986,7 @@ export class ToolAssistanceField extends React.Component; // @internal (undocumented) - static contextType: React.Context; + static contextType: React.Context; // @internal (undocumented) static readonly defaultProps: ToolAssistanceFieldDefaultProps; // @internal (undocumented) @@ -5957,7 +6004,7 @@ export interface ToolAssistanceFieldProps extends StatusFieldProps { defaultPromptAtCursor: boolean; fadeOutCursorPrompt: boolean; includePromptAtCursor: boolean; - uiSettings?: UiSettings; + uiSettings?: UiSettingsStorage; } // @internal @@ -5983,7 +6030,7 @@ export class ToolbarButtonHelper { static searchVerticalToolbarsByTitle(title: string): HTMLButtonElement | null; } -// @beta +// @public export function ToolbarComposer(props: ExtensibleToolbarProps): JSX.Element; // @beta @@ -6006,7 +6053,7 @@ export class ToolbarGroupItem extends React.Component; } -// @beta +// @public export class ToolbarHelper { static constructChildToolbarItems(itemDefs: AnyItemDef[]): Array; static createCustomDefinitionToolbarItem(itemPriority: number, itemDef: CustomItemDef, overrides?: Partial): CustomToolbarItem; @@ -6263,10 +6310,10 @@ export class ToolWidget extends React.Component; } -// @beta +// @public export function ToolWidgetComposer(props: ToolWidgetComposerProps): JSX.Element; -// @beta +// @public export interface ToolWidgetComposerProps extends CommonProps { cornerItem?: React.ReactNode; horizontalToolbar?: React.ReactNode; @@ -6375,7 +6422,7 @@ export class UiFramework { // (undocumented) static getIsUiVisible(): boolean; // @beta (undocumented) - static getUiSettings(): UiSettings; + static getUiSettingsStorage(): UiSettingsStorage; // @beta (undocumented) static getUserInfo(): UserInfo | undefined; // (undocumented) @@ -6404,11 +6451,13 @@ export class UiFramework { // @internal (undocumented) static get packageName(): string; // @internal - static postTelemetry(eventName: string, eventId?: GuidString, contextId?: GuidString, iModeId?: GuidString, changeSetId?: GuidString, time?: TrackingTime, additionalProperties?: { + static postTelemetry(eventName: string, eventId?: GuidString, contextId?: GuidString, iModeId?: GuidString, changeSetId?: string, time?: TrackingTime, additionalProperties?: { [key: string]: any; }): Promise; // @internal (undocumented) static get projectServices(): ProjectServices; + // @alpha + static registerUserSettingsProvider(entry: UserSettingsProvider): boolean; // (undocumented) static setAccudrawSnapMode(snapMode: SnapMode): void; // (undocumented) @@ -6432,7 +6481,11 @@ export class UiFramework { // @beta static get settingsManager(): SettingsManager; // @beta (undocumented) - static setUiSettings(uiSettings: UiSettings, immediateSync?: boolean): void; + static setUiSettingsStorage(storage: UiSettingsStorage, immediateSync?: boolean): Promise; + // (undocumented) + static setUiVersion(version: string): void; + // (undocumented) + static setUseDragInteraction(useDragInteraction: boolean): void; // @beta (undocumented) static setUserInfo(userInfo: UserInfo | undefined, immediateSync?: boolean): void; // (undocumented) @@ -6443,6 +6496,8 @@ export class UiFramework { static translate(key: string | string[]): string; // @beta static get uiVersion(): string; + // (undocumented) + static get useDragInteraction(): boolean; // @alpha (undocumented) static get widgetManager(): WidgetManager; } @@ -6458,17 +6513,22 @@ export interface UiIntervalEventArgs { } // @internal (undocumented) -export const UiSettingsContext: React.Context; +export const UiSettingsContext: React.Context; -// @alpha +// @beta +export function UiSettingsPage({ allowSettingUiFrameworkVersion }: { + allowSettingUiFrameworkVersion: boolean; +}): JSX.Element; + +// @beta export function UiSettingsProvider(props: UiSettingsProviderProps): JSX.Element; -// @alpha +// @beta export interface UiSettingsProviderProps { // (undocumented) children?: React.ReactNode; // (undocumented) - uiSettings: UiSettings; + settingsStorage: UiSettingsStorage; } // @public @@ -6482,6 +6542,12 @@ export class UiShowHideManager { static set inactivityTime(time: number); static get isUiVisible(): boolean; static set isUiVisible(visible: boolean); + // @internal (undocumented) + static setAutoHideUi(value: boolean): void; + // @internal (undocumented) + static setSnapWidgetOpacity(value: boolean): void; + // @internal (undocumented) + static setUseProximityOpacity(value: boolean): void; static get showHideFooter(): boolean; static set showHideFooter(showHide: boolean); static get showHidePanels(): boolean; @@ -6494,6 +6560,22 @@ export class UiShowHideManager { static set useProximityOpacity(value: boolean); } +// @internal +export class UiShowHideSettingsProvider implements UserSettingsProvider { + // (undocumented) + static initialize(): void; + // (undocumented) + loadUserSettings(storage: UiSettings): Promise; + // (undocumented) + readonly providerId = "UiShowHideSettingsProvider"; + // (undocumented) + static storeAutoHideUi(v: boolean, storage?: UiSettings): Promise; + // (undocumented) + static storeSnapWidgetOpacity(v: boolean, storage?: UiSettings): Promise; + // (undocumented) + static storeUseProximityOpacity(v: boolean, storage?: UiSettings): Promise; + } + // @public export class UiVisibilityChangedEvent extends UiEvent { } @@ -6575,6 +6657,22 @@ export interface UserProfileBackstageItemProps extends CommonProps { userInfo: UserInfo; } +// @beta +export interface UserSettingsProvider { + loadUserSettings(storage: UiSettingsStorage): Promise; + providerId: string; +} + +// @public +export class UserSettingsStorage implements UiSettingsStorage { + // (undocumented) + deleteSetting(namespace: string, name: string): Promise; + // (undocumented) + getSetting(namespace: string, name: string): Promise; + // (undocumented) + saveSetting(namespace: string, name: string, setting: any): Promise; +} + // @internal (undocumented) export function useSavedFrontstageState(frontstageDef: FrontstageDef): void; @@ -6596,8 +6694,8 @@ export const useUiItemsProviderStatusBarItems: (manager: StatusBarItemsManager_2 // @beta export const useUiItemsProviderToolbarItems: (manager: ToolbarItemsManager, toolbarUsage: ToolbarUsage, toolbarOrientation: ToolbarOrientation) => readonly CommonToolbarItem[]; -// @internal (undocumented) -export function useUiSettingsContext(): UiSettings; +// @beta (undocumented) +export function useUiSettingsStorageContext(): UiSettingsStorage; // @internal (undocumented) export function useUiVisibility(): boolean; @@ -7276,7 +7374,7 @@ export interface WidgetStackTabsProps { widgetTabs: WidgetTabs; } -// @public +// @public @deprecated export enum WidgetState { Closed = 1, Floating = 3, @@ -7331,7 +7429,7 @@ export enum WidgetType { ToolSettings = 4 } -// @beta +// @public export const withMessageCenterFieldProps:

(Component: (((props: P) => React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)> | null) & C) | ((new (props: P) => React.Component) & C)) => (props: JSX.LibraryManagedAttributes>>) => JSX.Element; // @alpha @@ -7389,7 +7487,7 @@ export const withSafeArea:

(Component: ( contextType?: React.Context | undefined; }; -// @beta +// @public export const withStatusFieldProps:

(Component: (((props: P) => React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)> | null) & C) | ((new (props: P) => React.Component) & C)) => (props: JSX.LibraryManagedAttributes>>) => JSX.Element; // @public diff --git a/common/changes/@bentley/bentleyjs-core/bentleyjs-core-release-tags_2021-03-31-20-18.json b/common/changes/@bentley/bentleyjs-core/bentleyjs-core-release-tags_2021-03-31-20-18.json new file mode 100644 index 00000000000..627c3c7e25b --- /dev/null +++ b/common/changes/@bentley/bentleyjs-core/bentleyjs-core-release-tags_2021-03-31-20-18.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/bentleyjs-core", + "comment": "Promote APIs to public.", + "type": "none" + } + ], + "packageName": "@bentley/bentleyjs-core", + "email": "22944042+pmconne@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/build-tools/create-full-summary_2021-03-30-01-01.json b/common/changes/@bentley/build-tools/create-full-summary_2021-03-30-01-01.json new file mode 100644 index 00000000000..400e8489be0 --- /dev/null +++ b/common/changes/@bentley/build-tools/create-full-summary_2021-03-30-01-01.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/build-tools", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/build-tools", + "email": "31107829+calebmshafer@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/build-tools/extract-api-deprecated_2021-03-20-14-56.json b/common/changes/@bentley/build-tools/extract-api-deprecated_2021-03-20-14-56.json new file mode 100644 index 00000000000..400e8489be0 --- /dev/null +++ b/common/changes/@bentley/build-tools/extract-api-deprecated_2021-03-20-14-56.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/build-tools", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/build-tools", + "email": "31107829+calebmshafer@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/ecschema-locaters/updateXmldom_2021-03-23-11-10.json b/common/changes/@bentley/ecschema-locaters/updateXmldom_2021-03-23-11-10.json new file mode 100644 index 00000000000..2896b650840 --- /dev/null +++ b/common/changes/@bentley/ecschema-locaters/updateXmldom_2021-03-23-11-10.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/ecschema-locaters", + "comment": "update xmldom to 0.5.0", + "type": "none" + } + ], + "packageName": "@bentley/ecschema-locaters", + "email": "47949861+DaumantasJankauskas@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/ecschema-metadata/fixUnitConversionTests_2021-03-24-15-03.json b/common/changes/@bentley/ecschema-metadata/fixUnitConversionTests_2021-03-24-15-03.json new file mode 100644 index 00000000000..27cd900af79 --- /dev/null +++ b/common/changes/@bentley/ecschema-metadata/fixUnitConversionTests_2021-03-24-15-03.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/ecschema-metadata", + "comment": "Fix unit conversion tests and add checking if base units matches for conversions", + "type": "none" + } + ], + "packageName": "@bentley/ecschema-metadata", + "email": "Ivan.Kok@bentley.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/ecschema-metadata/updateXmldom_2021-03-23-11-10.json b/common/changes/@bentley/ecschema-metadata/updateXmldom_2021-03-23-11-10.json new file mode 100644 index 00000000000..22e510022fb --- /dev/null +++ b/common/changes/@bentley/ecschema-metadata/updateXmldom_2021-03-23-11-10.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/ecschema-metadata", + "comment": "update xmldom to 0.5.0", + "type": "none" + } + ], + "packageName": "@bentley/ecschema-metadata", + "email": "47949861+DaumantasJankauskas@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/ecschema2ts/updateXmldom_2021-03-23-11-10.json b/common/changes/@bentley/ecschema2ts/updateXmldom_2021-03-23-11-10.json new file mode 100644 index 00000000000..0b7325da4d3 --- /dev/null +++ b/common/changes/@bentley/ecschema2ts/updateXmldom_2021-03-23-11-10.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/ecschema2ts", + "comment": "update xmldom to 0.5.0", + "type": "none" + } + ], + "packageName": "@bentley/ecschema2ts", + "email": "47949861+DaumantasJankauskas@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/electron-manager/native-fixes_2021-03-24-23-49.json b/common/changes/@bentley/electron-manager/native-fixes_2021-03-24-23-49.json new file mode 100644 index 00000000000..0b73cdb1421 --- /dev/null +++ b/common/changes/@bentley/electron-manager/native-fixes_2021-03-24-23-49.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/electron-manager", + "comment": "Fixes to desktop and iOS apps. ", + "type": "none" + } + ], + "packageName": "@bentley/electron-manager", + "email": "32458710+ramanujam-raman@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/electron-manager/nativeapp-storage_2021-03-23-11-26.json b/common/changes/@bentley/electron-manager/nativeapp-storage_2021-03-23-11-26.json new file mode 100644 index 00000000000..ae43777d92a --- /dev/null +++ b/common/changes/@bentley/electron-manager/nativeapp-storage_2021-03-23-11-26.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/electron-manager", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/electron-manager", + "email": "33296803+kabentley@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/electron-manager/ui-uitestapp-electron-position-size_2021-03-23-13-55.json b/common/changes/@bentley/electron-manager/ui-uitestapp-electron-position-size_2021-03-23-13-55.json new file mode 100644 index 00000000000..f2bdcf8b347 --- /dev/null +++ b/common/changes/@bentley/electron-manager/ui-uitestapp-electron-position-size_2021-03-23-13-55.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/electron-manager", + "comment": "Saving & restoring Electron main window size, position & maximized state", + "type": "none" + } + ], + "packageName": "@bentley/electron-manager", + "email": "51122937+DanEastBentley@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/frontend-devtools/Reality-model-optimization_2021-03-25-19-22.json b/common/changes/@bentley/frontend-devtools/Reality-model-optimization_2021-03-25-19-22.json new file mode 100644 index 00000000000..da0df086cce --- /dev/null +++ b/common/changes/@bentley/frontend-devtools/Reality-model-optimization_2021-03-25-19-22.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/frontend-devtools", + "comment": "Fix error in map mask argument parsing.", + "type": "none" + } + ], + "packageName": "@bentley/frontend-devtools", + "email": "rbbentley@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/frontend-devtools/tools-saveviewquote_2021-03-26-13-23.json b/common/changes/@bentley/frontend-devtools/tools-saveviewquote_2021-03-26-13-23.json new file mode 100644 index 00000000000..36590ee2de8 --- /dev/null +++ b/common/changes/@bentley/frontend-devtools/tools-saveviewquote_2021-03-26-13-23.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/frontend-devtools", + "comment": "Added 'quote' argument to SaveViewTool.", + "type": "none" + } + ], + "packageName": "@bentley/frontend-devtools", + "email": "mdastous-bentley@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/geometry-core/EDLGridC_2021-03-25-16-14.json b/common/changes/@bentley/geometry-core/EDLGridC_2021-03-25-16-14.json new file mode 100644 index 00000000000..3ed5b768fc4 --- /dev/null +++ b/common/changes/@bentley/geometry-core/EDLGridC_2021-03-25-16-14.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/geometry-core", + "comment": "grid line filtering corrections", + "type": "none" + } + ], + "packageName": "@bentley/geometry-core", + "email": "69321059+EarlinLutz@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodelhub-client/changeSetId-string_2021-03-26-20-11.json b/common/changes/@bentley/imodelhub-client/changeSetId-string_2021-03-26-20-11.json new file mode 100644 index 00000000000..5f7d7db9e1c --- /dev/null +++ b/common/changes/@bentley/imodelhub-client/changeSetId-string_2021-03-26-20-11.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodelhub-client", + "comment": "Properly declare changeSetId variables as string.", + "type": "none" + } + ], + "packageName": "@bentley/imodelhub-client", + "email": "36768600+scsewall@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-backend/IModelTransformer-exclude-deletes_2021-03-26-17-00.json b/common/changes/@bentley/imodeljs-backend/IModelTransformer-exclude-deletes_2021-03-26-17-00.json new file mode 100644 index 00000000000..7dda981902a --- /dev/null +++ b/common/changes/@bentley/imodeljs-backend/IModelTransformer-exclude-deletes_2021-03-26-17-00.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-backend", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-backend", + "email": "36768600+scsewall@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-backend/add-checkpoint-dbguid-test_2021-03-30-04-00.json b/common/changes/@bentley/imodeljs-backend/add-checkpoint-dbguid-test_2021-03-30-04-00.json new file mode 100644 index 00000000000..3862fb39ed3 --- /dev/null +++ b/common/changes/@bentley/imodeljs-backend/add-checkpoint-dbguid-test_2021-03-30-04-00.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-backend", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-backend", + "email": "31107829+calebmshafer@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-backend/changeSetId-string_2021-03-26-20-11.json b/common/changes/@bentley/imodeljs-backend/changeSetId-string_2021-03-26-20-11.json new file mode 100644 index 00000000000..66af4e99ac0 --- /dev/null +++ b/common/changes/@bentley/imodeljs-backend/changeSetId-string_2021-03-26-20-11.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-backend", + "comment": "Properly declare changeSetId variables as string.", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-backend", + "email": "36768600+scsewall@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-backend/changed-elements-test-fix_2021-03-30-13-19.json b/common/changes/@bentley/imodeljs-backend/changed-elements-test-fix_2021-03-30-13-19.json new file mode 100644 index 00000000000..3c018291e38 --- /dev/null +++ b/common/changes/@bentley/imodeljs-backend/changed-elements-test-fix_2021-03-30-13-19.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-backend", + "comment": "Fix tests for changed elements processing", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-backend", + "email": "4107657+diegopinate@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-backend/disable-txnmanager-tests_2021-03-31-13-03.json b/common/changes/@bentley/imodeljs-backend/disable-txnmanager-tests_2021-03-31-13-03.json new file mode 100644 index 00000000000..dd040d3facb --- /dev/null +++ b/common/changes/@bentley/imodeljs-backend/disable-txnmanager-tests_2021-03-31-13-03.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-backend", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-backend", + "email": "33296803+kabentley@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-backend/fix-afterstartup_2021-03-25-17-40.json b/common/changes/@bentley/imodeljs-backend/fix-afterstartup_2021-03-25-17-40.json new file mode 100644 index 00000000000..a3b433e34e8 --- /dev/null +++ b/common/changes/@bentley/imodeljs-backend/fix-afterstartup_2021-03-25-17-40.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-backend", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-backend", + "email": "33036725+wgoehrig@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-backend/fix-checkpoint-dbguid_2021-03-24-20-51.json b/common/changes/@bentley/imodeljs-backend/fix-checkpoint-dbguid_2021-03-24-20-51.json new file mode 100644 index 00000000000..3862fb39ed3 --- /dev/null +++ b/common/changes/@bentley/imodeljs-backend/fix-checkpoint-dbguid_2021-03-24-20-51.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-backend", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-backend", + "email": "31107829+calebmshafer@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-backend/fix-logging-for-tests_2021-03-29-20-33.json b/common/changes/@bentley/imodeljs-backend/fix-logging-for-tests_2021-03-29-20-33.json new file mode 100644 index 00000000000..dd040d3facb --- /dev/null +++ b/common/changes/@bentley/imodeljs-backend/fix-logging-for-tests_2021-03-29-20-33.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-backend", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-backend", + "email": "33296803+kabentley@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-backend/fix-v2-guidmismatch_2021-03-25-16-33.json b/common/changes/@bentley/imodeljs-backend/fix-v2-guidmismatch_2021-03-25-16-33.json new file mode 100644 index 00000000000..a3b433e34e8 --- /dev/null +++ b/common/changes/@bentley/imodeljs-backend/fix-v2-guidmismatch_2021-03-25-16-33.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-backend", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-backend", + "email": "33036725+wgoehrig@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-backend/native-fixes_2021-03-24-23-49.json b/common/changes/@bentley/imodeljs-backend/native-fixes_2021-03-24-23-49.json new file mode 100644 index 00000000000..92c774a49be --- /dev/null +++ b/common/changes/@bentley/imodeljs-backend/native-fixes_2021-03-24-23-49.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-backend", + "comment": "Fixes to desktop and iOS apps.", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-backend", + "email": "32458710+ramanujam-raman@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-backend/nativeapp-storage_2021-03-23-11-26.json b/common/changes/@bentley/imodeljs-backend/nativeapp-storage_2021-03-23-11-26.json new file mode 100644 index 00000000000..dd040d3facb --- /dev/null +++ b/common/changes/@bentley/imodeljs-backend/nativeapp-storage_2021-03-23-11-26.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-backend", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-backend", + "email": "33296803+kabentley@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-backend/rename-standalone_2021-03-26-14-58.json b/common/changes/@bentley/imodeljs-backend/rename-standalone_2021-03-26-14-58.json new file mode 100644 index 00000000000..f85aabe2253 --- /dev/null +++ b/common/changes/@bentley/imodeljs-backend/rename-standalone_2021-03-26-14-58.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-backend", + "comment": "rename (deprecate) BriefcaseIdValue.Standalone to BriefcaseIdValue.Unassigned to reduce confusion", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-backend", + "email": "33296803+kabentley@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-backend/test-minor-agent-cleanup_2021-03-30-17-57.json b/common/changes/@bentley/imodeljs-backend/test-minor-agent-cleanup_2021-03-30-17-57.json new file mode 100644 index 00000000000..3862fb39ed3 --- /dev/null +++ b/common/changes/@bentley/imodeljs-backend/test-minor-agent-cleanup_2021-03-30-17-57.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-backend", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-backend", + "email": "31107829+calebmshafer@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-backend/ui-uitestapp-electron-position-size_2021-03-23-13-55.json b/common/changes/@bentley/imodeljs-backend/ui-uitestapp-electron-position-size_2021-03-23-13-55.json new file mode 100644 index 00000000000..63816290cf1 --- /dev/null +++ b/common/changes/@bentley/imodeljs-backend/ui-uitestapp-electron-position-size_2021-03-23-13-55.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-backend", + "comment": "Saving & restoring Electron main window size, position & maximized state", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-backend", + "email": "51122937+DanEastBentley@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-backend/ui-uitestapp-electron-position-size_2021-03-24-13-27.json b/common/changes/@bentley/imodeljs-backend/ui-uitestapp-electron-position-size_2021-03-24-13-27.json new file mode 100644 index 00000000000..85e53d381d0 --- /dev/null +++ b/common/changes/@bentley/imodeljs-backend/ui-uitestapp-electron-position-size_2021-03-24-13-27.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-backend", + "comment": "add NativeHost.settingsStore", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-backend", + "email": "33296803+kabentley@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-backend/update-tags_2021-03-31-11-07.json b/common/changes/@bentley/imodeljs-backend/update-tags_2021-03-31-11-07.json new file mode 100644 index 00000000000..dd040d3facb --- /dev/null +++ b/common/changes/@bentley/imodeljs-backend/update-tags_2021-03-31-11-07.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-backend", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-backend", + "email": "33296803+kabentley@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-common/Reality-model-optimization_2021-03-25-19-22.json b/common/changes/@bentley/imodeljs-common/Reality-model-optimization_2021-03-25-19-22.json new file mode 100644 index 00000000000..899d12bc362 --- /dev/null +++ b/common/changes/@bentley/imodeljs-common/Reality-model-optimization_2021-03-25-19-22.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-common", + "comment": "Optimize reality model processing.", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-common", + "email": "rbbentley@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-common/changeSetId-string_2021-03-26-20-11.json b/common/changes/@bentley/imodeljs-common/changeSetId-string_2021-03-26-20-11.json new file mode 100644 index 00000000000..0bcafc78d9a --- /dev/null +++ b/common/changes/@bentley/imodeljs-common/changeSetId-string_2021-03-26-20-11.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-common", + "comment": "Properly declare changeSetId variables as string.", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-common", + "email": "36768600+scsewall@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-common/native-fixes_2021-03-24-23-49.json b/common/changes/@bentley/imodeljs-common/native-fixes_2021-03-24-23-49.json new file mode 100644 index 00000000000..83f513ae0b1 --- /dev/null +++ b/common/changes/@bentley/imodeljs-common/native-fixes_2021-03-24-23-49.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-common", + "comment": "Fixes to desktop and iOS apps.", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-common", + "email": "32458710+ramanujam-raman@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-common/promote-thematic-display-api-public_2021-03-31-18-24.json b/common/changes/@bentley/imodeljs-common/promote-thematic-display-api-public_2021-03-31-18-24.json new file mode 100644 index 00000000000..24974e32eb6 --- /dev/null +++ b/common/changes/@bentley/imodeljs-common/promote-thematic-display-api-public_2021-03-31-18-24.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-common", + "comment": "Promote thematic display API to public.", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-common", + "email": "47000437+markschlosseratbentley@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-common/rename-standalone_2021-03-26-14-58.json b/common/changes/@bentley/imodeljs-common/rename-standalone_2021-03-26-14-58.json new file mode 100644 index 00000000000..4c63d15acc3 --- /dev/null +++ b/common/changes/@bentley/imodeljs-common/rename-standalone_2021-03-26-14-58.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-common", + "comment": "rename (deprecate) BriefcaseIdValue.Standalone to BriefcaseIdValue.Unassigned to reduce confusion", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-common", + "email": "33296803+kabentley@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-common/rpc-leak_2021-03-25-17-42.json b/common/changes/@bentley/imodeljs-common/rpc-leak_2021-03-25-17-42.json new file mode 100644 index 00000000000..4a7f1e0bdf2 --- /dev/null +++ b/common/changes/@bentley/imodeljs-common/rpc-leak_2021-03-25-17-42.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-common", + "comment": "Memory leak fix", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-common", + "email": "69857376+swbsi@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-common/update-tags_2021-03-30-12-15.json b/common/changes/@bentley/imodeljs-common/update-tags_2021-03-30-12-15.json new file mode 100644 index 00000000000..6cc378a91b4 --- /dev/null +++ b/common/changes/@bentley/imodeljs-common/update-tags_2021-03-30-12-15.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-common", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-common", + "email": "33296803+kabentley@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-frontend/EDLGridC_2021-03-25-16-14.json b/common/changes/@bentley/imodeljs-frontend/EDLGridC_2021-03-25-16-14.json new file mode 100644 index 00000000000..bf010a907aa --- /dev/null +++ b/common/changes/@bentley/imodeljs-frontend/EDLGridC_2021-03-25-16-14.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-frontend", + "comment": "gridline filtering corrections", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-frontend", + "email": "69321059+EarlinLutz@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-frontend/Reality-model-optimization_2021-03-25-19-22.json b/common/changes/@bentley/imodeljs-frontend/Reality-model-optimization_2021-03-25-19-22.json new file mode 100644 index 00000000000..03319e20f92 --- /dev/null +++ b/common/changes/@bentley/imodeljs-frontend/Reality-model-optimization_2021-03-25-19-22.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-frontend", + "comment": "Optimize reality model processing.", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-frontend", + "email": "rbbentley@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-frontend/fix-edge-and-polyline-z_2021-03-29-16-18.json b/common/changes/@bentley/imodeljs-frontend/fix-edge-and-polyline-z_2021-03-29-16-18.json new file mode 100644 index 00000000000..738a223a90e --- /dev/null +++ b/common/changes/@bentley/imodeljs-frontend/fix-edge-and-polyline-z_2021-03-29-16-18.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-frontend", + "comment": "fixed z for edges and polylines when extended behind the eye", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-frontend", + "email": "36053767+MarcNeely@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-frontend/fix-orbitgt-pointcloud-location_2021-03-16-08-45.json b/common/changes/@bentley/imodeljs-frontend/fix-orbitgt-pointcloud-location_2021-03-16-08-45.json new file mode 100644 index 00000000000..8075f7c4cb8 --- /dev/null +++ b/common/changes/@bentley/imodeljs-frontend/fix-orbitgt-pointcloud-location_2021-03-16-08-45.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-frontend", + "comment": "Fix orbitgt pointcloud position", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-frontend", + "email": "69574322+lerzeel2@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-frontend/nativeapp-storage_2021-03-23-11-26.json b/common/changes/@bentley/imodeljs-frontend/nativeapp-storage_2021-03-23-11-26.json new file mode 100644 index 00000000000..8f5f4b08451 --- /dev/null +++ b/common/changes/@bentley/imodeljs-frontend/nativeapp-storage_2021-03-23-11-26.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-frontend", + "comment": "promote NativeApp to beta", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-frontend", + "email": "33296803+kabentley@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-frontend/remove-deprecated-ldclient_2021-03-25-03-12.json b/common/changes/@bentley/imodeljs-frontend/remove-deprecated-ldclient_2021-03-25-03-12.json new file mode 100644 index 00000000000..38699ba4c6e --- /dev/null +++ b/common/changes/@bentley/imodeljs-frontend/remove-deprecated-ldclient_2021-03-25-03-12.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-frontend", + "comment": "Drop deprecated ldclient-js dependency", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-frontend", + "email": "31107829+calebmshafer@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-frontend/update-tags_2021-03-30-12-15.json b/common/changes/@bentley/imodeljs-frontend/update-tags_2021-03-30-12-15.json new file mode 100644 index 00000000000..3c74dc042da --- /dev/null +++ b/common/changes/@bentley/imodeljs-frontend/update-tags_2021-03-30-12-15.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-frontend", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-frontend", + "email": "33296803+kabentley@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-frontend/view-creator-2d-update_2021-02-18-17-08.json b/common/changes/@bentley/imodeljs-frontend/view-creator-2d-update_2021-02-18-17-08.json new file mode 100644 index 00000000000..c442c73b1ee --- /dev/null +++ b/common/changes/@bentley/imodeljs-frontend/view-creator-2d-update_2021-02-18-17-08.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-frontend", + "comment": "ViewCreator2d API - modelType parameter removed", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-frontend", + "email": "roopksaini@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-frontend/virtual-cursor-for-locate_2021-03-25-15-39.json b/common/changes/@bentley/imodeljs-frontend/virtual-cursor-for-locate_2021-03-25-15-39.json new file mode 100644 index 00000000000..9c0b09f7dd7 --- /dev/null +++ b/common/changes/@bentley/imodeljs-frontend/virtual-cursor-for-locate_2021-03-25-15-39.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-frontend", + "comment": "Add an option to use the virtual cursor to help with element locate w/touch input.", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-frontend", + "email": "65233531+bbastings@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-frontend/z-scaling_2021-03-22-21-25.json b/common/changes/@bentley/imodeljs-frontend/z-scaling_2021-03-22-21-25.json new file mode 100644 index 00000000000..7b035fe06de --- /dev/null +++ b/common/changes/@bentley/imodeljs-frontend/z-scaling_2021-03-22-21-25.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-frontend", + "comment": "Added capability to scale the model display transform nonuniformly and have still Accusnap properly.", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-frontend", + "email": "36053767+MarcNeely@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/itwin-client/nativeapp-storage_2021-03-23-11-26.json b/common/changes/@bentley/itwin-client/nativeapp-storage_2021-03-23-11-26.json new file mode 100644 index 00000000000..852052a589e --- /dev/null +++ b/common/changes/@bentley/itwin-client/nativeapp-storage_2021-03-23-11-26.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/itwin-client", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/itwin-client", + "email": "33296803+kabentley@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/itwin-client/updateXmldom_2021-03-23-11-10.json b/common/changes/@bentley/itwin-client/updateXmldom_2021-03-23-11-10.json new file mode 100644 index 00000000000..46747a4057f --- /dev/null +++ b/common/changes/@bentley/itwin-client/updateXmldom_2021-03-23-11-10.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/itwin-client", + "comment": "update xmldom to 0.5.0", + "type": "none" + } + ], + "packageName": "@bentley/itwin-client", + "email": "47949861+DaumantasJankauskas@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/mobile-manager/native-fixes_2021-03-24-23-49.json b/common/changes/@bentley/mobile-manager/native-fixes_2021-03-24-23-49.json new file mode 100644 index 00000000000..9717062131e --- /dev/null +++ b/common/changes/@bentley/mobile-manager/native-fixes_2021-03-24-23-49.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/mobile-manager", + "comment": "Fixes to desktop and iOS apps.", + "type": "none" + } + ], + "packageName": "@bentley/mobile-manager", + "email": "32458710+ramanujam-raman@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/perf-tools/extract-api-deprecated_2021-03-29-17-35.json b/common/changes/@bentley/perf-tools/extract-api-deprecated_2021-03-29-17-35.json new file mode 100644 index 00000000000..dfbd877fca5 --- /dev/null +++ b/common/changes/@bentley/perf-tools/extract-api-deprecated_2021-03-29-17-35.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/perf-tools", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/perf-tools", + "email": "31107829+calebmshafer@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/presentation-components/ui-components-rxjs_2021-03-26-14-55.json b/common/changes/@bentley/presentation-components/ui-components-rxjs_2021-03-26-14-55.json new file mode 100644 index 00000000000..137de3fd800 --- /dev/null +++ b/common/changes/@bentley/presentation-components/ui-components-rxjs_2021-03-26-14-55.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/presentation-components", + "comment": "Fix compatibility issue when multiple versions of `rxjs` are in use.", + "type": "none" + } + ], + "packageName": "@bentley/presentation-components", + "email": "70327485+roluk@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/presentation-components/update-immer_2021-03-24-15-39.json b/common/changes/@bentley/presentation-components/update-immer_2021-03-24-15-39.json new file mode 100644 index 00000000000..d603aa33bd4 --- /dev/null +++ b/common/changes/@bentley/presentation-components/update-immer_2021-03-24-15-39.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/presentation-components", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/presentation-components", + "email": "70327485+roluk@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/ui-abstract/toolSettingNumberEditorFix_2021-03-25-03-11.json b/common/changes/@bentley/ui-abstract/toolSettingNumberEditorFix_2021-03-25-03-11.json new file mode 100644 index 00000000000..70a2a967bad --- /dev/null +++ b/common/changes/@bentley/ui-abstract/toolSettingNumberEditorFix_2021-03-25-03-11.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/ui-abstract", + "comment": "Add PropertyDescriptionHelper.buildNumberEditorDescription method", + "type": "none" + } + ], + "packageName": "@bentley/ui-abstract", + "email": "65047615+bsteinbk@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/ui-abstract/ui-publish-apis_2021-03-29-13-40.json b/common/changes/@bentley/ui-abstract/ui-publish-apis_2021-03-29-13-40.json new file mode 100644 index 00000000000..897bc797c13 --- /dev/null +++ b/common/changes/@bentley/ui-abstract/ui-publish-apis_2021-03-29-13-40.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/ui-abstract", + "comment": "Publish APIs used by iTwinViewer.", + "type": "none" + } + ], + "packageName": "@bentley/ui-abstract", + "email": "45079789+NancyMcCallB@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/ui-components/addUiSettingsPage_2021-03-24-14-51.json b/common/changes/@bentley/ui-components/addUiSettingsPage_2021-03-24-14-51.json new file mode 100644 index 00000000000..d38006110ce --- /dev/null +++ b/common/changes/@bentley/ui-components/addUiSettingsPage_2021-03-24-14-51.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/ui-components", + "comment": "Update to use UiSettingsStorage.", + "type": "none" + } + ], + "packageName": "@bentley/ui-components", + "email": "65047615+bsteinbk@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/ui-components/fixHueSlider_2021-03-25-16-15.json b/common/changes/@bentley/ui-components/fixHueSlider_2021-03-25-16-15.json new file mode 100644 index 00000000000..34f114dadbb --- /dev/null +++ b/common/changes/@bentley/ui-components/fixHueSlider_2021-03-25-16-15.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/ui-components", + "comment": "Fix color picker hue display in DR by removing webkit prefix. Adjust max slider value.", + "type": "none" + } + ], + "packageName": "@bentley/ui-components", + "email": "65047615+bsteinbk@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/ui-components/toolSettingNumberEditorFix_2021-03-25-03-11.json b/common/changes/@bentley/ui-components/toolSettingNumberEditorFix_2021-03-25-03-11.json new file mode 100644 index 00000000000..aa4a4399b91 --- /dev/null +++ b/common/changes/@bentley/ui-components/toolSettingNumberEditorFix_2021-03-25-03-11.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/ui-components", + "comment": "Fix number editor so it can process Enter key and call OnCommit processing.", + "type": "none" + } + ], + "packageName": "@bentley/ui-components", + "email": "65047615+bsteinbk@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/ui-components/ui-components-rxjs_2021-03-26-14-55.json b/common/changes/@bentley/ui-components/ui-components-rxjs_2021-03-26-14-55.json new file mode 100644 index 00000000000..7bc633b21c1 --- /dev/null +++ b/common/changes/@bentley/ui-components/ui-components-rxjs_2021-03-26-14-55.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/ui-components", + "comment": "Fix compatibility issue when multiple versions of `rxjs` are in use.", + "type": "none" + } + ], + "packageName": "@bentley/ui-components", + "email": "70327485+roluk@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/ui-components/ui-fix-multiple-selection-mode-in-controlled-tree_2021-03-25-09-24.json b/common/changes/@bentley/ui-components/ui-fix-multiple-selection-mode-in-controlled-tree_2021-03-25-09-24.json new file mode 100644 index 00000000000..0194b2e04a3 --- /dev/null +++ b/common/changes/@bentley/ui-components/ui-fix-multiple-selection-mode-in-controlled-tree_2021-03-25-09-24.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/ui-components", + "comment": "Fix a crash when clicking on a `ControlledTree` node when the tree contains placeholder nodes and using `SelectionMode.Multiple`", + "type": "none" + } + ], + "packageName": "@bentley/ui-components", + "email": "35135765+grigasp@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/ui-components/update-immer_2021-03-24-15-39.json b/common/changes/@bentley/ui-components/update-immer_2021-03-24-15-39.json new file mode 100644 index 00000000000..8b0919d3a2b --- /dev/null +++ b/common/changes/@bentley/ui-components/update-immer_2021-03-24-15-39.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/ui-components", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/ui-components", + "email": "70327485+roluk@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/ui-core/addUiSettingsPage_2021-03-24-14-51.json b/common/changes/@bentley/ui-core/addUiSettingsPage_2021-03-24-14-51.json new file mode 100644 index 00000000000..d39f2cf1051 --- /dev/null +++ b/common/changes/@bentley/ui-core/addUiSettingsPage_2021-03-24-14-51.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/ui-core", + "comment": "Add more descriptive UiSettingsStorage, LocalSettingsStorage, and SessionSettingsStorage and deprecate badly name beta classes.", + "type": "none" + } + ], + "packageName": "@bentley/ui-core", + "email": "65047615+bsteinbk@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/ui-core/extract-api-deprecated_2021-03-29-17-35.json b/common/changes/@bentley/ui-core/extract-api-deprecated_2021-03-29-17-35.json new file mode 100644 index 00000000000..5c4be439a5e --- /dev/null +++ b/common/changes/@bentley/ui-core/extract-api-deprecated_2021-03-29-17-35.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/ui-core", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/ui-core", + "email": "31107829+calebmshafer@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/ui-framework/addUiSettingsPage_2021-03-24-14-51.json b/common/changes/@bentley/ui-framework/addUiSettingsPage_2021-03-24-14-51.json new file mode 100644 index 00000000000..1d417cc5433 --- /dev/null +++ b/common/changes/@bentley/ui-framework/addUiSettingsPage_2021-03-24-14-51.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/ui-framework", + "comment": "Add UiSettingsPage, AppUiSettings, and ability to register UserSettingsProvider to provide default settings from UsSettingsStorage.", + "type": "none" + } + ], + "packageName": "@bentley/ui-framework", + "email": "65047615+bsteinbk@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/ui-framework/changeSetId-string_2021-03-26-20-11.json b/common/changes/@bentley/ui-framework/changeSetId-string_2021-03-26-20-11.json new file mode 100644 index 00000000000..c7166054507 --- /dev/null +++ b/common/changes/@bentley/ui-framework/changeSetId-string_2021-03-26-20-11.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/ui-framework", + "comment": "Properly declare changeSetId variables as string.", + "type": "none" + } + ], + "packageName": "@bentley/ui-framework", + "email": "36768600+scsewall@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/ui-framework/extract-api-deprecated_2021-03-20-14-56.json b/common/changes/@bentley/ui-framework/extract-api-deprecated_2021-03-20-14-56.json new file mode 100644 index 00000000000..b0fb2c3ba94 --- /dev/null +++ b/common/changes/@bentley/ui-framework/extract-api-deprecated_2021-03-20-14-56.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/ui-framework", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/ui-framework", + "email": "31107829+calebmshafer@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/ui-framework/ui-components-rxjs_2021-03-26-14-55.json b/common/changes/@bentley/ui-framework/ui-components-rxjs_2021-03-26-14-55.json new file mode 100644 index 00000000000..61513fb4c2b --- /dev/null +++ b/common/changes/@bentley/ui-framework/ui-components-rxjs_2021-03-26-14-55.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/ui-framework", + "comment": "Fix compatibility issue when multiple versions of `rxjs` are in use.", + "type": "none" + } + ], + "packageName": "@bentley/ui-framework", + "email": "70327485+roluk@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/ui-framework/ui-publish-apis_2021-03-29-13-40.json b/common/changes/@bentley/ui-framework/ui-publish-apis_2021-03-29-13-40.json new file mode 100644 index 00000000000..4d79b17db98 --- /dev/null +++ b/common/changes/@bentley/ui-framework/ui-publish-apis_2021-03-29-13-40.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/ui-framework", + "comment": "Publish APIs used bu iTwinViewer.", + "type": "none" + } + ], + "packageName": "@bentley/ui-framework", + "email": "45079789+NancyMcCallB@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/ui-framework/update-immer_2021-03-24-15-39.json b/common/changes/@bentley/ui-framework/update-immer_2021-03-24-15-39.json new file mode 100644 index 00000000000..61faabf7bcc --- /dev/null +++ b/common/changes/@bentley/ui-framework/update-immer_2021-03-24-15-39.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/ui-framework", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/ui-framework", + "email": "70327485+roluk@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/ui-ninezone/update-immer_2021-03-24-15-39.json b/common/changes/@bentley/ui-ninezone/update-immer_2021-03-24-15-39.json new file mode 100644 index 00000000000..84a2c18a816 --- /dev/null +++ b/common/changes/@bentley/ui-ninezone/update-immer_2021-03-24-15-39.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/ui-ninezone", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/ui-ninezone", + "email": "70327485+roluk@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/config/rush/browser-approved-packages.json b/common/config/rush/browser-approved-packages.json index 5ea95b89629..9b6d1a6b90b 100644 --- a/common/config/rush/browser-approved-packages.json +++ b/common/config/rush/browser-approved-packages.json @@ -646,10 +646,6 @@ "name": "jsonwebtoken", "allowedCategories": [ "backend" ] }, - { - "name": "ldclient-js", - "allowedCategories": [ "frontend" ] - }, { "name": "linkify-it", "allowedCategories": [ "frontend" ] diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 82433820bf9..561816a6990 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -5,11 +5,11 @@ dependencies: '@babel/core': 7.7.4 '@bentley/icons-generic': 1.0.34 '@bentley/icons-generic-webfont': 1.0.34 - '@bentley/imodeljs-native': 2.15.0 + '@bentley/imodeljs-native': 2.15.1 '@bentley/react-scripts': 3.4.9_50a9a52775dfbe3fbd64ccf8e2a3fa32 '@bentley/units-schema': 1.0.6 '@microsoft/api-extractor': 7.7.3 - '@microsoft/applicationinsights-web': 2.6.0 + '@microsoft/applicationinsights-web': 2.6.1 '@openid/appauth': 1.3.0_debug@2.6.9 '@rush-temp/analytical-backend': 'file:projects/analytical-backend.tgz' '@rush-temp/backend-application-insights-client': 'file:projects/backend-application-insights-client.tgz' @@ -126,7 +126,7 @@ dependencies: '@types/faker': 4.1.12 '@types/file-saver': 2.0.1 '@types/flatbuffers': 1.10.0 - '@types/formidable': 1.2.0 + '@types/formidable': 1.2.1 '@types/fs-extra': 4.0.11 '@types/glob': 5.0.36 '@types/i18next': 8.4.6 @@ -172,11 +172,11 @@ dependencies: '@types/spdy': 3.4.4 '@types/stream-buffers': 3.0.3 '@types/superagent': 4.1.10 - '@types/supertest': 2.0.10 + '@types/supertest': 2.0.11 '@types/tar': 4.0.4 '@types/testing-library__react-hooks': 3.4.1 '@types/uuid': 7.0.4 - '@types/webpack': 4.41.26 + '@types/webpack': 4.41.27 '@types/webpack-sources': 0.1.8 '@types/ws': 6.0.4 '@types/xmldom': 0.1.30 @@ -259,7 +259,7 @@ dependencies: i18next-node-fs-backend: 2.1.3 i18next-xhr-backend: 2.0.1 ignore-styles: 5.0.1 - immer: 8.0.1 + immer: 9.0.1 immutable: 3.8.2 inspire-tree: 5.0.2 istanbul-instrumenter-loader: 3.0.1_webpack@4.42.0 @@ -270,7 +270,6 @@ dependencies: json5: 2.2.0 jsonc-parser: 2.0.3 jsonwebtoken: 8.5.1 - ldclient-js: 2.10.2 linkify-it: 2.2.0 lint-staged: 10.5.4 lodash: 4.17.21 @@ -338,7 +337,7 @@ dependencies: resolve: 1.19.0 resolve-url-loader: 3.1.2 rimraf: 3.0.2 - rxjs: 6.6.6 + rxjs: 6.6.7 sass: 1.32.8 sass-loader: 10.1.1_sass@1.32.8+webpack@4.42.0 save: 2.4.0 @@ -387,7 +386,7 @@ dependencies: wms-capabilities: 0.4.0 ws: 7.4.4 xml-js: 1.6.11 - xmldom: 0.1.31 + xmldom: 0.5.0 xmlhttprequest: 1.8.0 xpath: 0.0.27 yargonaut: 1.1.4 @@ -451,7 +450,7 @@ packages: node: '>=8.0.0' resolution: integrity: sha512-lNUmDRVGpanCsiUN3NWxFTdwmdFI53xwhkTFfHDGTYk46ca7Ind3nanJc+U6Zj9Tv+9nTCWRBscWEW1DyKOpTw== - /@azure/core-auth/1.2.0: + /@azure/core-auth/1.3.0: dependencies: '@azure/abort-controller': 1.0.4 tslib: 2.1.0 @@ -459,14 +458,14 @@ packages: engines: node: '>=8.0.0' resolution: - integrity: sha512-KUl+Nwn/Sm6Lw5d3U90m1jZfNSL087SPcqHLxwn2T6PupNKmcgsEbDjHB25gDvHO4h7pBsTlrdJAY7dz+Qk8GA== + integrity: sha512-kSDSZBL6c0CYdhb+7KuutnKGf2geeT+bCJAgccB0DD7wmNJSsQPcF7TcuoZX83B7VK4tLz/u+8sOO/CnCsYp8A== /@azure/ms-rest-azure-env/2.0.0: dev: false resolution: integrity: sha512-dG76W7ElfLi+fbTjnZVGj+M9e0BIEJmRxU6fHaUQ12bZBe8EJKYb2GV50YWNaP2uJiVQ5+7nXEVj1VN1UQtaEw== /@azure/ms-rest-js/1.11.2_debug@2.6.9: dependencies: - '@azure/core-auth': 1.2.0 + '@azure/core-auth': 1.3.0 axios: 0.21.1_debug@2.6.9 form-data: 2.5.1 tough-cookie: 2.5.0 @@ -479,11 +478,9 @@ packages: debug: '*' resolution: integrity: sha512-2AyQ1IKmLGKW7DU3/x3TsTBzZLcbC9YRI+yuDPuXAQrv3zar340K9wsxU413kHFIDjkWNCo9T0w5VtwcyWxhbQ== - /@azure/ms-rest-js/2.2.3: + /@azure/ms-rest-js/2.3.0: dependencies: - '@azure/core-auth': 1.2.0 - '@types/node-fetch': 2.5.8 - '@types/tunnel': 0.0.1 + '@azure/core-auth': 1.3.0 abort-controller: 3.0.0 form-data: 2.5.1 node-fetch: 2.6.1 @@ -494,18 +491,18 @@ packages: xml2js: 0.4.23 dev: false resolution: - integrity: sha512-sXOhOu/37Tr8428f32Jwuwga975Xw64pYg1UeUwOBMhkNgtn5vUuNRa3fhmem+I6f8EKoi6hOsYDFlaHeZ52jA== - /@azure/ms-rest-nodeauth/3.0.8: + integrity: sha512-8NOnHgovi61NpcUld53zRkY/IcQJBBO48VeMntNTUtaPo8yYYTnu1hWRvp6b6vpBnur7HGmuj692J9li5Kx6/Q== + /@azure/ms-rest-nodeauth/3.0.9: dependencies: '@azure/ms-rest-azure-env': 2.0.0 - '@azure/ms-rest-js': 2.2.3 + '@azure/ms-rest-js': 2.3.0 adal-node: 0.1.28 dev: false resolution: - integrity: sha512-HmEkzAUGWxWHrwHysX9vE00cuK2WjxCYvBWV6cbsvm+eSm3VXpkGHPSoCHXyqUQOGqu58LXI4XFVJxFT/s40pA== + integrity: sha512-+GdDHUJlWtIDanRZemFooLy68NsBDhN/Oni9DSFeoXIFNGlSe1IOes8/IRkQdrNXyUvBanuzzR7I5WYYzYQsmA== /@azure/storage-blob/10.4.0: dependencies: - '@azure/ms-rest-js': 2.2.3 + '@azure/ms-rest-js': 2.3.0 events: 3.3.0 tslib: 1.14.1 dev: false @@ -538,10 +535,10 @@ packages: '@babel/code-frame': 7.12.13 '@babel/generator': 7.13.9 '@babel/helpers': 7.13.10 - '@babel/parser': 7.13.12 + '@babel/parser': 7.13.13 '@babel/template': 7.12.13 - '@babel/traverse': 7.13.0 - '@babel/types': 7.13.12 + '@babel/traverse': 7.13.13 + '@babel/types': 7.13.14 convert-source-map: 1.7.0 debug: 4.3.1 json5: 2.2.0 @@ -558,12 +555,12 @@ packages: dependencies: '@babel/code-frame': 7.12.13 '@babel/generator': 7.13.9 - '@babel/helper-module-transforms': 7.13.12 + '@babel/helper-module-transforms': 7.13.14 '@babel/helpers': 7.13.10 - '@babel/parser': 7.13.12 + '@babel/parser': 7.13.13 '@babel/template': 7.12.13 - '@babel/traverse': 7.13.0 - '@babel/types': 7.13.12 + '@babel/traverse': 7.13.13 + '@babel/types': 7.13.14 convert-source-map: 1.7.0 debug: 4.3.1 gensync: 1.0.0-beta.2 @@ -579,7 +576,7 @@ packages: integrity: sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w== /@babel/generator/7.13.9: dependencies: - '@babel/types': 7.13.12 + '@babel/types': 7.13.14 jsesc: 2.5.2 source-map: 0.5.7 dev: false @@ -587,18 +584,18 @@ packages: integrity: sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw== /@babel/helper-annotate-as-pure/7.12.13: dependencies: - '@babel/types': 7.13.12 + '@babel/types': 7.13.14 dev: false resolution: integrity: sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw== /@babel/helper-builder-binary-assignment-operator-visitor/7.12.13: dependencies: '@babel/helper-explode-assignable-expression': 7.13.0 - '@babel/types': 7.13.12 + '@babel/types': 7.13.14 dev: false resolution: integrity: sha512-CZOv9tGphhDRlVjVkAgm8Nhklm9RzSmWpX2my+t7Ua/KT616pEzXsQCjinzvkRvHWJ9itO4f296efroX23XCMA== - /@babel/helper-compilation-targets/7.13.10_@babel+core@7.9.0: + /@babel/helper-compilation-targets/7.13.13_@babel+core@7.9.0: dependencies: '@babel/compat-data': 7.13.12 '@babel/core': 7.9.0 @@ -609,7 +606,7 @@ packages: peerDependencies: '@babel/core': ^7.0.0 resolution: - integrity: sha512-/Xju7Qg1GQO4mHZ/Kcs6Au7gfafgZnwm+a7sy/ow/tV1sHeraRUHbjdat8/UvDor4Tez+siGKDk6zIKtCPKVJA== + integrity: sha512-q1kcdHNZehBwD9jYPh3WyXcsFERi39X4I59I3NadciWtNDyZ6x+GboOxncFK0kXlKIv6BJm5acncehXWUjWQMQ== /@babel/helper-create-class-features-plugin/7.13.11_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -636,10 +633,10 @@ packages: /@babel/helper-define-polyfill-provider/0.1.5_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-compilation-targets': 7.13.10_@babel+core@7.9.0 + '@babel/helper-compilation-targets': 7.13.13_@babel+core@7.9.0 '@babel/helper-module-imports': 7.13.12 '@babel/helper-plugin-utils': 7.13.0 - '@babel/traverse': 7.13.0 + '@babel/traverse': 7.13.13 debug: 4.3.1 lodash.debounce: 4.0.8 resolve: 1.19.0 @@ -651,7 +648,7 @@ packages: integrity: sha512-nXuzCSwlJ/WKr8qxzW816gwyT6VZgiJG17zR40fou70yfAcqjoNyTLl/DQ+FExw5Hx5KNqshmN8Ldl/r2N7cTg== /@babel/helper-explode-assignable-expression/7.13.0: dependencies: - '@babel/types': 7.13.12 + '@babel/types': 7.13.14 dev: false resolution: integrity: sha512-qS0peLTDP8kOisG1blKbaoBg/o9OSa1qoumMjTK5pM+KDTtpxpsiubnCGP34vK8BXGcb2M9eigwgvoJryrzwWA== @@ -659,36 +656,36 @@ packages: dependencies: '@babel/helper-get-function-arity': 7.12.13 '@babel/template': 7.12.13 - '@babel/types': 7.13.12 + '@babel/types': 7.13.14 dev: false resolution: integrity: sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA== /@babel/helper-get-function-arity/7.12.13: dependencies: - '@babel/types': 7.13.12 + '@babel/types': 7.13.14 dev: false resolution: integrity: sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg== /@babel/helper-hoist-variables/7.13.0: dependencies: - '@babel/traverse': 7.13.0 - '@babel/types': 7.13.12 + '@babel/traverse': 7.13.13 + '@babel/types': 7.13.14 dev: false resolution: integrity: sha512-0kBzvXiIKfsCA0y6cFEIJf4OdzfpRuNk4+YTeHZpGGc666SATFKTz6sRncwFnQk7/ugJ4dSrCj6iJuvW4Qwr2g== /@babel/helper-member-expression-to-functions/7.13.12: dependencies: - '@babel/types': 7.13.12 + '@babel/types': 7.13.14 dev: false resolution: integrity: sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw== /@babel/helper-module-imports/7.13.12: dependencies: - '@babel/types': 7.13.12 + '@babel/types': 7.13.14 dev: false resolution: integrity: sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA== - /@babel/helper-module-transforms/7.13.12: + /@babel/helper-module-transforms/7.13.14: dependencies: '@babel/helper-module-imports': 7.13.12 '@babel/helper-replace-supers': 7.13.12 @@ -696,14 +693,14 @@ packages: '@babel/helper-split-export-declaration': 7.12.13 '@babel/helper-validator-identifier': 7.12.11 '@babel/template': 7.12.13 - '@babel/traverse': 7.13.0 - '@babel/types': 7.13.12 + '@babel/traverse': 7.13.13 + '@babel/types': 7.13.14 dev: false resolution: - integrity: sha512-7zVQqMO3V+K4JOOj40kxiCrMf6xlQAkewBB0eu2b03OO/Q21ZutOzjpfD79A5gtE/2OWi1nv625MrDlGlkbknQ== + integrity: sha512-QuU/OJ0iAOSIatyVZmfqB0lbkVP0kDRiKj34xy+QNsnVZi/PA6BoSoreeqnxxa9EHFAIL0R9XOaAR/G9WlIy5g== /@babel/helper-optimise-call-expression/7.12.13: dependencies: - '@babel/types': 7.13.12 + '@babel/types': 7.13.14 dev: false resolution: integrity: sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA== @@ -715,7 +712,7 @@ packages: dependencies: '@babel/helper-annotate-as-pure': 7.12.13 '@babel/helper-wrap-function': 7.13.0 - '@babel/types': 7.13.12 + '@babel/types': 7.13.14 dev: false resolution: integrity: sha512-pUQpFBE9JvC9lrQbpX0TmeNIy5s7GnZjna2lhhcHC7DzgBs6fWn722Y5cfwgrtrqc7NAJwMvOa0mKhq6XaE4jg== @@ -723,26 +720,26 @@ packages: dependencies: '@babel/helper-member-expression-to-functions': 7.13.12 '@babel/helper-optimise-call-expression': 7.12.13 - '@babel/traverse': 7.13.0 - '@babel/types': 7.13.12 + '@babel/traverse': 7.13.13 + '@babel/types': 7.13.14 dev: false resolution: integrity: sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw== /@babel/helper-simple-access/7.13.12: dependencies: - '@babel/types': 7.13.12 + '@babel/types': 7.13.14 dev: false resolution: integrity: sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA== /@babel/helper-skip-transparent-expression-wrappers/7.12.1: dependencies: - '@babel/types': 7.13.12 + '@babel/types': 7.13.14 dev: false resolution: integrity: sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA== /@babel/helper-split-export-declaration/7.12.13: dependencies: - '@babel/types': 7.13.12 + '@babel/types': 7.13.14 dev: false resolution: integrity: sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg== @@ -758,16 +755,16 @@ packages: dependencies: '@babel/helper-function-name': 7.12.13 '@babel/template': 7.12.13 - '@babel/traverse': 7.13.0 - '@babel/types': 7.13.12 + '@babel/traverse': 7.13.13 + '@babel/types': 7.13.14 dev: false resolution: integrity: sha512-1UX9F7K3BS42fI6qd2A4BjKzgGjToscyZTdp1DjknHLCIvpgne6918io+aL5LXFcER/8QWiwpoY902pVEqgTXA== /@babel/helpers/7.13.10: dependencies: '@babel/template': 7.12.13 - '@babel/traverse': 7.13.0 - '@babel/types': 7.13.12 + '@babel/traverse': 7.13.13 + '@babel/types': 7.13.14 dev: false resolution: integrity: sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ== @@ -779,13 +776,13 @@ packages: dev: false resolution: integrity: sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg== - /@babel/parser/7.13.12: + /@babel/parser/7.13.13: dev: false engines: node: '>=6.0.0' hasBin: true resolution: - integrity: sha512-4T7Pb244rxH24yR116LAuJ+adxXXnHhZaLJjegJVKSdoNCe4x1eDBaud5YIcQFcqzsaD5BHvJw5BQ0AZapdCRw== + integrity: sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw== /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/7.13.12_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -923,7 +920,7 @@ packages: dependencies: '@babel/compat-data': 7.13.12 '@babel/core': 7.9.0 - '@babel/helper-compilation-targets': 7.13.10_@babel+core@7.9.0 + '@babel/helper-compilation-targets': 7.13.13_@babel+core@7.9.0 '@babel/helper-plugin-utils': 7.13.0 '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.9.0 '@babel/plugin-transform-parameters': 7.13.0_@babel+core@7.9.0 @@ -1279,7 +1276,7 @@ packages: /@babel/plugin-transform-modules-amd/7.13.0_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-module-transforms': 7.13.12 + '@babel/helper-module-transforms': 7.13.14 '@babel/helper-plugin-utils': 7.13.0 babel-plugin-dynamic-import-node: 2.3.3 dev: false @@ -1290,7 +1287,7 @@ packages: /@babel/plugin-transform-modules-commonjs/7.13.8_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-module-transforms': 7.13.12 + '@babel/helper-module-transforms': 7.13.14 '@babel/helper-plugin-utils': 7.13.0 '@babel/helper-simple-access': 7.13.12 babel-plugin-dynamic-import-node: 2.3.3 @@ -1303,7 +1300,7 @@ packages: dependencies: '@babel/core': 7.9.0 '@babel/helper-hoist-variables': 7.13.0 - '@babel/helper-module-transforms': 7.13.12 + '@babel/helper-module-transforms': 7.13.14 '@babel/helper-plugin-utils': 7.13.0 '@babel/helper-validator-identifier': 7.12.11 babel-plugin-dynamic-import-node: 2.3.3 @@ -1315,7 +1312,7 @@ packages: /@babel/plugin-transform-modules-umd/7.13.0_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-module-transforms': 7.13.12 + '@babel/helper-module-transforms': 7.13.14 '@babel/helper-plugin-utils': 7.13.0 dev: false peerDependencies: @@ -1368,7 +1365,7 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-nqVigwVan+lR+g8Fj8Exl0UQX2kymtjcWfMOYM1vTYEKujeyv2SkMgazf2qNcK7l4SDiKyTA/nHCPqL4e2zo1A== - /@babel/plugin-transform-react-constant-elements/7.13.10_@babel+core@7.9.0: + /@babel/plugin-transform-react-constant-elements/7.13.13_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 '@babel/helper-plugin-utils': 7.13.0 @@ -1376,7 +1373,7 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-E+aCW9j7mLq01tOuGV08YzLBt+vSyr4bOPT75B6WrAlrUfmOYOZ/yWk847EH0dv0xXiCihWLEmlX//O30YhpIw== + integrity: sha512-SNJU53VM/SjQL0bZhyU+f4kJQz7bQQajnrZRSaU21hruG/NWY41AEM9AWXeXX90pYr/C2yAmTgI6yW3LlLrAUQ== /@babel/plugin-transform-react-display-name/7.12.13_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -1429,7 +1426,7 @@ packages: '@babel/helper-module-imports': 7.13.12 '@babel/helper-plugin-utils': 7.13.0 '@babel/plugin-syntax-jsx': 7.12.13_@babel+core@7.9.0 - '@babel/types': 7.13.12 + '@babel/types': 7.13.14 dev: false peerDependencies: '@babel/core': ^7.0.0-0 @@ -1555,7 +1552,7 @@ packages: dependencies: '@babel/compat-data': 7.13.12 '@babel/core': 7.9.0 - '@babel/helper-compilation-targets': 7.13.10_@babel+core@7.9.0 + '@babel/helper-compilation-targets': 7.13.13_@babel+core@7.9.0 '@babel/helper-plugin-utils': 7.13.0 '@babel/helper-validator-option': 7.12.17 '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.13.12_@babel+core@7.9.0 @@ -1617,11 +1614,11 @@ packages: '@babel/plugin-transform-unicode-escapes': 7.12.13_@babel+core@7.9.0 '@babel/plugin-transform-unicode-regex': 7.12.13_@babel+core@7.9.0 '@babel/preset-modules': 0.1.4_@babel+core@7.9.0 - '@babel/types': 7.13.12 + '@babel/types': 7.13.14 babel-plugin-polyfill-corejs2: 0.1.10_@babel+core@7.9.0 babel-plugin-polyfill-corejs3: 0.1.7_@babel+core@7.9.0 babel-plugin-polyfill-regenerator: 0.1.6_@babel+core@7.9.0 - core-js-compat: 3.9.1 + core-js-compat: 3.10.0 semver: 6.3.0 dev: false peerDependencies: @@ -1632,7 +1629,7 @@ packages: dependencies: '@babel/compat-data': 7.13.12 '@babel/core': 7.9.0 - '@babel/helper-compilation-targets': 7.13.10_@babel+core@7.9.0 + '@babel/helper-compilation-targets': 7.13.13_@babel+core@7.9.0 '@babel/helper-module-imports': 7.13.12 '@babel/helper-plugin-utils': 7.13.0 '@babel/plugin-proposal-async-generator-functions': 7.13.8_@babel+core@7.9.0 @@ -1685,9 +1682,9 @@ packages: '@babel/plugin-transform-typeof-symbol': 7.12.13_@babel+core@7.9.0 '@babel/plugin-transform-unicode-regex': 7.12.13_@babel+core@7.9.0 '@babel/preset-modules': 0.1.4_@babel+core@7.9.0 - '@babel/types': 7.13.12 + '@babel/types': 7.13.14 browserslist: 4.16.3 - core-js-compat: 3.9.1 + core-js-compat: 3.10.0 invariant: 2.2.4 levenary: 1.1.1 semver: 5.7.1 @@ -1702,17 +1699,18 @@ packages: '@babel/helper-plugin-utils': 7.13.0 '@babel/plugin-proposal-unicode-property-regex': 7.12.13_@babel+core@7.9.0 '@babel/plugin-transform-dotall-regex': 7.12.13_@babel+core@7.9.0 - '@babel/types': 7.13.12 + '@babel/types': 7.13.14 esutils: 2.0.3 dev: false peerDependencies: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg== - /@babel/preset-react/7.12.13_@babel+core@7.9.0: + /@babel/preset-react/7.13.13_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 '@babel/helper-plugin-utils': 7.13.0 + '@babel/helper-validator-option': 7.12.17 '@babel/plugin-transform-react-display-name': 7.12.13_@babel+core@7.9.0 '@babel/plugin-transform-react-jsx': 7.13.12_@babel+core@7.9.0 '@babel/plugin-transform-react-jsx-development': 7.12.17_@babel+core@7.9.0 @@ -1721,7 +1719,7 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-TYM0V9z6Abb6dj1K7i5NrEhA13oS5ujUYQYDfqIBXYHOc2c2VkFgc+q9kyssIyUfy4/hEwqrgSlJ/Qgv8zJLsA== + integrity: sha512-gx+tDLIE06sRjKJkVtpZ/t3mzCDOnPG+ggHZG9lffUbX8+wC739x20YQc9V35Do6ZAxaUc/HhVHIiOzz5MvDmA== /@babel/preset-react/7.9.1_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -1748,7 +1746,7 @@ packages: integrity: sha512-S4cueFnGrIbvYJgwsVFKdvOmpiL0XGw9MFW9D0vgRys5g36PBhZRL8NX8Gr2akz8XRtzq6HuDXPD/1nniagNUg== /@babel/runtime-corejs3/7.13.10: dependencies: - core-js-pure: 3.9.1 + core-js-pure: 3.10.0 regenerator-runtime: 0.13.7 dev: false resolution: @@ -1768,33 +1766,32 @@ packages: /@babel/template/7.12.13: dependencies: '@babel/code-frame': 7.12.13 - '@babel/parser': 7.13.12 - '@babel/types': 7.13.12 + '@babel/parser': 7.13.13 + '@babel/types': 7.13.14 dev: false resolution: integrity: sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA== - /@babel/traverse/7.13.0: + /@babel/traverse/7.13.13: dependencies: '@babel/code-frame': 7.12.13 '@babel/generator': 7.13.9 '@babel/helper-function-name': 7.12.13 '@babel/helper-split-export-declaration': 7.12.13 - '@babel/parser': 7.13.12 - '@babel/types': 7.13.12 + '@babel/parser': 7.13.13 + '@babel/types': 7.13.14 debug: 4.3.1 globals: 11.12.0 - lodash: 4.17.21 dev: false resolution: - integrity: sha512-xys5xi5JEhzC3RzEmSGrs/b3pJW/o87SypZ+G/PhaE7uqVQNv/jlmVIBXuoh5atqQ434LfXV+sf23Oxj0bchJQ== - /@babel/types/7.13.12: + integrity: sha512-CblEcwmXKR6eP43oQGG++0QMTtCjAsa3frUuzHoiIJWpaIIi8dwMyEFUJoXRLxagGqCK+jALRwIO+o3R9p/uUg== + /@babel/types/7.13.14: dependencies: '@babel/helper-validator-identifier': 7.12.11 lodash: 4.17.21 to-fast-properties: 2.0.0 dev: false resolution: - integrity: sha512-K4nY2xFN4QMvQwkQ+zmBDp6ANMbVNw6BbxWmYA4qNjhR9W+Lj/8ky5MEY2Me5r+B2c6/v6F53oMndG+f9s3IiA== + integrity: sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ== /@bentley/icons-generic-webfont/1.0.34: dev: false resolution: @@ -1803,15 +1800,15 @@ packages: dev: false resolution: integrity: sha512-IIs1wDcY2oZ8tJ3EZRw0U51M+0ZL3MvwoDYYmhUXaa9/UZqpFoOyLBGaxjirQteWXqTIMm3mFvmC+Nbn1ok4Iw== - /@bentley/imodeljs-native/2.15.0: + /@bentley/imodeljs-native/2.15.1: dev: false requiresBuild: true resolution: - integrity: sha512-pVB0376m1oBnyavWENshDOZwwBF8b8EuqFaunxxIZJKKStmJBdGOmz4I3vuvWPmx9B3lL7NI9vCjBR+j+pIc/A== + integrity: sha512-FBUvs7giBvnsYKDdhSuPkGn9RGh/P/I9nB++GP2Tj87nBtGOFOP50XLY9nZEtwvE7/cP77mit/UB5dcVE5fxuQ== /@bentley/react-scripts/3.4.9_50a9a52775dfbe3fbd64ccf8e2a3fa32: dependencies: '@babel/core': 7.9.0 - '@bentley/webpack-tools-core': 2.13.2_webpack@4.42.0 + '@bentley/webpack-tools-core': 2.13.3_webpack@4.42.0 '@svgr/webpack': 4.3.3 '@typescript-eslint/eslint-plugin': 4.11.1_d0bf3c0366a0d1edd9673f55e63a15b1 '@typescript-eslint/parser': 4.11.1_eslint@6.8.0+typescript@4.1.5 @@ -1859,7 +1856,7 @@ packages: sass-loader: 8.0.2_sass@1.32.8+webpack@4.42.0 semver: 6.3.0 source-map-loader: 1.1.3_webpack@4.42.0 - speed-measure-webpack-plugin: 1.4.2_webpack@4.42.0 + speed-measure-webpack-plugin: 1.5.0_webpack@4.42.0 style-loader: 0.23.1 svg-sprite-loader: 4.2.1_webpack@4.42.0 terser-webpack-plugin: 3.0.7_webpack@4.42.0 @@ -1891,7 +1888,7 @@ packages: dev: false resolution: integrity: sha512-E59wOYMdBDVNDbZ3+FChznM+Twdo+ZHcCa7FRTgnatc7Z4N4yBpyXwh186COgOarBk2uoBtEskvaPqz5RhHIaA== - /@bentley/webpack-tools-core/2.13.2_webpack@4.42.0: + /@bentley/webpack-tools-core/2.13.3_webpack@4.42.0: dependencies: chalk: 3.0.0 file-loader: 4.3.0_webpack@4.42.0 @@ -1908,7 +1905,7 @@ packages: peerDependencies: webpack: 4.42.0 resolution: - integrity: sha512-F2lWJoP4Z/qdQsFDistS0P/gdqLRgH2xrawRQQS/2KeT/oMiTOd0XRjavvhU0hGEfTnNXRstsvin5SzaTYf2Tw== + integrity: sha512-rYegg4ilmUxp/qEImcIUO1FsztSRH8omk9ZojKXpLQXrfRaQ602DmG7t8LzayliuiMnQUgNXvP2j7Ce4d20OEw== /@cnakazawa/watch/1.0.4: dependencies: exec-sh: 0.3.6 @@ -2260,7 +2257,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.3 '@types/istanbul-reports': 3.0.0 - '@types/node': 12.20.6 + '@types/node': 10.14.1 '@types/yargs': 15.0.13 chalk: 4.1.0 dev: false @@ -2294,74 +2291,74 @@ packages: hasBin: true resolution: integrity: sha512-bjLnWrowqSFJFO/TO54E8wzoS1TogBJSfd+9DKEu4t8E7DvXgDPj+Tmez8d4f8Wk/kH4sIcBWFsqsLAJJOI5zw== - /@microsoft/applicationinsights-analytics-js/2.6.0: + /@microsoft/applicationinsights-analytics-js/2.6.1: dependencies: - '@microsoft/applicationinsights-common': 2.6.0 - '@microsoft/applicationinsights-core-js': 2.6.0 + '@microsoft/applicationinsights-common': 2.6.1 + '@microsoft/applicationinsights-core-js': 2.6.1 '@microsoft/applicationinsights-shims': 1.0.3 '@microsoft/dynamicproto-js': 1.1.1 dev: false resolution: - integrity: sha512-l7UnG4hiuQpzb5J6X3wijkS7LGhz8AenxBXQDkmdgWf5tjwWV3jZN4UXz9eEduC9MRldOKZ6OUb3uI2bvIqPVw== - /@microsoft/applicationinsights-channel-js/2.6.0: + integrity: sha512-wRH67jZTPy6SP54ygAQsFXu5ZnfzGOoMkA6ll1pkhSC4hij7Cvzumr4PYkr2gIPBPxD354sLOjBL/lmOgBTc5g== + /@microsoft/applicationinsights-channel-js/2.6.1: dependencies: - '@microsoft/applicationinsights-common': 2.6.0 - '@microsoft/applicationinsights-core-js': 2.6.0 + '@microsoft/applicationinsights-common': 2.6.1 + '@microsoft/applicationinsights-core-js': 2.6.1 '@microsoft/applicationinsights-shims': 1.0.3 '@microsoft/dynamicproto-js': 1.1.1 dev: false resolution: - integrity: sha512-APxSM6eilHq9TRsrEvqGvKfNFBW/C5EZim1XJlNI9cX22I6LrbrB70clcBwa+ssRQtPg6c+MRPVQBVe5O57+fQ== - /@microsoft/applicationinsights-common/2.6.0: + integrity: sha512-oatERKW3eAjVNm5ej2NpRvBCCtPtiINIKxLiWVHv2SC2rzGrUnxNWFlUJojRT+u5ZXXsB51Ktw9gx8iHexnDVw== + /@microsoft/applicationinsights-common/2.6.1: dependencies: - '@microsoft/applicationinsights-core-js': 2.6.0 + '@microsoft/applicationinsights-core-js': 2.6.1 '@microsoft/applicationinsights-shims': 1.0.3 '@microsoft/dynamicproto-js': 1.1.1 dev: false resolution: - integrity: sha512-AqsA9RZKEi0BbzhK3XBNSPx2x4JNRqvg149Jjf0o5UKI2zm3JCzSeXtquTQYD3iwSflqKAn0jLjr1ao+/DbIiA== - /@microsoft/applicationinsights-core-js/2.6.0: + integrity: sha512-kV/dI9UwTew3mq+3F3Tkl2dBn2C4+FOOG3S5cS7/SssVsUlvLrWXBrOOxfIJ7+EjQQ6ijcqlDus4Nz7Ms2Kk2Q== + /@microsoft/applicationinsights-core-js/2.6.1: dependencies: '@microsoft/applicationinsights-shims': 1.0.3 '@microsoft/dynamicproto-js': 1.1.1 dev: false resolution: - integrity: sha512-IBayTBEISR2hVkG6aNAYGDQrJzx2JguWEAWpcV00sLT+J4zAY80Pk+CClI35txB7ePk8pu1p8ebq2Z+l1GfJag== - /@microsoft/applicationinsights-dependencies-js/2.6.0: + integrity: sha512-SFnTx48BGkmz6P9GvXFeIZ641vnfrKo0hB74Hp9GR7UE3hqIDuomIBQS17unnh05T5w3PWKlDCGNpiCbJV6kzQ== + /@microsoft/applicationinsights-dependencies-js/2.6.1: dependencies: - '@microsoft/applicationinsights-common': 2.6.0 - '@microsoft/applicationinsights-core-js': 2.6.0 + '@microsoft/applicationinsights-common': 2.6.1 + '@microsoft/applicationinsights-core-js': 2.6.1 '@microsoft/applicationinsights-shims': 1.0.3 '@microsoft/dynamicproto-js': 1.1.1 dev: false resolution: - integrity: sha512-b6crj5dG/JXiiuzjSy6+NmpJFlnPQmvG/wxTO5fqzy9zh7GHva7P/KFaWq/ABTuX9aFyd22w1TY73PuiPQ+fRw== - /@microsoft/applicationinsights-properties-js/2.6.0: + integrity: sha512-Y7R6BjzyB6NrkIBTT6FJrr2jKr8MiLxy264b1xUQHjry3Bwu+fsPJ7EETV61dkOEHs6IlWxAtikNCv8f3zQydw== + /@microsoft/applicationinsights-properties-js/2.6.1: dependencies: - '@microsoft/applicationinsights-common': 2.6.0 - '@microsoft/applicationinsights-core-js': 2.6.0 + '@microsoft/applicationinsights-common': 2.6.1 + '@microsoft/applicationinsights-core-js': 2.6.1 '@microsoft/applicationinsights-shims': 1.0.3 '@microsoft/dynamicproto-js': 1.1.1 dev: false resolution: - integrity: sha512-LI1CjB6sMU3BVHvP5OcIgeijc89KaSVNCxO0drMWa6eBDBY7IMdh1qg0XC0SsumLIYuxYrkYgDb3o5webqNllA== + integrity: sha512-TQmKp9/j0yMGjUHBatRQ41E4S/1yTkJImh8csZxc4waMQmV9ITkNrDaC3bIPcrw+ASepI3QfP7sry+n7nvYLFw== /@microsoft/applicationinsights-shims/1.0.3: dev: false resolution: integrity: sha512-+S17aqEkOYpyBpmclhgwcEplwnxSo5AxYBdRg38GBobI1GKPSpZfnLssLzcjJ6XZCS5tqB5xjyTZs6gHj7ZJWQ== - /@microsoft/applicationinsights-web/2.6.0: - dependencies: - '@microsoft/applicationinsights-analytics-js': 2.6.0 - '@microsoft/applicationinsights-channel-js': 2.6.0 - '@microsoft/applicationinsights-common': 2.6.0 - '@microsoft/applicationinsights-core-js': 2.6.0 - '@microsoft/applicationinsights-dependencies-js': 2.6.0 - '@microsoft/applicationinsights-properties-js': 2.6.0 + /@microsoft/applicationinsights-web/2.6.1: + dependencies: + '@microsoft/applicationinsights-analytics-js': 2.6.1 + '@microsoft/applicationinsights-channel-js': 2.6.1 + '@microsoft/applicationinsights-common': 2.6.1 + '@microsoft/applicationinsights-core-js': 2.6.1 + '@microsoft/applicationinsights-dependencies-js': 2.6.1 + '@microsoft/applicationinsights-properties-js': 2.6.1 '@microsoft/applicationinsights-shims': 1.0.3 '@microsoft/dynamicproto-js': 1.1.1 dev: false resolution: - integrity: sha512-3TONRtWjAm2fwWmicTTWM8gPJmg81zKiYyfzp7aSF0HiT/0ljArDR8KJgHeiX6ZiwT49xVkUoBxxEXtUEvJVkQ== + integrity: sha512-jKF6hpPq3pzLqLSVxTMh7HU9tr/SPOFJN12pFYpNMU/rAMs/fgZsmFG/oBAzYTZxNilDTFljoblB66J9X+K6RQ== /@microsoft/dynamicproto-js/1.1.1: dependencies: findup-sync: 4.0.0 @@ -2586,7 +2583,7 @@ packages: integrity: sha512-qNuGF1QON1626UCaZamWt5yedpgOytvLj5BQZe2j1k1B8DUG4OyugZyfEwBeXozCUwhLEpsrgPrE+eCu4fY17w== /@svgr/hast-util-to-babel-ast/4.3.2: dependencies: - '@babel/types': 7.13.12 + '@babel/types': 7.13.14 dev: false engines: node: '>=8' @@ -2616,9 +2613,9 @@ packages: /@svgr/webpack/4.3.3: dependencies: '@babel/core': 7.9.0 - '@babel/plugin-transform-react-constant-elements': 7.13.10_@babel+core@7.9.0 + '@babel/plugin-transform-react-constant-elements': 7.13.13_@babel+core@7.9.0 '@babel/preset-env': 7.13.12_@babel+core@7.9.0 - '@babel/preset-react': 7.12.13_@babel+core@7.9.0 + '@babel/preset-react': 7.13.13_@babel+core@7.9.0 '@svgr/core': 4.3.3 '@svgr/plugin-jsx': 4.3.3 '@svgr/plugin-svgo': 4.3.1 @@ -2656,7 +2653,7 @@ packages: node: '>=8' resolution: integrity: sha512-Y1T2bjtvQMewffn1CJ28kpgnuvPYKsBcZMagEH0ppfEMZPDc8AkkEnTk4smrGZKw0cblNB3lhM2FMnpfLExlHg== - /@testing-library/dom/7.30.0: + /@testing-library/dom/7.30.3: dependencies: '@babel/code-frame': 7.12.13 '@babel/runtime': 7.13.10 @@ -2670,7 +2667,7 @@ packages: engines: node: '>=10' resolution: - integrity: sha512-v4GzWtltaiDE0yRikLlcLAfEiiK8+ptu6OuuIebm9GdC2XlZTNDPGEfM2UkEtnH7hr9TRq2sivT5EA9P1Oy7bw== + integrity: sha512-7JhIg2MW6WPwyikH2iL3o7z+FTVgSOd2jqCwTAHqK7Qal2gRRYiUQyURAxtbK9VXm/UTyG9bRihv8C5Tznr2zw== /@testing-library/react-hooks/3.7.0_98e0eb37a9f7280a1c5a6c886619f5b4: dependencies: '@babel/runtime': 7.13.10 @@ -2715,8 +2712,8 @@ packages: integrity: sha512-S6oPal772qJZHoRZLFc/XoZW2gFvwXusYUmXPXkgxJLuEk2vOt7jc4Yo6z/vtI0EBkbPBVrJJ0B+prLIKiWqHg== /@types/babel__core/7.1.14: dependencies: - '@babel/parser': 7.13.12 - '@babel/types': 7.13.12 + '@babel/parser': 7.13.13 + '@babel/types': 7.13.14 '@types/babel__generator': 7.6.2 '@types/babel__template': 7.4.0 '@types/babel__traverse': 7.11.1 @@ -2725,20 +2722,20 @@ packages: integrity: sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g== /@types/babel__generator/7.6.2: dependencies: - '@babel/types': 7.13.12 + '@babel/types': 7.13.14 dev: false resolution: integrity: sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ== /@types/babel__template/7.4.0: dependencies: - '@babel/parser': 7.13.12 - '@babel/types': 7.13.12 + '@babel/parser': 7.13.13 + '@babel/types': 7.13.14 dev: false resolution: integrity: sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A== /@types/babel__traverse/7.11.1: dependencies: - '@babel/types': 7.13.12 + '@babel/types': 7.13.14 dev: false resolution: integrity: sha512-Vs0hm0vPahPMYi9tDjtP66llufgO3ST16WXaSTtDGEl9cewAl3AibmxWw6TINOqHPT9z0uABKAYjT9jNSg4npw== @@ -2753,20 +2750,20 @@ packages: /@types/body-parser/1.19.0: dependencies: '@types/connect': 3.4.34 - '@types/node': 12.20.6 + '@types/node': 12.20.7 dev: false resolution: integrity: sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== /@types/bunyan-seq/0.2.2: dependencies: '@types/bunyan': 1.8.6 - '@types/node': 12.20.6 + '@types/node': 12.20.7 dev: false resolution: integrity: sha512-UXW9AI3WN7U2SRoVK112yG3L/JOXtLc40Bm1WiFk/nnxh5CHLcAXgwHupOPp7PHc2aWLE5ZAgLHfl4ULn4KeBw== /@types/bunyan/1.8.6: dependencies: - '@types/node': 12.20.6 + '@types/node': 12.20.7 dev: false resolution: integrity: sha512-YiozPOOsS6bIuz31ilYqR5SlLif4TBWsousN2aCWLi5233nZSX19tFbcQUPdR7xJ8ypPyxkCGNxg0CIV5n9qxQ== @@ -2840,7 +2837,7 @@ packages: integrity: sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog== /@types/cpx/1.5.1: dependencies: - '@types/node': 12.20.6 + '@types/node': 12.20.7 dev: false resolution: integrity: sha512-PVFRjClbYw22ckQR1OtjlQpg3ZqbSIIOq+/dbkqsIEYWmpZqL9OXLXTLUEhsYaST+1oNfx5c067aMqoXnAp8+w== @@ -2864,7 +2861,7 @@ packages: integrity: sha512-3JwbEeRVQ3n6+JgBW/hCdkydRk9/vWT+UEglcXEJqLJEcUganDH37zlfLznxPKTZZfDqA9K229l1qN458ubcOQ== /@types/dotenv/6.1.1: dependencies: - '@types/node': 12.20.6 + '@types/node': 12.20.7 dev: false resolution: integrity: sha512-ftQl3DtBvqHl9L16tpqqzA4YzCSXZfi7g8cQceTz5rOlYtk/IZbFjAv3mLOQlNIgOaylCQWQoBdDQHPgEBJPHg== @@ -2908,29 +2905,29 @@ packages: dev: false resolution: integrity: sha512-7btbphLrKvo5yl/5CC2OCxUSMx1wV1wvGT1qDXkSt7yi00/YW7E8k6qzXqJHsp+WU0eoG7r6MTQQXI9lIvd0qA== - /@types/formidable/1.2.0: + /@types/formidable/1.2.1: dependencies: - '@types/node': 12.20.6 + '@types/node': 12.20.7 dev: false resolution: - integrity: sha512-Gpj8Jc/M29THMBThhvprrY1eJe2+9E/LhyiLrzwgTT5X/FNkpqxmnH8KuX7SLSSOYnVWsxoZZEspXzc4+UR17Q== + integrity: sha512-4sVIjd51AADf4YnpsdqNQh7n3ZRLo16w+w6F7zzKjK6dBsQc9Vrq8GQLpHOOWq0V8Fe2bbp2EpihHx57iVgm5Q== /@types/fs-extra/4.0.11: dependencies: - '@types/node': 12.20.6 + '@types/node': 12.20.7 dev: false resolution: integrity: sha512-iHm/nBVmMR/VJQn/xCSwZ6C/qfm1Tgsh2IrCCinAsF+mlHDGpV+vLR/EA6xezBXpv0/amDOaqxa8f2TVxBQyog== /@types/glob/5.0.36: dependencies: '@types/events': 3.0.0 - '@types/minimatch': 3.0.3 - '@types/node': 12.20.6 + '@types/minimatch': 3.0.4 + '@types/node': 12.20.7 dev: false resolution: integrity: sha512-KEzSKuP2+3oOjYYjujue6Z3Yqis5HKA1BsIC+jZ1v3lrRNdsqyNNtX0rQf6LSuI4DJJ2z5UV//zBZCcvM0xikg== /@types/glob/7.1.3: dependencies: - '@types/minimatch': 3.0.3 + '@types/minimatch': 3.0.4 '@types/node': 10.14.1 dev: false resolution: @@ -3007,7 +3004,7 @@ packages: integrity: sha512-iGjZEnheu9n53fhlU+QLovdKHsdwPJ79iammNM0opTEp18zcJ/nNAIthuMilJoDPNUQHoqRAJdDNI5rxHY4nkA== /@types/jsdom/12.2.4: dependencies: - '@types/node': 12.20.6 + '@types/node': 12.20.7 '@types/tough-cookie': 4.0.0 parse5: 4.0.0 dev: false @@ -3027,7 +3024,7 @@ packages: integrity: sha512-sqm9g7mHlPY/43fcSNrCYfOeX9zkTTK+euO5E6+CVijSMm5tTjkVdwdqRkY3ljjIAf8679vps5jKUoJBCLsMDA== /@types/jsonwebtoken/8.5.1: dependencies: - '@types/node': 12.20.6 + '@types/node': 12.20.7 dev: false resolution: integrity: sha512-rNAPdomlIUX0i0cg2+I+Q1wOUr531zHBQ+cV/28PJ39bSPKjahatZZ2LMuhiguETkCgLVzfruw/ZvNMNkKoSzw== @@ -3065,6 +3062,10 @@ packages: dev: false resolution: integrity: sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== + /@types/minimatch/3.0.4: + dev: false + resolution: + integrity: sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA== /@types/minipass/2.2.0: dependencies: '@types/node': 10.14.1 @@ -3077,7 +3078,7 @@ packages: integrity: sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ== /@types/multiparty/0.0.31: dependencies: - '@types/node': 12.20.6 + '@types/node': 12.20.7 dev: false resolution: integrity: sha1-MB/S2wWl1PNAhhPrW5ZSzWELeeI= @@ -3088,21 +3089,14 @@ packages: dev: false resolution: integrity: sha512-jI/ewavBQ7X5178262JQR0ewicPAcJhXS/iFaNJl0VHLfyosZ/kwSrsa6VNQNSO8i9d8SqdRgOtZSOKJ/+iNMw== - /@types/node-fetch/2.5.8: - dependencies: - '@types/node': 10.14.1 - form-data: 3.0.1 - dev: false - resolution: - integrity: sha512-fbjI6ja0N5ZA8TV53RUqzsKNkl9fv8Oj3T7zxW7FGv1GSH7gwJaNF8dzCjrqKaxKeUpTz4yT1DaJFq/omNpGfw== /@types/node/10.14.1: dev: false resolution: integrity: sha512-Rymt08vh1GaW4vYB6QP61/5m/CFLGnFZP++bJpWbiNxceNa6RBipDmb413jvtSf/R1gg5a/jQVl2jY4XVRscEA== - /@types/node/12.20.6: + /@types/node/12.20.7: dev: false resolution: - integrity: sha512-sRVq8d+ApGslmkE9e3i+D3gFGk7aZHAT+G4cIpIEdLJYPsWiSPwcAnJEjddLQQDqV3Ra2jOclX/Sv6YrvGYiWA== + integrity: sha512-gWL8VUkg8VRaCAUgG9WmhefMqHmMblxe2rVpMF86nZY/+ZysU+BkAp+3cz03AixWDSSz0ks5WX59yAhv/cDwFA== /@types/node/8.10.54: dev: false resolution: @@ -3121,7 +3115,7 @@ packages: integrity: sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== /@types/puppeteer/2.0.1: dependencies: - '@types/node': 12.20.6 + '@types/node': 12.20.7 dev: false resolution: integrity: sha512-G8vEyU83Bios+dzs+DZGpAirDmMqRhfFBJCkFrg+A5+6n5EPPHxwBLImJto3qjh0mrBXbLBCyuahhhtTrAfR5g== @@ -3186,17 +3180,17 @@ packages: dependencies: '@types/history': 4.7.8 '@types/react': 16.9.43 - '@types/react-router': 5.1.12 + '@types/react-router': 5.1.13 dev: false resolution: integrity: sha512-D5mHD6TbdV/DNHYsnwBTv+y73ei+mMjrkGrla86HthE4/PVvL1J94Bu3qABU+COXzpL23T1EZapVVpwHuBXiUg== - /@types/react-router/5.1.12: + /@types/react-router/5.1.13: dependencies: '@types/history': 4.7.8 '@types/react': 16.9.43 dev: false resolution: - integrity: sha512-0bhXQwHYfMeJlCh7mGhc0VJTRm0Gk+Z8T00aiP4702mDUuLs9SMhnd2DitpjWFjdOecx2UXtICK14H9iMnziGA== + integrity: sha512-ZIuaO9Yrln54X6elg8q2Ivp6iK6p4syPsefEYAhRDAoqNh48C8VYUmB9RkXjKSQAJSJV0mbIFCX7I4vZDcHrjg== /@types/react-select/3.0.26: dependencies: '@types/react': 16.9.43 @@ -3267,7 +3261,7 @@ packages: /@types/rimraf/2.0.4: dependencies: '@types/glob': 7.1.3 - '@types/node': 12.20.6 + '@types/node': 12.20.7 dev: false resolution: integrity: sha512-8gBudvllD2A/c0CcEX/BivIDorHFt5UI5m46TsNj8DjWCCTTZT74kEe4g+QsY7P/B9WdO98d82zZgXO/RQzu2Q== @@ -3277,7 +3271,7 @@ packages: integrity: sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ== /@types/serve-handler/6.1.0: dependencies: - '@types/node': 12.20.6 + '@types/node': 12.20.7 dev: false resolution: integrity: sha512-F9BpWotLZrQvq1CdE3oCnVmQUb6dWq20HSB/qjF4mG7WkZjPQ6fye8veznMKoBPhFJve4yz9C1NOITdTcoLngQ== @@ -3319,13 +3313,13 @@ packages: integrity: sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== /@types/source-map-support/0.4.2: dependencies: - '@types/node': 12.20.6 + '@types/node': 12.20.7 dev: false resolution: integrity: sha512-GbGWx39O8NdUHSChdrU0XeigBAgu1Teg3llwE0slSVcH2qISaQT70ftAiH+h4HIt3VIObFU34PSpXIKJuXCybQ== /@types/spdy/3.4.4: dependencies: - '@types/node': 12.20.6 + '@types/node': 12.20.7 dev: false resolution: integrity: sha512-N9LBlbVRRYq6HgYpPkqQc3a9HJ/iEtVZToW6xlTtJiMhmRJ7jJdV7TaZQJw/Ve/1ePUsQiCTDc4JMuzzag94GA== @@ -3335,31 +3329,31 @@ packages: integrity: sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== /@types/stream-buffers/3.0.3: dependencies: - '@types/node': 12.20.6 + '@types/node': 12.20.7 dev: false resolution: integrity: sha512-NeFeX7YfFZDYsCfbuaOmFQ0OjSmHreKBpp7MQ4alWQBHeh2USLsj7qyMyn9t82kjqIX516CR/5SRHnARduRtbQ== /@types/superagent/4.1.10: dependencies: '@types/cookiejar': 2.1.2 - '@types/node': 12.20.6 + '@types/node': 12.20.7 dev: false resolution: integrity: sha512-xAgkb2CMWUMCyVc/3+7iQfOEBE75NvuZeezvmixbUw3nmENf2tCnQkW5yQLTYqvXUQ+R6EXxdqKKbal2zM5V/g== - /@types/supertest/2.0.10: + /@types/supertest/2.0.11: dependencies: '@types/superagent': 4.1.10 dev: false resolution: - integrity: sha512-Xt8TbEyZTnD5Xulw95GLMOkmjGICrOQyJ2jqgkSjAUR3mm7pAIzSR0NFBaMcwlzVvlpCjNwbATcWWwjNiZiFrQ== - /@types/tapable/1.0.6: + integrity: sha512-uci4Esokrw9qGb9bvhhSVEjd6rkny/dk5PK/Qz4yxKiyppEI+dOPlNrZBahE3i+PoKFYyDxChVXZ/ysS/nrm1Q== + /@types/tapable/1.0.7: dev: false resolution: - integrity: sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA== + integrity: sha512-0VBprVqfgFD7Ehb2vd8Lh9TG3jP98gvr8rgehQqzztZNI7o8zS8Ad4jyZneKELphpuE212D8J70LnSNQSyO6bQ== /@types/tar/4.0.4: dependencies: '@types/minipass': 2.2.0 - '@types/node': 12.20.6 + '@types/node': 12.20.7 dev: false resolution: integrity: sha512-0Xv+xcmkTsOZdIF4yCnd7RkOOyfyqPaqJ7RZFKnwdxfDbkN3eAAE9sHl8zJFqBz4VhxolW9EErbjR1oyH7jK2A== @@ -3377,12 +3371,6 @@ packages: dev: false resolution: integrity: sha512-I8MnZqNXsOLHsU111oHbn3khtvKMi5Bn4qVFsIWSJcCP1KKDiXX5AEw8UPk0nSopeC+Hvxt6yAy1/a5PailFqg== - /@types/tunnel/0.0.1: - dependencies: - '@types/node': 10.14.1 - dev: false - resolution: - integrity: sha512-AOqu6bQu5MSWwYvehMXLukFHnupHrpZ8nvgae5Ggie9UwzDR1CCwoXgSSWNZJuyOlCdfdsWMA5F2LlmvyoTv8A== /@types/uglify-js/3.13.0: dependencies: source-map: 0.6.1 @@ -3395,26 +3383,26 @@ packages: integrity: sha512-WGZCqBZZ0mXN2RxvLHL6/7RCu+OWs28jgQMP04LWfpyJlQUMTR6YU9CNJAKDgbw+EV/u687INXuLUc7FuML/4g== /@types/webpack-sources/0.1.8: dependencies: - '@types/node': 12.20.6 + '@types/node': 12.20.7 '@types/source-list-map': 0.1.2 source-map: 0.6.1 dev: false resolution: integrity: sha512-JHB2/xZlXOjzjBB6fMOpH1eQAfsrpqVVIbneE0Rok16WXwFaznaI5vfg75U5WgGJm7V9W1c4xeRQDjX/zwvghA== - /@types/webpack/4.41.26: + /@types/webpack/4.41.27: dependencies: '@types/anymatch': 1.3.1 - '@types/node': 12.20.6 - '@types/tapable': 1.0.6 + '@types/node': 12.20.7 + '@types/tapable': 1.0.7 '@types/uglify-js': 3.13.0 '@types/webpack-sources': 0.1.8 source-map: 0.6.1 dev: false resolution: - integrity: sha512-7ZyTfxjCRwexh+EJFwRUM+CDB2XvgHl4vfuqf1ZKrgGvcS5BrNvPQqJh3tsZ0P6h6Aa1qClVHaJZszLPzpqHeA== + integrity: sha512-wK/oi5gcHi72VMTbOaQ70VcDxSQ1uX8S2tukBK9ARuGXrYM/+u4ou73roc7trXDNmCxCoerE8zruQqX/wuHszA== /@types/ws/6.0.4: dependencies: - '@types/node': 12.20.6 + '@types/node': 12.20.7 dev: false resolution: integrity: sha512-PpPrX7SZW9re6+Ha8ojZG4Se8AZXgf0GK6zmfqEuCsY49LFDNXO3SByp44X3dFEqtB73lkCDAdUazhAjVPiNwg== @@ -3449,12 +3437,13 @@ packages: optional: true resolution: integrity: sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA== - /@typescript-eslint/eslint-plugin/4.11.1_2c8775c659f30a6f114aeb366fcb50dc: + /@typescript-eslint/eslint-plugin/4.11.1_092af2b0a1e663b3f2e5c1684c00c49e: dependencies: - '@typescript-eslint/experimental-utils': 4.11.1_typescript@4.1.5 - '@typescript-eslint/parser': 4.11.1_typescript@4.1.5 + '@typescript-eslint/experimental-utils': 4.11.1_eslint@7.23.0+typescript@4.1.5 + '@typescript-eslint/parser': 4.11.1_eslint@7.23.0+typescript@4.1.5 '@typescript-eslint/scope-manager': 4.11.1 debug: 4.3.1 + eslint: 7.23.0 functional-red-black-tree: 1.0.1 regexpp: 3.1.0 semver: 7.3.5 @@ -3472,13 +3461,12 @@ packages: optional: true resolution: integrity: sha512-fABclAX2QIEDmTMk6Yd7Muv1CzFLwWM4505nETzRHpP3br6jfahD9UUJkhnJ/g2m7lwfz8IlswcwGGPGiq9exw== - /@typescript-eslint/eslint-plugin/4.11.1_96d27755fc43363abf6361149046cfaa: + /@typescript-eslint/eslint-plugin/4.11.1_2c8775c659f30a6f114aeb366fcb50dc: dependencies: - '@typescript-eslint/experimental-utils': 4.11.1_eslint@7.22.0+typescript@4.1.5 - '@typescript-eslint/parser': 4.11.1_eslint@7.22.0+typescript@4.1.5 + '@typescript-eslint/experimental-utils': 4.11.1_typescript@4.1.5 + '@typescript-eslint/parser': 4.11.1_typescript@4.1.5 '@typescript-eslint/scope-manager': 4.11.1 debug: 4.3.1 - eslint: 7.22.0 functional-red-black-tree: 1.0.1 regexpp: 3.1.0 semver: 7.3.5 @@ -3520,12 +3508,12 @@ packages: optional: true resolution: integrity: sha512-fABclAX2QIEDmTMk6Yd7Muv1CzFLwWM4505nETzRHpP3br6jfahD9UUJkhnJ/g2m7lwfz8IlswcwGGPGiq9exw== - /@typescript-eslint/experimental-utils/3.10.1_eslint@7.22.0+typescript@4.1.5: + /@typescript-eslint/experimental-utils/3.10.1_eslint@7.23.0+typescript@4.1.5: dependencies: '@types/json-schema': 7.0.7 '@typescript-eslint/types': 3.10.1 '@typescript-eslint/typescript-estree': 3.10.1_typescript@4.1.5 - eslint: 7.22.0 + eslint: 7.23.0 eslint-scope: 5.1.1 eslint-utils: 2.1.0 dev: false @@ -3569,13 +3557,13 @@ packages: typescript: '*' resolution: integrity: sha512-mAlWowT4A6h0TC9F+J5pdbEhjNiEMO+kqPKQ4sc3fVieKL71dEqfkKgtcFVSX3cjSBwYwhImaQ/mXQF0oaI38g== - /@typescript-eslint/experimental-utils/4.11.1_eslint@7.22.0+typescript@4.1.5: + /@typescript-eslint/experimental-utils/4.11.1_eslint@7.23.0+typescript@4.1.5: dependencies: '@types/json-schema': 7.0.7 '@typescript-eslint/scope-manager': 4.11.1 '@typescript-eslint/types': 4.11.1 '@typescript-eslint/typescript-estree': 4.11.1_typescript@4.1.5 - eslint: 7.22.0 + eslint: 7.23.0 eslint-scope: 5.1.1 eslint-utils: 2.1.0 typescript: 4.1.5 @@ -3623,13 +3611,13 @@ packages: optional: true resolution: integrity: sha512-BJ3jwPQu1jeynJ5BrjLuGfK/UJu6uwHxJ/di7sanqmUmxzmyIcd3vz58PMR7wpi8k3iWq2Q11KMYgZbUpRoIPw== - /@typescript-eslint/parser/4.11.1_eslint@7.22.0+typescript@4.1.5: + /@typescript-eslint/parser/4.11.1_eslint@7.23.0+typescript@4.1.5: dependencies: '@typescript-eslint/scope-manager': 4.11.1 '@typescript-eslint/types': 4.11.1 '@typescript-eslint/typescript-estree': 4.11.1_typescript@4.1.5 debug: 4.3.1 - eslint: 7.22.0 + eslint: 7.23.0 typescript: 4.1.5 dev: false engines: @@ -3956,7 +3944,7 @@ packages: request: 2.88.2 underscore: 1.12.1 uuid: 3.4.0 - xmldom: 0.1.31 + xmldom: 0.5.0 xpath.js: 1.1.0 dev: false engines: @@ -4052,7 +4040,7 @@ packages: dev: false resolution: integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - /ajv/7.2.3: + /ajv/8.0.2: dependencies: fast-deep-equal: 3.1.3 json-schema-traverse: 1.0.0 @@ -4060,7 +4048,7 @@ packages: uri-js: 4.4.1 dev: false resolution: - integrity: sha512-idv5WZvKVXDqKralOImQgPM9v6WOdLNa0IY3B3doOjw/YxRGT8I+allIJ6kd7Uaj+SF1xZUSU+nPM5aDNBVtnw== + integrity: sha512-V0HGxJd0PiDF0ecHYIesTOqfd1gJguwQUOYfMfAWnRsWQEXfc5ifbUFhD3Wjc+O+y7VAqL+g07prq9gHQ/JOZQ== /almost-equal/1.1.0: dev: false resolution: @@ -4465,7 +4453,7 @@ packages: /autoprefixer/8.6.5: dependencies: browserslist: 3.2.8 - caniuse-lite: 1.0.30001204 + caniuse-lite: 1.0.30001205 normalize-range: 0.1.2 num2fraction: 1.2.2 postcss: 6.0.23 @@ -4477,7 +4465,7 @@ packages: /autoprefixer/9.8.6: dependencies: browserslist: 4.16.3 - caniuse-lite: 1.0.30001204 + caniuse-lite: 1.0.30001205 colorette: 1.2.2 normalize-range: 0.1.2 num2fraction: 1.2.2 @@ -4552,9 +4540,9 @@ packages: /babel-eslint/10.1.0_eslint@6.8.0: dependencies: '@babel/code-frame': 7.12.13 - '@babel/parser': 7.13.12 - '@babel/traverse': 7.13.0 - '@babel/types': 7.13.12 + '@babel/parser': 7.13.13 + '@babel/traverse': 7.13.13 + '@babel/types': 7.13.14 eslint: 6.8.0 eslint-visitor-keys: 1.3.0 resolve: 1.19.0 @@ -4727,7 +4715,7 @@ packages: dependencies: '@babel/core': 7.9.0 '@babel/helper-define-polyfill-provider': 0.1.5_@babel+core@7.9.0 - core-js-compat: 3.9.1 + core-js-compat: 3.10.0 dev: false peerDependencies: '@babel/core': ^7.0.0-0 @@ -4870,10 +4858,6 @@ packages: node: '>=0.10.0' resolution: integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== - /base64-js/1.3.0: - dev: false - resolution: - integrity: sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw== /base64-js/1.5.1: dev: false resolution: @@ -5002,11 +4986,11 @@ packages: dev: false resolution: integrity: sha1-aN/1++YMUes3cl6p4+0xDcwed24= - /boolean/3.0.2: + /boolean/3.0.3: dev: false optional: true resolution: - integrity: sha512-RwywHlpCRc3/Wh81MiCKun4ydaIFyW5Ea6JbL6sRCVx5q5irDw7pMXBUFYF/jArQ6YrG36q0kpovc9P/Kd3I4g== + integrity: sha512-EqrTKXQX6Z3A2nRmMEIlAIfjQOgFnVO2nqZGpbcsPnYGWBwpFqzlrozU1dy+S2iqfYDLh26ef4KrgTxu9xQrxA== /boxen/4.2.0: dependencies: ansi-align: 3.0.0 @@ -5139,18 +5123,16 @@ packages: integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== /browserslist/3.2.8: dependencies: - caniuse-lite: 1.0.30001204 - electron-to-chromium: 1.3.699 + caniuse-lite: 1.0.30001205 + electron-to-chromium: 1.3.703 dev: false hasBin: true resolution: integrity: sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ== - caniuse-lite: 1.0.30001197 - electron-to-chromium: 1.3.683 /browserslist/4.11.1: dependencies: - caniuse-lite: 1.0.30001204 - electron-to-chromium: 1.3.699 + caniuse-lite: 1.0.30001205 + electron-to-chromium: 1.3.703 node-releases: 1.1.71 pkg-up: 2.0.0 dev: false @@ -5159,8 +5141,8 @@ packages: integrity: sha512-DCTr3kDrKEYNw6Jb9HFxVLQNaue8z+0ZfRBRjmCunKDEXEBajKDj2Y+Uelg+Pi29OnvaSGwjOsnRyNEkXzHg5g== /browserslist/4.14.2: dependencies: - caniuse-lite: 1.0.30001204 - electron-to-chromium: 1.3.699 + caniuse-lite: 1.0.30001205 + electron-to-chromium: 1.3.703 escalade: 3.1.1 node-releases: 1.1.71 dev: false @@ -5171,9 +5153,9 @@ packages: integrity: sha512-HI4lPveGKUR0x2StIz+2FXfDk9SfVMrxn6PLh1JeGUwcuoDkdKZebWiyLRJ68iIPDpMI4JLVDf7S7XzslgWOhw== /browserslist/4.16.3: dependencies: - caniuse-lite: 1.0.30001204 + caniuse-lite: 1.0.30001205 colorette: 1.2.2 - electron-to-chromium: 1.3.699 + electron-to-chromium: 1.3.703 escalade: 3.1.1 node-releases: 1.1.71 dev: false @@ -5458,16 +5440,16 @@ packages: /caniuse-api/3.0.0: dependencies: browserslist: 4.16.3 - caniuse-lite: 1.0.30001204 + caniuse-lite: 1.0.30001205 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 dev: false resolution: integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== - /caniuse-lite/1.0.30001204: + /caniuse-lite/1.0.30001205: dev: false resolution: - integrity: sha512-JUdjWpcxfJ9IPamy2f5JaRDCaqJOxDzOSKtbdx4rH9VivMd1vIzoPumsJa9LoMIi4Fx2BV2KZOxWhNkBjaYivQ== + integrity: sha512-TL1GrS5V6LElbitPazidkBMD9sa448bQDDLrumDqaggmKFcuU2JW1wTOHJPukAcOMtEmLcmDJEzfRrf+GjM0Og== /capture-exit/2.0.0: dependencies: rsvp: 4.8.5 @@ -5704,7 +5686,7 @@ packages: integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== /chrome-launcher/0.10.7: dependencies: - '@types/node': 12.20.6 + '@types/node': 12.20.7 is-wsl: 1.1.0 lighthouse-logger: 1.2.0 mkdirp: 0.5.1 @@ -6217,29 +6199,29 @@ packages: node: '>=0.10.0' resolution: integrity: sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= - /core-js-compat/3.9.1: + /core-js-compat/3.10.0: dependencies: browserslist: 4.16.3 semver: 7.0.0 dev: false resolution: - integrity: sha512-jXAirMQxrkbiiLsCx9bQPJFA6llDadKMpYrBJQJ3/c4/vsPP/fAf29h24tviRlvwUL6AmY5CHLu2GvjuYviQqA== - /core-js-pure/3.9.1: + integrity: sha512-9yVewub2MXNYyGvuLnMHcN1k9RkvB7/ofktpeKTIaASyB88YYqGzUnu0ywMMhJrDHOMiTjSHWGzR+i7Wb9Z1kQ== + /core-js-pure/3.10.0: dev: false requiresBuild: true resolution: - integrity: sha512-laz3Zx0avrw9a4QEIdmIblnVuJz8W51leY9iLThatCsFawWxC3sE4guASC78JbCin+DkwMpCdp1AVAuzL/GN7A== + integrity: sha512-CC582enhrFZStO4F8lGI7QL3SYx7/AIRc+IdSi3btrQGrVsTawo5K/crmKbRrQ+MOMhNX4v+PATn0k2NN6wI7A== /core-js/2.6.12: deprecated: 'core-js@<3 is no longer maintained and not recommended for usage due to the number of issues. Please, upgrade your dependencies to the actual version of core-js@3.' dev: false requiresBuild: true resolution: integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== - /core-js/3.9.1: + /core-js/3.10.0: dev: false requiresBuild: true resolution: - integrity: sha512-gSjRvzkxQc1zjM/5paAmL4idJBFzuJoo+jDjF1tStYFMV2ERfD02HhahhCGXUyHxQRG4yFKVSdO6g62eoRMcDg== + integrity: sha512-MQx/7TLgmmDVamSyfE+O+5BHvG1aUGj/gHhLn1wVtm2B5u1eVIPvh7vkfjwWKNCjrTJB8+He99IntSQ1qP+vYQ== /core-util-is/1.0.2: dev: false resolution: @@ -6505,7 +6487,7 @@ packages: node: '>=8.0.0' resolution: integrity: sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== - /css-tree/1.1.2: + /css-tree/1.1.3: dependencies: mdn-data: 2.0.14 source-map: 0.6.1 @@ -6513,7 +6495,7 @@ packages: engines: node: '>=8.0.0' resolution: - integrity: sha512-wCoWush5Aeo48GLhfHPbmvZs59Z+M7k5+B1xDnXbdWNcEF423DoFdqSWE0PM5aNk5nI5cp1q7ms36zGApY/sKQ== + integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== /css-what/3.4.2: dev: false engines: @@ -6629,7 +6611,7 @@ packages: integrity: sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ== /csso/4.2.0: dependencies: - css-tree: 1.1.2 + css-tree: 1.1.3 dev: false engines: node: '>=8.0.0' @@ -7263,14 +7245,14 @@ packages: dev: false resolution: integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= - /electron-to-chromium/1.3.699: + /electron-to-chromium/1.3.703: dev: false resolution: - integrity: sha512-fjt43CPXdPYwD9ybmKbNeLwZBmCVdLY2J5fGZub7/eMPuiqQznOGNXv/wurnpXIlE7ScHnvG9Zi+H4/i6uMKmw== + integrity: sha512-SVBVhNB+4zPL+rvtWLw7PZQkw/Eqj1HQZs22xtcqW36+xoifzEOEEDEpkxSMfB6RFeSIOcG00w6z5mSqLr1Y6w== /electron/11.4.1: dependencies: '@electron/get': 1.12.4 - '@types/node': 12.20.6 + '@types/node': 12.20.7 extract-zip: 1.7.0 dev: false engines: @@ -7486,7 +7468,7 @@ packages: object.assign: 4.1.2 string.prototype.trimend: 1.0.4 string.prototype.trimstart: 1.0.4 - unbox-primitive: 1.0.0 + unbox-primitive: 1.0.1 dev: false engines: node: '>= 0.4' @@ -7614,11 +7596,11 @@ packages: dev: false resolution: integrity: sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA== - /eslint-import-resolver-typescript/2.0.0_e8c3ce6c9a72ba40b09aaf06432c8600: + /eslint-import-resolver-typescript/2.0.0_dc50b4c80c8cf29c4f2da372abb90f3d: dependencies: debug: 4.3.1 - eslint: 7.22.0 - eslint-plugin-import: 2.20.1_eslint@7.22.0 + eslint: 7.23.0 + eslint-plugin-import: 2.20.1_eslint@7.23.0 is-glob: 4.0.1 resolve: 1.19.0 tiny-glob: 0.2.8 @@ -7674,10 +7656,10 @@ packages: node: '>=4' resolution: integrity: sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA== - /eslint-plugin-deprecation/1.1.0_eslint@7.22.0+typescript@4.1.5: + /eslint-plugin-deprecation/1.1.0_eslint@7.23.0+typescript@4.1.5: dependencies: - '@typescript-eslint/experimental-utils': 3.10.1_eslint@7.22.0+typescript@4.1.5 - eslint: 7.22.0 + '@typescript-eslint/experimental-utils': 3.10.1_eslint@7.23.0+typescript@4.1.5 + eslint: 7.23.0 tslib: 1.14.1 tsutils: 3.17.1_typescript@4.1.5 typescript: 4.1.5 @@ -7753,14 +7735,14 @@ packages: eslint: 2.x - 6.x resolution: integrity: sha512-qQHgFOTjguR+LnYRoToeZWT62XM55MBVXObHM6SKFd1VzDcX/vqT1kAz8ssqigh5eMj8qXcRoXXGZpPP6RfdCw== - /eslint-plugin-import/2.20.1_eslint@7.22.0: + /eslint-plugin-import/2.20.1_eslint@7.23.0: dependencies: array-includes: 3.1.3 array.prototype.flat: 1.2.4 contains-path: 0.1.0 debug: 2.6.9 doctrine: 1.5.0 - eslint: 7.22.0 + eslint: 7.23.0 eslint-import-resolver-node: 0.3.4 eslint-module-utils: 2.6.0 has: 1.0.3 @@ -7801,11 +7783,11 @@ packages: eslint: ^6.0.0 || ^7.0.0 resolution: integrity: sha512-LlRdsSQBSPsI3MvhWoGc+Ev3PfFRBk41wwkmbOgC7KP7WQlbeWPpASF5Vdv17XEZ7J+xvPB3KCMyR//6Dbjnnw== - /eslint-plugin-jsdoc/30.6.2_eslint@7.22.0: + /eslint-plugin-jsdoc/30.6.2_eslint@7.23.0: dependencies: comment-parser: 0.7.6 debug: 4.3.1 - eslint: 7.22.0 + eslint: 7.23.0 jsdoctypeparser: 9.0.0 lodash: 4.17.21 regextras: 0.7.1 @@ -7857,7 +7839,7 @@ packages: eslint: ^3 || ^4 || ^5 || ^6 || ^7 resolution: integrity: sha512-0rGPJBbwHoGNPU73/QCLP/vveMlM1b1Z9PponxO87jfr6tuH5ligXbDT6nHSSzBC8ovX2Z+BQu7Bk5D/Xgq9zg== - /eslint-plugin-jsx-a11y/6.4.1_eslint@7.22.0: + /eslint-plugin-jsx-a11y/6.4.1_eslint@7.23.0: dependencies: '@babel/runtime': 7.13.10 aria-query: 4.2.2 @@ -7867,7 +7849,7 @@ packages: axobject-query: 2.2.0 damerau-levenshtein: 1.0.6 emoji-regex: 9.2.2 - eslint: 7.22.0 + eslint: 7.23.0 has: 1.0.3 jsx-ast-utils: 3.2.0 language-tags: 1.0.5 @@ -7884,9 +7866,9 @@ packages: eslint: '>=2.0.0' resolution: integrity: sha512-epsA4g804mRovlOHSbeO1xxW7REGeUjULRME9MJTJDOVscNIA01AkR66TP4cmHDfD+w72EQ9cPhf37MbZiFI2w== - /eslint-plugin-prefer-arrow/1.1.7_eslint@7.22.0: + /eslint-plugin-prefer-arrow/1.1.7_eslint@7.23.0: dependencies: - eslint: 7.22.0 + eslint: 7.23.0 dev: false peerDependencies: eslint: '>=2.0.0' @@ -7910,9 +7892,9 @@ packages: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 resolution: integrity: sha512-EjxTHxjLKIBWFgDJdhKKzLh5q+vjTFrqNZX36uIxWS4OfyXe5DawqPj3U5qeJ1ngLwatjzQnmR0Lz0J0YH3kxw== - /eslint-plugin-react-hooks/3.0.0_eslint@7.22.0: + /eslint-plugin-react-hooks/3.0.0_eslint@7.23.0: dependencies: - eslint: 7.22.0 + eslint: 7.23.0 dev: false engines: node: '>=7' @@ -7963,11 +7945,11 @@ packages: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 resolution: integrity: sha512-SPT8j72CGuAP+JFbT0sJHOB80TX/pu44gQ4vXH/cq+hQTiY2PuZ6IHkqXJV6x1b28GDdo1lbInjKUrrdUf0LOQ== - /eslint-plugin-react/7.19.0_eslint@7.22.0: + /eslint-plugin-react/7.19.0_eslint@7.23.0: dependencies: array-includes: 3.1.3 doctrine: 2.1.0 - eslint: 7.22.0 + eslint: 7.23.0 has: 1.0.3 jsx-ast-utils: 2.4.1 object.entries: 1.1.3 @@ -8076,7 +8058,7 @@ packages: hasBin: true resolution: integrity: sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== - /eslint/7.22.0: + /eslint/7.23.0: dependencies: '@babel/code-frame': 7.12.11 '@eslint/eslintrc': 0.4.0 @@ -8112,7 +8094,7 @@ packages: semver: 7.3.5 strip-ansi: 6.0.0 strip-json-comments: 3.1.1 - table: 6.0.7 + table: 6.0.9 text-table: 0.2.0 v8-compile-cache: 2.3.0 dev: false @@ -8120,7 +8102,7 @@ packages: node: ^10.12.0 || >=12.0.0 hasBin: true resolution: - integrity: sha512-3VawOtjSJUQiiqac8MQc+w457iGLfuNGLFn8JmF051tTKbh5/x/0vlcEj8OgDCaw7Ysa2Jn8paGshV7x2abKXg== + integrity: sha512-kqvNVbdkjzpFy0XOszNwjkKzZ+6TcwCQ/h+ozlcIWwaimBBuhlQ4nN6kbiM2L+OjDcznkTJxzYfRFH92sx4a0Q== /espree/6.2.1: dependencies: acorn: 7.4.1 @@ -8473,10 +8455,6 @@ packages: dev: false resolution: integrity: sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ= - /fast-deep-equal/2.0.1: - dev: false - resolution: - integrity: sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= /fast-deep-equal/3.1.3: dev: false resolution: @@ -8896,7 +8874,7 @@ packages: integrity: sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= /fork-ts-checker-webpack-plugin/4.1.6: dependencies: - '@babel/code-frame': 7.12.13 + '@babel/code-frame': 7.10.4 chalk: 2.4.2 micromatch: 3.1.10 minimatch: 3.0.4 @@ -9242,8 +9220,8 @@ packages: integrity: sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY= /global-agent/2.1.12: dependencies: - boolean: 3.0.2 - core-js: 3.9.1 + boolean: 3.0.3 + core-js: 3.10.0 es6-error: 4.1.1 matcher: 3.0.0 roarr: 2.15.4 @@ -9472,7 +9450,7 @@ packages: node: '>=0.4.7' hasBin: true optionalDependencies: - uglify-js: 3.13.2 + uglify-js: 3.13.3 resolution: integrity: sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== /har-schema/2.0.0: @@ -10046,6 +10024,10 @@ packages: dev: false resolution: integrity: sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA== + /immer/9.0.1: + dev: false + resolution: + integrity: sha512-7CCw1DSgr8kKYXTYOI1qMM/f5qxT5vIVMeGLDCDX8CSxsggr1Sjdoha4OhsP0AZ1UvWbyZlILHvLjaynuu02Mg== /immutable/3.8.2: dev: false engines: @@ -10155,7 +10137,6 @@ packages: dev: false resolution: integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - ansi-escapes: 4.3.1 /inquirer/7.3.3: dependencies: ansi-escapes: 4.3.2 @@ -10167,7 +10148,7 @@ packages: lodash: 4.17.21 mute-stream: 0.0.8 run-async: 2.4.1 - rxjs: 6.6.6 + rxjs: 6.6.7 string-width: 4.2.2 strip-ansi: 6.0.0 through: 2.3.8 @@ -10811,10 +10792,10 @@ packages: /istanbul-lib-instrument/3.3.0: dependencies: '@babel/generator': 7.13.9 - '@babel/parser': 7.13.12 + '@babel/parser': 7.13.13 '@babel/template': 7.12.13 - '@babel/traverse': 7.13.0 - '@babel/types': 7.13.12 + '@babel/traverse': 7.13.13 + '@babel/types': 7.13.14 istanbul-lib-coverage: 2.0.5 semver: 6.3.0 dev: false @@ -11015,7 +10996,7 @@ packages: integrity: sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ== /jest-jasmine2/24.9.0: dependencies: - '@babel/traverse': 7.13.0 + '@babel/traverse': 7.13.13 '@jest/environment': 24.9.0 '@jest/test-result': 24.9.0 '@jest/types': 24.9.0 @@ -11206,7 +11187,7 @@ packages: integrity: sha512-bpaeBnDpdqaRTzN8tWg0DqOTo2DvD3StOemxn67CUd1p1Po+BUpvePAp44jdJ7Pxcjfg+42o4NHw1SxdCA2rvg== /jest-snapshot/24.9.0: dependencies: - '@babel/types': 7.13.12 + '@babel/types': 7.13.14 '@jest/types': 24.9.0 chalk: 2.4.2 expect: 24.9.0 @@ -11759,23 +11740,6 @@ packages: node: '>=0.10.0' resolution: integrity: sha1-odePw6UEdMuAhF07O24dpJpEbo4= - /ldclient-js-common/2.10.2: - dependencies: - base64-js: 1.3.0 - fast-deep-equal: 2.0.1 - uuid: 3.3.2 - deprecated: The new name for this package is launchdarkly-js-sdk-common - dev: false - resolution: - integrity: sha512-V6o41jHrBZXl4x9CKl1SvyFUWOg9dlip68jVuzkXrsVjbxcaDqM+L3znPqkDmEmbjIX9eGPtJ4m++5HEjHZYpw== - /ldclient-js/2.10.2: - dependencies: - escape-string-regexp: 1.0.5 - ldclient-js-common: 2.10.2 - deprecated: The new name for this package is launchdarkly-js-client-sdk - dev: false - resolution: - integrity: sha512-6SpSbp91jBw+ZRKUPg2E1euegFLpwhD+wvnJhZ9NyXQH6F9jMeTEkB8mB1DlMQOUyU7NF1FNIrmF9y20rKfPXA== /left-pad/1.3.0: deprecated: use String.prototype.padStart() dev: false @@ -11846,7 +11810,7 @@ packages: dedent: 0.7.0 enquirer: 2.3.6 execa: 4.1.0 - listr2: 3.4.3_enquirer@2.3.6 + listr2: 3.4.4_enquirer@2.3.6 log-symbols: 4.1.0 micromatch: 4.0.2 normalize-path: 3.0.0 @@ -11857,7 +11821,7 @@ packages: hasBin: true resolution: integrity: sha512-EechC3DdFic/TdOPgj/RB3FicqE6932LTHCUm0Y2fsD9KGlLB+RwJl2q1IYBIvEsKzDOgn0D4gll+YxG5RsrKg== - /listr2/3.4.3_enquirer@2.3.6: + /listr2/3.4.4_enquirer@2.3.6: dependencies: chalk: 4.1.0 cli-truncate: 2.1.0 @@ -11866,7 +11830,7 @@ packages: indent-string: 4.0.0 log-update: 4.0.0 p-map: 4.0.0 - rxjs: 6.6.6 + rxjs: 6.6.7 through: 2.3.8 wrap-ansi: 7.0.0 dev: false @@ -11875,7 +11839,7 @@ packages: peerDependencies: enquirer: '>= 2.3.0 < 3' resolution: - integrity: sha512-wZmkzNiuinOfwrGqAwTCcPw6aKQGTAMGXwG5xeU1WpDjJNeBA35jGBeWxR3OF+R6Yl5Y3dRG+3vE8t6PDcSNHA== + integrity: sha512-okQwtieCHDX5erlxcEn2xwNfVQhV+/YiD+U0v+6DQamXnknE7mc6B5fopHl96v1PuZjTpaoChctf96PB6K4XRg== /load-json-file/2.0.0: dependencies: graceful-fs: 4.2.6 @@ -11988,6 +11952,10 @@ packages: dev: false resolution: integrity: sha1-soqmKIorn8ZRA1x3EfZathkDMaY= + /lodash.clonedeep/4.5.0: + dev: false + resolution: + integrity: sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= /lodash.debounce/4.0.8: dev: false resolution: @@ -11996,6 +11964,10 @@ packages: dev: false resolution: integrity: sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg= + /lodash.flatten/4.4.0: + dev: false + resolution: + integrity: sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= /lodash.flattendeep/4.4.0: dev: false resolution: @@ -12057,6 +12029,10 @@ packages: dev: false resolution: integrity: sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== + /lodash.truncate/4.4.2: + dev: false + resolution: + integrity: sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= /lodash.uniq/4.5.0: dev: false resolution: @@ -13411,7 +13387,7 @@ packages: dependencies: acorn: 7.4.1 base64-js: 1.5.1 - core-js: 3.9.1 + core-js: 3.10.0 crypto-js: 4.0.0 serialize-javascript: 4.0.0 dev: false @@ -14661,7 +14637,7 @@ packages: dependencies: autoprefixer: 9.8.6 browserslist: 4.16.3 - caniuse-lite: 1.0.30001204 + caniuse-lite: 1.0.30001205 css-blank-pseudo: 0.1.4 css-has-pseudo: 0.10.0 css-prefers-color-scheme: 3.1.1 @@ -15313,7 +15289,7 @@ packages: integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== /react-app-polyfill/1.0.6: dependencies: - core-js: 3.9.1 + core-js: 3.10.0 object-assign: 4.1.1 promise: 8.1.0 raf: 3.4.1 @@ -15584,7 +15560,7 @@ packages: integrity: sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw== /react-select-event/5.0.0: dependencies: - '@testing-library/dom': 7.30.0 + '@testing-library/dom': 7.30.3 dev: false resolution: integrity: sha512-bESECffhi//x1nlMoRJtwI0nGl5n6OKaVYeIEcPTV8flVPycvUoBGank/1RIoxVc6WtoQ4QbPbU8xMvX0xAiOA== @@ -16323,7 +16299,7 @@ packages: integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== /roarr/2.15.4: dependencies: - boolean: 3.0.2 + boolean: 3.0.3 detect-node: 2.0.5 globalthis: 1.0.2 json-stringify-safe: 5.0.1 @@ -16370,14 +16346,14 @@ packages: dev: false resolution: integrity: sha1-FPlQpCF9fjXapxu8vljv9o6ksrc= - /rxjs/6.6.6: + /rxjs/6.6.7: dependencies: tslib: 1.14.1 dev: false engines: npm: '>=2.0.0' resolution: - integrity: sha512-/oTwee4N4iWzAMAL9xdGKjkEHmIwupR3oXbQjCKywF1BeFohswF3vZdogbmEF6pZkOsXTzWkrZszrWpQTByYVg== + integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== /safe-buffer/5.1.2: dev: false resolution: @@ -17153,7 +17129,7 @@ packages: supports-color: '*' resolution: integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== - /speed-measure-webpack-plugin/1.4.2_webpack@4.42.0: + /speed-measure-webpack-plugin/1.5.0_webpack@4.42.0: dependencies: chalk: 4.1.0 webpack: 4.42.0_webpack@4.42.0 @@ -17163,7 +17139,7 @@ packages: peerDependencies: webpack: ^1 || ^2 || ^3 || ^4 || ^5 resolution: - integrity: sha512-AtVzD0bnIy2/B0fWqJpJgmhcrfWFhBlduzSo0uwplr/QvB33ZNZj2NEth3NONgdnZJqicK0W0mSxnLSbsVCDbw== + integrity: sha512-Re0wX5CtM6gW7bZA64ONOfEPEhwbiSF/vz6e2GvadjuaPrQcHTQdRGsD8+BE7iUOysXH8tIenkPCQBEcspXsNg== /split-string/3.1.0: dependencies: extend-shallow: 3.0.2 @@ -17740,17 +17716,22 @@ packages: node: '>=6.0.0' resolution: integrity: sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== - /table/6.0.7: + /table/6.0.9: dependencies: - ajv: 7.2.3 - lodash: 4.17.21 + ajv: 8.0.2 + is-boolean-object: 1.1.0 + is-number-object: 1.0.4 + is-string: 1.0.5 + lodash.clonedeep: 4.5.0 + lodash.flatten: 4.4.0 + lodash.truncate: 4.4.2 slice-ansi: 4.0.0 string-width: 4.2.2 dev: false engines: node: '>=10.0.0' resolution: - integrity: sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g== + integrity: sha512-F3cLs9a3hL1Z7N4+EkSscsel3z55XT950AvB05bwayrNg5T1/gykXtigioTAjbltvbMSJvvhFCbnf6mX+ntnJQ== /tapable/1.1.3: dev: false engines: @@ -17807,7 +17788,7 @@ packages: integrity: sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA== /tedious/9.2.3: dependencies: - '@azure/ms-rest-nodeauth': 3.0.8 + '@azure/ms-rest-nodeauth': 3.0.9 '@js-joda/core': 3.2.0 adal-node: 0.1.28 bl: 3.0.1 @@ -18464,14 +18445,14 @@ packages: dev: false resolution: integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== - /uglify-js/3.13.2: + /uglify-js/3.13.3: dev: false engines: node: '>=0.8.0' hasBin: true optional: true resolution: - integrity: sha512-SbMu4D2Vo95LMC/MetNaso1194M1htEA+JrqE9Hk+G2DhI+itfS9TRu9ZKeCahLDNa/J3n4MqUJ/fOHMzQpRWw== + integrity: sha512-otIc7O9LyxpUcQoXzj2hL4LPWKklO6LJWoJUzNa8A17Xgi4fOeDC8FBDOLHnC/Slo1CQgsZMcM6as0M76BZaig== /uglify-js/3.4.10: dependencies: commander: 2.19.0 @@ -18490,7 +18471,7 @@ packages: node: '>= 0.8' resolution: integrity: sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA== - /unbox-primitive/1.0.0: + /unbox-primitive/1.0.1: dependencies: function-bind: 1.1.1 has-bigints: 1.0.1 @@ -18498,7 +18479,7 @@ packages: which-boxed-primitive: 1.0.2 dev: false resolution: - integrity: sha512-P/51NX+JXyxK/aigg1/ZgyccdAxm5K1+n8+tvqSntjOivPt19gvm1VC49RWYetsiub8WViUchdxl/KWHHB0kzA== + integrity: sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== /unbzip2-stream/1.4.3: dependencies: buffer: 5.7.1 @@ -18844,11 +18825,6 @@ packages: node: '>= 0.4.0' resolution: integrity: sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= - /uuid/3.3.2: - dev: false - hasBin: true - resolution: - integrity: sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== /uuid/3.4.0: dev: false hasBin: true @@ -19574,13 +19550,12 @@ packages: dev: false resolution: integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== - /xmldom/0.1.31: - deprecated: Deprecated due to CVE-2021-21366 resolved in 0.5.0 + /xmldom/0.5.0: dev: false engines: - node: '>=0.1' + node: '>=10.0.0' resolution: - integrity: sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ== + integrity: sha512-Foaj5FXVzgn7xFzsKeNIde9g6aFBxTPi37iwsno8QvApmtg7KYrr+OPyRHcJF7dud2a5nGRBXK3n0dL62Gf7PA== /xmlhttprequest/1.8.0: dev: false engines: @@ -19770,7 +19745,7 @@ packages: dev: false name: '@rush-temp/analytical-backend' resolution: - integrity: sha512-AZ+HdApRhvlTFYG1AaThqVrKaTGszL2j6b14M/XKcU2rbnigAAtIQ9tpoj8fxmWEbPnLIGBgCzoP/bvAOPAYug== + integrity: sha512-yPBwagWu/vzfwNTQA95xVsfzIFU0nTZHr8xvblXjwI7FEheq3+ITubHQKT4kNhYTFoe3epDqaDdVaNB+gzzx6g== tarball: 'file:projects/analytical-backend.tgz' version: 0.0.0 'file:projects/backend-application-insights-client.tgz': @@ -19790,7 +19765,7 @@ packages: dev: false name: '@rush-temp/backend-application-insights-client' resolution: - integrity: sha512-bfiaBryqKcwM0y4Lo1vM75S+v6Vp63OXvyrdQzNFtWg7Dh3ZFBZOn9i7rwJNQVH6lT4DAMPfSXPFI2Lo1Ic+rQ== + integrity: sha512-NAZulUQkSctrypEMFuGQxH+bELB3qq24zhaZ6Kepr8OTOKMdON/qjZP24jRpu+dChRhb3w5SWWPl7nwu7C9A7A== tarball: 'file:projects/backend-application-insights-client.tgz' version: 0.0.0 'file:projects/backend-itwin-client.tgz': @@ -19831,7 +19806,7 @@ packages: dev: false name: '@rush-temp/backend-itwin-client' resolution: - integrity: sha512-TnW8tMq8Id7SuhSjAG+Ncbe85Qtiz7qNlfMfsk+SjQjlGQVDpmcq376CIpN/tD4PwMnyoQ/AyoI4jCJNzsxZww== + integrity: sha512-Wh4lINxcyg3wbqZl+I9gx9O0H1nkXN9/Ed0EyJOZ2DeX+szmIGSVXiM/f3og5yyhKgBqr0UwICMetu3roYypyA== tarball: 'file:projects/backend-itwin-client.tgz' version: 0.0.0 'file:projects/backend-webpack-tools.tgz': @@ -19852,7 +19827,7 @@ packages: dev: false name: '@rush-temp/backend-webpack-tools' resolution: - integrity: sha512-JMX3JHjs9qWxAh83ZK8+sIOzNhVzUbFYDPhQnk4B7C6bnIFNJJ+6TP+bHMdR802nssUADGfE8LHRfmVfUZtB9A== + integrity: sha512-DuhdO/GzBgAceseTUhNeX4u3DaAPoAABjos0a58qJCovBiJW65AaABZbon9CTQDll7+KYnX7o9mob25UgDBuvg== tarball: 'file:projects/backend-webpack-tools.tgz' version: 0.0.0 'file:projects/bentleyjs-core.tgz': @@ -19871,7 +19846,7 @@ packages: dev: false name: '@rush-temp/bentleyjs-core' resolution: - integrity: sha512-G354ZZzX/ffhfBlE4afGLpUbo7GAgY903GT5KY0g92T86pLaBmWsOxTZktQ3HY7WBaNrzlpHbfXDO0jUeSCbQQ== + integrity: sha512-ueZj2I2lynCbd9SaN0SAh0fVLseg3dfO5ke1h3Ws4xR5XsrJWpRs3IHzGx76BP/aTHnKaaOZ4Nnk7ru5GfuUQg== tarball: 'file:projects/bentleyjs-core.tgz' version: 0.0.0 'file:projects/build-tools.tgz': @@ -19907,7 +19882,7 @@ packages: dev: false name: '@rush-temp/build-tools' resolution: - integrity: sha512-PZr3Qx6HF5PkQNaCjrEE3zGM2Io/b3QtxDerbbfO4/gVrYxcNrblA6OYmcfGek2tgZMc+E8G4ZL5f+PuF75zhw== + integrity: sha512-osQkSerHX42YlROWHyU+heiciBjISZouzi+23PVzcOV7BcF2lifuy7RRQiNi30G+B95IzjpfrSXfbhrfOXc+sA== tarball: 'file:projects/build-tools.tgz' version: 0.0.0 'file:projects/certa.tgz': @@ -19938,7 +19913,7 @@ packages: dev: false name: '@rush-temp/certa' resolution: - integrity: sha512-BJz/ppn5QStlm8yV8cu3DXhlwsgERaCY7S+05QsGSTTvk1ccJabP2+4OTnG9bWV8He6yacwjHzyk6N5jqEj/LA== + integrity: sha512-wAXlq8v9ctz033xz5gwd5NNXcIeMLhqS80TNvN/XbpBzCA7MY5apgcIcmtefjeh/rH3cQrgpsLsUAv/QcIdX8w== tarball: 'file:projects/certa.tgz' version: 0.0.0 'file:projects/config-loader.tgz': @@ -19955,7 +19930,7 @@ packages: dev: false name: '@rush-temp/config-loader' resolution: - integrity: sha512-spfRT6tal19HJ0nhqJ/hwFsAyZCudnG44ZUhqwa307yA2EoZZs6O5D8twvz6IorENqXU+rfm033zLIEYNi1kwg== + integrity: sha512-pukv+e6Wt7GJ9wN/lPFRweOELE2b6YlGwK2aY9CkxQeq436JO+HmwLGR/c/4rJorFIELn5jfW1krYtqx72kXuw== tarball: 'file:projects/config-loader.tgz' version: 0.0.0 'file:projects/context-registry-client.tgz': @@ -19976,7 +19951,7 @@ packages: dev: false name: '@rush-temp/context-registry-client' resolution: - integrity: sha512-aC3cjYMZKjj24mtJSxE9wV8Weup674qSJAAwT8v4Ee6+1KtyXnKnAq9tWSvV+08jDYVngYvwfz/p7fAhSISpTg== + integrity: sha512-aBzK9wF9VIChUnB5jDLPWOyrW2bxEx5KymKILoDfKQNIR/n0CN3H+V5ithqNBaLElqJ8Ah/M5cfqxuUMml8WXg== tarball: 'file:projects/context-registry-client.tgz' version: 0.0.0 'file:projects/core-full-stack-tests.tgz': @@ -20005,7 +19980,7 @@ packages: dev: false name: '@rush-temp/core-full-stack-tests' resolution: - integrity: sha512-gzRq3ABgmdgsDeFppty2iGpeS7hduA4N0kUNToCdDznDZEl0mCtK4u+OofSUHTiR/mu10jYjI3luzX+FRC902A== + integrity: sha512-I/0Jaio9FwGsGQKHhCczXaK9I1zsdSB5lBT2I+zub2icfc9iu7nkWc/3m2FR1u9NK6yWvwP84Ze/vz/RpKcSlg== tarball: 'file:projects/core-full-stack-tests.tgz' version: 0.0.0 'file:projects/display-performance-test-app.tgz_sass@1.32.8+webpack-cli@3.3.12': @@ -20035,7 +20010,7 @@ packages: sass: '*' webpack-cli: '*' resolution: - integrity: sha512-uPDWiSomBPV6h9URdY3n0WAmBJOip6A4mKixdlF8qKsMyXGneJNAnMvsP58dwBOEP6fGZuHN8DENBtKhkNl6lg== + integrity: sha512-i+jlZkj8e+7wxYuudrVPQDl0n8tHpBbaQpygICpL/4XHGU+7rQQgVzLEHzf2FhH5oxCwsiHEdO9wD+ocsf4tWw== tarball: 'file:projects/display-performance-test-app.tgz' version: 0.0.0 'file:projects/display-test-app.tgz_webpack-cli@3.3.12': @@ -20069,7 +20044,7 @@ packages: peerDependencies: webpack-cli: '*' resolution: - integrity: sha512-lHU4QgL80YUnBrVDIKOTKvHHiYacgfx5/IkuwRfgPUIVU6y0xnLWk3O328A+aFPsryLOt6ucQJs7zrWZAuUGvA== + integrity: sha512-UtRWNbjARkSfZdDkzphBqP5ozB9ASdhoXkXrM8k6YHU9nPlxGbJIZZngWJjv6a8GEovkBEijy6MIYH+18UQeKA== tarball: 'file:projects/display-test-app.tgz' version: 0.0.0 'file:projects/ecschema-locaters.tgz': @@ -20089,11 +20064,11 @@ packages: nyc: 14.1.1 rimraf: 3.0.2 typescript: 4.1.5 - xmldom: 0.1.31 + xmldom: 0.5.0 dev: false name: '@rush-temp/ecschema-locaters' resolution: - integrity: sha512-RuTH+ml7s7kGaVNDKTQrPql8EUvgZeqlZg+jIJOjGcNgt9ZcsgUPzYIsyMkrkwsQZJ3VPwLNOdtllAlVkL8uAg== + integrity: sha512-J3/D9KtA+cn8z13XmeOydNmllOooysFJDQ7dITSypcrG3Ouaztz9LoIifFb1xc0W+LXUURiiU6BH4UErxz/HiQ== tarball: 'file:projects/ecschema-locaters.tgz' version: 0.0.0 'file:projects/ecschema-metadata.tgz': @@ -20112,6 +20087,7 @@ packages: benchmark: 2.1.4 chai: 4.3.4 chai-as-promised: 7.1.1_chai@4.3.4 + cpx: 1.5.0 eslint: 6.8.0 i18next-node-fs-backend: 2.1.3 mocha: 5.2.0 @@ -20119,12 +20095,12 @@ packages: rimraf: 3.0.2 sinon: 9.2.4 typescript: 4.1.5 - xmldom: 0.1.31 + xmldom: 0.5.0 xmlhttprequest: 1.8.0 dev: false name: '@rush-temp/ecschema-metadata' resolution: - integrity: sha512-OjBRoUyuhfKYLLZx4s/zBm+sdCVnSJ8p7JCxgzGA5GaUewo6ZvPKXq25PROW5RFQSkJoFXrSb/J3vk9xS8pbjw== + integrity: sha512-bEV+/oWx5rC2NVBdnzHx69ay6TH4x5kqeFnaF/anwfoIGxjFw+ryDt9pbiPx3vlxGFFc6DfTdQydUTiMGtbrUQ== tarball: 'file:projects/ecschema-metadata.tgz' version: 0.0.0 'file:projects/ecschema2ts.tgz': @@ -20147,11 +20123,11 @@ packages: rimraf: 3.0.2 source-map-support: 0.5.19 typescript: 4.1.5 - xmldom: 0.1.31 + xmldom: 0.5.0 dev: false name: '@rush-temp/ecschema2ts' resolution: - integrity: sha512-dajB5yXn33vjWjqE6fTpkcgIkc9gDjbjYm2KelpIxaLRZ/adXUI0fXqxOgXh1Ky9+4pE+UcadVrbkd+lFpATDw== + integrity: sha512-kuUq0ipQGosRwudwvM5ph5pyvxdJwBKg8/pdJjNFCzpZPDnIvsSsqDGGrUj0zasWMoAmIba07Jiz78IvjxErqQ== tarball: 'file:projects/ecschema2ts.tgz' version: 0.0.0 'file:projects/electron-manager.tgz_debug@2.6.9': @@ -20170,24 +20146,24 @@ packages: peerDependencies: debug: '*' resolution: - integrity: sha512-w/CqYYTTTP0JdP7Rr4F1m2kHApN8me6qZmf5ZkKIpfTbBoENs9lKoTZVLX5hGmxMteX7dQRYls3cl7HtDzjTmw== + integrity: sha512-wZNHaUfmUPy/LI8BcMhK41xbw4qpV508/vrqopjgXksf9EwOLuW1ONiu25j1UlGjhaKmT+rZgmk01cM1PuugvQ== tarball: 'file:projects/electron-manager.tgz' version: 0.0.0 'file:projects/eslint-plugin.tgz': dependencies: - '@typescript-eslint/eslint-plugin': 4.11.1_96d27755fc43363abf6361149046cfaa - '@typescript-eslint/parser': 4.11.1_eslint@7.22.0+typescript@4.1.5 - eslint: 7.22.0 + '@typescript-eslint/eslint-plugin': 4.11.1_092af2b0a1e663b3f2e5c1684c00c49e + '@typescript-eslint/parser': 4.11.1_eslint@7.23.0+typescript@4.1.5 + eslint: 7.23.0 eslint-import-resolver-node: 0.3.4 - eslint-import-resolver-typescript: 2.0.0_e8c3ce6c9a72ba40b09aaf06432c8600 - eslint-plugin-deprecation: 1.1.0_eslint@7.22.0+typescript@4.1.5 - eslint-plugin-import: 2.20.1_eslint@7.22.0 + eslint-import-resolver-typescript: 2.0.0_dc50b4c80c8cf29c4f2da372abb90f3d + eslint-plugin-deprecation: 1.1.0_eslint@7.23.0+typescript@4.1.5 + eslint-plugin-import: 2.20.1_eslint@7.23.0 eslint-plugin-jam3: 0.2.3 - eslint-plugin-jsdoc: 30.6.2_eslint@7.22.0 - eslint-plugin-jsx-a11y: 6.4.1_eslint@7.22.0 - eslint-plugin-prefer-arrow: 1.1.7_eslint@7.22.0 - eslint-plugin-react: 7.19.0_eslint@7.22.0 - eslint-plugin-react-hooks: 3.0.0_eslint@7.22.0 + eslint-plugin-jsdoc: 30.6.2_eslint@7.23.0 + eslint-plugin-jsx-a11y: 6.4.1_eslint@7.23.0 + eslint-plugin-prefer-arrow: 1.1.7_eslint@7.23.0 + eslint-plugin-react: 7.19.0_eslint@7.23.0 + eslint-plugin-react-hooks: 3.0.0_eslint@7.23.0 require-dir: 1.2.0 typescript: 4.1.5 dev: false @@ -20228,7 +20204,7 @@ packages: dev: false name: '@rush-temp/example-code-app' resolution: - integrity: sha512-AlMe5i0L3lYggtI8R8xyTcjEyE99woLOf4JzPLQx/RvLZVtLxkLwtMZnOuEQCu1pO5Y3UotJVRfn+S7CWsR3UQ== + integrity: sha512-iy+HrwwPV5OlR7RrndRXSc1bERLfV9LSGqjh0OoSkV8+hJPHkcrNflkuQfOQMGuHNRn/qzNeh1cBCVyr6h2XHw== tarball: 'file:projects/example-code-app.tgz' version: 0.0.0 'file:projects/example-code-snippets.tgz': @@ -20262,7 +20238,7 @@ packages: dev: false name: '@rush-temp/example-code-snippets' resolution: - integrity: sha512-naYm9G2lRjoqe0SEE8fWQqlM2+XeYAIokUgyU3gWkRPeQBxL4WzHfLMLE4TfPLjX4N3U5+RxM6iumc7hupAnzQ== + integrity: sha512-q+33os/xENG4+GEippqmhMk5LC+hZSMUAK+aDIeRUC8IlRdHPbnNLug/nGFFXS3u8JDt8aP2DEtgm9SON0Kq8A== tarball: 'file:projects/example-code-snippets.tgz' version: 0.0.0 'file:projects/export-gltf.tgz': @@ -20276,7 +20252,7 @@ packages: dev: false name: '@rush-temp/export-gltf' resolution: - integrity: sha512-AF9lDC0DRQ7Lscl6DTxr7vYpFzXSv588I9Zyct8yRYbHQYO/TSwijhJpc4d11xQ5oCwxA78PCLLTari0gZNmeg== + integrity: sha512-MuqaTgje47Q1ShuKmE7v7m8exxXZt15UB1O59jDwJsPD1d+Nmw73MLDMAlc2K4h8Ta5YIQVgVbtXq78moAyzIQ== tarball: 'file:projects/export-gltf.tgz' version: 0.0.0 'file:projects/export-obj.tgz': @@ -20290,7 +20266,7 @@ packages: dev: false name: '@rush-temp/export-obj' resolution: - integrity: sha512-jhLEM9COIQS72uokbNlzoKeMzUa+dD09XtDcrovqISS1bMsGyQLI1eFc8/dLY+qAuuaPtFerR1GfLCZgj4KcrQ== + integrity: sha512-KXx9YulCEu2shNI4zh/ERn7WkrpTp7ma5F+uINNMvGbEnsRpvBh13U0CY+cZP0C6VlOJu5yU1dfZWGRmhmUjjA== tarball: 'file:projects/export-obj.tgz' version: 0.0.0 'file:projects/express-server.tgz': @@ -20301,7 +20277,7 @@ packages: '@types/mocha': 5.2.7 '@types/node': 10.14.1 '@types/sinon': 9.0.11 - '@types/supertest': 2.0.10 + '@types/supertest': 2.0.11 chai: 4.3.4 eslint: 6.8.0 express: 4.17.1 @@ -20315,7 +20291,7 @@ packages: dev: false name: '@rush-temp/express-server' resolution: - integrity: sha512-IFKVKRtATZCv5rfe80XdJVETE+ERfMNS1xeVS8HyqXxcwK0Xi474KL+onAY6cs10J7TnffMGFTVuzAFAbKFDjA== + integrity: sha512-sze/Djuuo4FnMhRcbjnsTMVYQ5dasNewjvWpYrI8vag7v/7yx49SMbwWzWxOcpzfXKk29iRTcolXpdQb3fg4Bw== tarball: 'file:projects/express-server.tgz' version: 0.0.0 'file:projects/extension-cli.tgz': @@ -20340,7 +20316,7 @@ packages: dev: false name: '@rush-temp/extension-cli' resolution: - integrity: sha512-Avb2HvF+/KEAOZddbaX5wM52WQHN31skw6iWu4pmA/cHEoMAh+IoI8gVv0yTwl18K+6Vtnf8N7D1+tWuCO0l5w== + integrity: sha512-VNUe2SW8LVBb3wmT5l9US2wx83l23TxypyJqZqPZVXkLFmQFc6NCC78l9b7QCav9TZZ5X6Iw1dW3Jr4oQToVhA== tarball: 'file:projects/extension-cli.tgz' version: 0.0.0 'file:projects/extension-client.tgz': @@ -20359,7 +20335,7 @@ packages: dev: false name: '@rush-temp/extension-client' resolution: - integrity: sha512-sqOZQQYNlGzAiELS0gmgsPLTqLjtRSTPpOpBfONEIXUSxad1azPWrKDbzzBUI44gyZjiSEoTs6sV7SLbmQo4Uw== + integrity: sha512-0QsoFDzq6a3RF49IevsbXmveI1xBYoJJBkS9YH9MflMc/VA0TOq14BpYcyHDUyrGYGu/ZHs+xjPZQn9kxfm54g== tarball: 'file:projects/extension-client.tgz' version: 0.0.0 'file:projects/extension-webpack-tools.tgz': @@ -20405,12 +20381,12 @@ packages: dev: false name: '@rush-temp/extension-webpack-tools' resolution: - integrity: sha512-XrDtgZsUSR6XEbZgupyki07Fsby8GOwvDQDyBxj2+t5V5CMhWDDOi6YuUFNcsQokQU4qikCls2VWUXkoZpjHDQ== + integrity: sha512-/x87WFhiZzhlQMf+EJf+42lfoqp1HaQvrWX38yjJwykXqq/SVFKYhHrxTGSNaghT1nCQJGj8JmxE4eH54Jjr7A== tarball: 'file:projects/extension-webpack-tools.tgz' version: 0.0.0 'file:projects/frontend-application-insights-client.tgz': dependencies: - '@microsoft/applicationinsights-web': 2.6.0 + '@microsoft/applicationinsights-web': 2.6.1 '@types/chai': 4.2.15 '@types/mocha': 5.2.7 '@types/node': 10.14.1 @@ -20425,7 +20401,7 @@ packages: dev: false name: '@rush-temp/frontend-application-insights-client' resolution: - integrity: sha512-kVEsmb9d0pIpp8hSJjCLyoy23lRQJJ1yk1hkA6mpZzCfO8I6SZ/e2XhK/R6H5blPGAQXRWNQPeFJraV7JUrvUg== + integrity: sha512-qdf3ifdY3QXLseGTyKCg3d2lwoVKmdjL+fivmr+fFVgUl42Isv3PTcDSs+KUZ2yJGf4suuqebdCfmoOmnhHbew== tarball: 'file:projects/frontend-application-insights-client.tgz' version: 0.0.0 'file:projects/frontend-authorization-client.tgz': @@ -20438,7 +20414,7 @@ packages: dev: false name: '@rush-temp/frontend-authorization-client' resolution: - integrity: sha512-gvD7rP7L/tr6LXuv242jD61zIFY6uR2LLo62OVgok9Jg+HaaXuFVr9CayfdKz9mxJOpCCzq7klfy+X1aL9H1hg== + integrity: sha512-Q9D8tlz4PrEdHJyiPTjAdeQgRJ7TVw+F6B6B9vaIp2WJnwC4yrMn+qDtQLhjh4baDbVquVDmxNBk7z+DEz4SeA== tarball: 'file:projects/frontend-authorization-client.tgz' version: 0.0.0 'file:projects/frontend-devtools.tgz': @@ -20453,7 +20429,7 @@ packages: dev: false name: '@rush-temp/frontend-devtools' resolution: - integrity: sha512-v3lpcrA9U6Wm641kRW9G+hPylqzyClMvo6x3sBInbEMsOEK9Bk+NrxNl3yZDWpdnWggWp1OlecwJAIY26DJohg== + integrity: sha512-pSrg5cjbDFM3f1Cy/CbHjoPMoKlVcGTylKCuZjKyT66wU6vGuNlB/7/QXwRAiv498M58pkMpelCasH9JXTF1Pg== tarball: 'file:projects/frontend-devtools.tgz' version: 0.0.0 'file:projects/geometry-core.tgz': @@ -20474,7 +20450,7 @@ packages: dev: false name: '@rush-temp/geometry-core' resolution: - integrity: sha512-lrgDLwLfNJSXOUAk+1piK+B6bOEesGvOBMOU7Sb8U7RDAmKZJvKOgRY/HAU7b1mGIeVvKk/nXkbGWxooTaPE7g== + integrity: sha512-0OYuCW/mm5XXkgQZv/iUJpE+9jUMW+Sf64LM7hitWJR/BWFyBFkfDoqHgpVm4r25u/C9ZceDEU0fwB7V8TcCLg== tarball: 'file:projects/geometry-core.tgz' version: 0.0.0 'file:projects/geonames-extension.tgz_webpack@4.42.0': @@ -20491,7 +20467,7 @@ packages: peerDependencies: webpack: '*' resolution: - integrity: sha512-Upzni1XTjOaoIKF4Up15xpw/+NPVhAQaFI4wIfb2mv02g82zdz9IpQMw0sHnEXZym543KFJgHrgX5Z/scp/dZQ== + integrity: sha512-fRxjw48EpfXhdy6ABkXZHNVNXpaRXvK1pqvXIwQZkRl0Cr4VfekNxXpAdJScl6Xte8u9wy7Z+p1IrZA150vk7A== tarball: 'file:projects/geonames-extension.tgz' version: 0.0.0 'file:projects/hypermodeling-frontend.tgz': @@ -20512,7 +20488,7 @@ packages: dev: false name: '@rush-temp/hypermodeling-frontend' resolution: - integrity: sha512-fewmwWeEKyWUy7RPp/cM5USHw+NACL4MxuqPO6bdEVdt2dbDcBa4XnUxqBzIguGZnsX7+jMyrC9hcTMCvv3OXw== + integrity: sha512-t6mdRVbboY96WgcDaN+RMR01fFVOaxnUN52+h2JkEqiH5Pzwhn5WVu49f+rBKOZVLO8THfcaKk6WdiVDfp5w4g== tarball: 'file:projects/hypermodeling-frontend.tgz' version: 0.0.0 'file:projects/imjs-importer.tgz': @@ -20530,7 +20506,7 @@ packages: dev: false name: '@rush-temp/imjs-importer' resolution: - integrity: sha512-76vQ3+VAqAHOl98DDiX8SYoKx0x3LS+B2s9PTo9JC83JTsKSh7av3/e9lbVnclc5BgsEZK7pKKLlzYgcYHObKw== + integrity: sha512-3bnaDpikUQwaN4rzKpUkqTv37RzZAOGxnyuZrF44KxaB4yWcdtGi6FEQZGQl3DKgJfDnJEbn0fgSQ7+1WRrqQQ== tarball: 'file:projects/imjs-importer.tgz' version: 0.0.0 'file:projects/imodel-bridge.tgz': @@ -20550,7 +20526,7 @@ packages: dev: false name: '@rush-temp/imodel-bridge' resolution: - integrity: sha512-qlE0RTROFqRqLfLLG0S1lHTY/OH6DEBLPpTaMLdphS0wNNVXBolQWsSJlzpNaprk7j3+oY5XoHrLCHl2oosaLQ== + integrity: sha512-p8Z1DaJYAIMcBBJNka7sKTN2Gbj0ZOOR6lQvrZ6Kh/heFPLsQXRfrbak7JARCNTnVd9HqJ2xebBxgSVnCM2HHw== tarball: 'file:projects/imodel-bridge.tgz' version: 0.0.0 'file:projects/imodel-from-geojson.tgz_request@2.88.2': @@ -20572,7 +20548,7 @@ packages: peerDependencies: request: '*' resolution: - integrity: sha512-lUi4kfgdbZD61rfhtxGjSLMlvz0fpnaH5pY+ZlKsHITV5uDL4Ib43PnWxJHqlGfdHxLdeebt3JxZso+5jEfm3A== + integrity: sha512-6BWWzeI2zuY5Dfso8g+tKtgRoNcEl8mASgFNnOPwtI6mx6Eu2b/yoMscbL/R6KO5KsqtC7koFy12LWGtp0lA5A== tarball: 'file:projects/imodel-from-geojson.tgz' version: 0.0.0 'file:projects/imodel-from-orbitgt-pointcloud.tgz': @@ -20591,7 +20567,7 @@ packages: dev: false name: '@rush-temp/imodel-from-orbitgt-pointcloud' resolution: - integrity: sha512-exQBkFsSgC3czvN2J4BixPqxMof1I7qcAl4J4VS6kHv/Bk9uVoYZDgNH7h4g62Vd3eqWNdYt8AtnnEnNKrx/xg== + integrity: sha512-dyiUkB/JtQlzpot2Vzy/ompt+ZW9vforlKcP9aQJqbMv5Ky7DFUhGYBw1IW3ZBmrFjC4Mk+unpgY46cT549GpA== tarball: 'file:projects/imodel-from-orbitgt-pointcloud.tgz' version: 0.0.0 'file:projects/imodel-from-reality-model.tgz': @@ -20611,7 +20587,7 @@ packages: dev: false name: '@rush-temp/imodel-from-reality-model' resolution: - integrity: sha512-JA1Mr37DGm1iWQAaNlrR6z+uFWVevgLGAZt3yMaO3BgIVynr+VPMkxd+SlUHISaoYUpnkDbPtSq4k3ga/qJXPw== + integrity: sha512-NatTk7XKLJ6+ree1LLgXGAKbe+8upTNc15Ype33U62NG1AgLSNVBgHf8s5lDigm+TcqYcMI2FlTyi+Vylnta7A== tarball: 'file:projects/imodel-from-reality-model.tgz' version: 0.0.0 'file:projects/imodel-transformer.tgz_request@2.88.2': @@ -20636,7 +20612,7 @@ packages: peerDependencies: request: '*' resolution: - integrity: sha512-9OKCIJ+VM4xUQNsxNzNHxf2z5MQtrhtDsky3JLxik253gJjX+MRpiIV0ZXQZF8IOmi3a7AQpwGQKhQsHAmmiQQ== + integrity: sha512-4VdSLUdMjPR6GJHsa1mY0BBXeXj4nx3LjV8xqth0pdTVp06IQAbrLUNDa3SC+3682cJaqVdmoJz0tALEvsCCcw== tarball: 'file:projects/imodel-transformer.tgz' version: 0.0.0 'file:projects/imodelhub-client-tests.tgz': @@ -20661,7 +20637,7 @@ packages: dev: false name: '@rush-temp/imodelhub-client-tests' resolution: - integrity: sha512-SZ5Efp2pPhxfE4L6+Dpxer5hl6ZCc4OT4tpzVtFcyvlQXTPygnr3a6VhqKeLOo83UT9G1Gis8CXaH5siZBSRvg== + integrity: sha512-Yh7MUhagApzUlIp/57bKJznNH6LWJhg/WWvbr8Jyy2+lnlEZ9GRn3uxJoE3Q6b+e2j5FaIBZK8sBZ4wUcf7ywQ== tarball: 'file:projects/imodelhub-client-tests.tgz' version: 0.0.0 'file:projects/imodelhub-client.tgz': @@ -20677,17 +20653,17 @@ packages: dev: false name: '@rush-temp/imodelhub-client' resolution: - integrity: sha512-3akXxsG06PyED9r2WXtfFZZsSVDkHAG6R1eY+jkEnllX/lq2yYKKN6xKY2GRtbOhOLhQspw/EzmPaedklr1yBA== + integrity: sha512-8YE6hffbMyuEUYoROV1ZS6d9O4mxuKcVTUeHdA7/K7WTPQd7a8udQu4DQgCkqAZ4nJZwUSae+8TyCW2fIcrrkg== tarball: 'file:projects/imodelhub-client.tgz' version: 0.0.0 'file:projects/imodeljs-backend.tgz_debug@2.6.9': dependencies: '@azure/storage-blob': 10.4.0 - '@bentley/imodeljs-native': 2.15.0 + '@bentley/imodeljs-native': 2.15.1 '@types/chai': 4.2.15 '@types/chai-as-promised': 7.1.3 '@types/deep-assign': 0.1.1 - '@types/formidable': 1.2.0 + '@types/formidable': 1.2.1 '@types/fs-extra': 4.0.11 '@types/glob': 5.0.36 '@types/js-base64': 2.3.2 @@ -20730,7 +20706,7 @@ packages: peerDependencies: debug: '*' resolution: - integrity: sha512-1jt3seUZG5drv8EZwIrvHOf9I5+mGXDQHTcLBgYwgAA2jWkW0kZXMwWNVuavV/ZJm9FfONo325R1BMPpuUj1Lg== + integrity: sha512-+tUkbarOdZuHv2Wqj2ljYJJTzMdbzjzOL8qlG2w8pAFYU1VgqiQlcspFhvMw2VEw1tjCQioCHvZpV7tqi7ER+w== tarball: 'file:projects/imodeljs-backend.tgz' version: 0.0.0 'file:projects/imodeljs-common.tgz': @@ -20754,7 +20730,7 @@ packages: dev: false name: '@rush-temp/imodeljs-common' resolution: - integrity: sha512-mKI/NfOtTzEoa1spsJBFGIY4hT4Z0SV+Is9mrR/QNKJFWZSE3KA3QdJfcnFuTOrP2aGNHs13GUCxRmrL7x6o2A== + integrity: sha512-kpo5oQYj722us84x5ybSggdbwkXKTIC5wa/oejOpkQj8vD7LNTCvReLM/LqQQ3hLTSVujeqLYLcr1N+lIjwZHA== tarball: 'file:projects/imodeljs-common.tgz' version: 0.0.0 'file:projects/imodeljs-editor-backend.tgz_webpack@4.42.0': @@ -20783,7 +20759,7 @@ packages: peerDependencies: webpack: '*' resolution: - integrity: sha512-roLZy7xFLfo7GsR8cldWaryz/2aGDYGHfB1RfNQByjAPBru8T4S8QlIgnyCpHCFWilsd0rK1JtNSQyo9E7I9eQ== + integrity: sha512-Jg53bbU+U2K2/8LXliinnpV8JcIw9yoTNxQbXvPt9RpwdqauW2ocXLuQVH4No1pKVEuNeGGjqo8SxKwdbT2Ttg== tarball: 'file:projects/imodeljs-editor-backend.tgz' version: 0.0.0 'file:projects/imodeljs-editor-common.tgz': @@ -20800,7 +20776,7 @@ packages: dev: false name: '@rush-temp/imodeljs-editor-common' resolution: - integrity: sha512-vCHLfi+bT4sKAcUkBIUoaSDP8QdDnwoB0wR9c2QgnNliYJjF7/YT3+cTCwCY9wvFjnPpAYbiKjiqMrAO7hi8qQ== + integrity: sha512-LQRcZQRQ7UYRPez5RUSuNNsGubn3p3LnnHMWBeGLGcWhWWsaOaPG5iPCb3kj/5JSfr1cn50pbE9xIL/7W60npw== tarball: 'file:projects/imodeljs-editor-common.tgz' version: 0.0.0 'file:projects/imodeljs-editor-frontend.tgz_webpack@4.42.0': @@ -20825,7 +20801,7 @@ packages: peerDependencies: webpack: '*' resolution: - integrity: sha512-51I+/7ICDzCNJ9U71TcLZNQ92u29koV+PGlovk8Iv/MR1GbN0GQovPyrId122YWCkGqHlT8/srPMW7scMcxoZw== + integrity: sha512-cfg6GMunpMU3kG/Xb+o7SVFM4Q5RI3YQZ+u89zR5I3MVQ1S4Rs/nxLP091fSBRGpJm8p7oduP3nKmHpyaUos+w== tarball: 'file:projects/imodeljs-editor-frontend.tgz' version: 0.0.0 'file:projects/imodeljs-frontend.tgz': @@ -20845,7 +20821,6 @@ packages: fuse.js: 3.6.1 glob: 7.1.6 js-base64: 2.6.4 - ldclient-js: 2.10.2 mocha: 5.2.0 nyc: 14.1.1 oidc-client: 1.11.5 @@ -20860,7 +20835,7 @@ packages: dev: false name: '@rush-temp/imodeljs-frontend' resolution: - integrity: sha512-khuDsyXUykSSm7+HYgGvUXfX3Vk0jCmRZDOuQO0VR2+ytZRDAzEIC05wwtJtfoYLIyBNk1W/Mylqr7xVjkKjlQ== + integrity: sha512-Chhwr5FfEmLRsfNQvx6tTDaZK/9czS1SpjOAM/ZzJ0YYj5JkBM6IPCEPimkSTgqPxHvem8sCFsk1ljepBSF0Zw== tarball: 'file:projects/imodeljs-frontend.tgz' version: 0.0.0 'file:projects/imodeljs-i18n.tgz': @@ -20877,7 +20852,7 @@ packages: dev: false name: '@rush-temp/imodeljs-i18n' resolution: - integrity: sha512-cr8AcWQBIqftGg8hasp+kdGZtu8su91eZacWyzGu3eDlBoa1WBA59OCd0uVkblL/0J9vhNa4LUntpHv/1FrhIQ== + integrity: sha512-JxX79tisaos5LnYfM5XxnZwSKm8gv7MO/Em1Wue+KVH38psnEvBlE9pgQ8zsrScwxRIHCsuLIfeAfH97bQQEIg== tarball: 'file:projects/imodeljs-i18n.tgz' version: 0.0.0 'file:projects/imodeljs-markup.tgz': @@ -20899,7 +20874,7 @@ packages: dev: false name: '@rush-temp/imodeljs-markup' resolution: - integrity: sha512-cnkfu5OGt/v8Cx8vbKmAj+wEE1neKNRine4lrtjxFNB8iJe7SzeFIfgJ0uUUXU8u3w3Pb6KCTrbA/A6em/8JOg== + integrity: sha512-P0QXOu2rODI8dCxLmUyF17HdFcevHttdU845aEN8pxrl8u18J14dOUcGO2F1ElWX6DIQX4vPend0C2dNeN5Vzg== tarball: 'file:projects/imodeljs-markup.tgz' version: 0.0.0 'file:projects/imodeljs-quantity.tgz': @@ -20921,7 +20896,7 @@ packages: dev: false name: '@rush-temp/imodeljs-quantity' resolution: - integrity: sha512-rr+HLywUNOLJC09Nwn/1VHib/uhPzmsZ0M3IOoSZYm+xlkY+d7my7sur1U5w1VH0b7poXsRwfNAhJAI37e6pBA== + integrity: sha512-8DTs2S0tFkHwOSIjIc6OVpdzESNIkaS2po1Ub6AZEwDryFQ8dxC//sEpjdD1wn3G1WqAL/rnkyKzdL+t2DcV1Q== tarball: 'file:projects/imodeljs-quantity.tgz' version: 0.0.0 'file:projects/internal-tools.tgz': @@ -20950,7 +20925,7 @@ packages: peerDependencies: webpack: '*' resolution: - integrity: sha512-ynG/2JpVB1css8TkMe7tcvA5NOfqjx3OuwQkt3A1CbCu0PvLLPO0nbq2cK4WtNTa+xbiRx6Lrq50rfaNRKE62A== + integrity: sha512-Vaqy4GKke7urrdW64L3YIuTbPWAU2VneoQzuml0nBprux+wpj8UM/toNRKlSR3byt3yNhwPrFLWgt0isZGCjiA== tarball: 'file:projects/iot-demo.tgz' version: 0.0.0 'file:projects/itwin-client.tgz': @@ -20977,12 +20952,12 @@ packages: superagent: 5.3.1 typescript: 4.1.5 webpack: 4.42.0_webpack@4.42.0 - xmldom: 0.1.31 + xmldom: 0.5.0 xpath: 0.0.27 dev: false name: '@rush-temp/itwin-client' resolution: - integrity: sha512-V0iQGM9cGHVEtJGBtSsyBvafLLHySfAMfv4QBpI3yYUUuE13jlV9Duu9l6tscT3p4EzTBMfEP6d3jVeUuYFrjA== + integrity: sha512-UFWcTcEAjqUsMpFyYyMGTAYzaDklSvIikGa+sKU7kiST8EYkQ0E+wnP/7koUOEmZgiJCfr+zgH2SbOCpfWJdUg== tarball: 'file:projects/itwin-client.tgz' version: 0.0.0 'file:projects/linear-referencing-backend.tgz': @@ -21001,7 +20976,7 @@ packages: dev: false name: '@rush-temp/linear-referencing-backend' resolution: - integrity: sha512-ZQPEuyxpJR4vpTZizC6OiyoS9ABGtG5YBIXuxSiLPbU9ZxYGdOrcBuBRlWz78DOa8zNR3h9K8yHgMFV3DmjJXQ== + integrity: sha512-IZmYGFCZVYmqRXy3l7gkuCQAFKiYLXkzkiZkk9Y6D9YKn5IKYxRA0hMz52zC8Qb67WYSgCGo4XL+p59/b53w4g== tarball: 'file:projects/linear-referencing-backend.tgz' version: 0.0.0 'file:projects/linear-referencing-common.tgz': @@ -21019,7 +20994,7 @@ packages: dev: false name: '@rush-temp/linear-referencing-common' resolution: - integrity: sha512-Ppgd52lBFZhA8k4yZSLGvMe9FFXEQPKhBVgS5OemLxtXle2rYGHPIrHwvxpLHdDm1zHaKpnFUeCGX1KxdFUMZw== + integrity: sha512-8yZHAXEafBflSYRskt/7bEva51pRH83kTMVZ5+/RLTWL8ecwR1UOX8HN5k9pOiNY8mNvEtEcarZTOlw4by74RA== tarball: 'file:projects/linear-referencing-common.tgz' version: 0.0.0 'file:projects/logger-config.tgz': @@ -21037,7 +21012,7 @@ packages: dev: false name: '@rush-temp/logger-config' resolution: - integrity: sha512-yp55y9bUyIZPa3qN3ynGuuoTD9D+x8eKTWyi7gHpi1/cHcCDEwGPOLT38nv/usiVNiMmAiy9F7/JrnNMEUF3xw== + integrity: sha512-xcXMQikcoTee+2t1IWbds49cN5F2L6+VHe027p0CQ61kLNVsKBuBdwZ4xGQnM8yanPkmoMtjSMFXdK+1M08ngQ== tarball: 'file:projects/logger-config.tgz' version: 0.0.0 'file:projects/map-layers.tgz_123a069461c022655d11fe624d233491': @@ -21087,7 +21062,7 @@ packages: sass: '*' webpack-cli: '*' resolution: - integrity: sha512-AROWH8ThdAXlqcCLGP8qFNDMcudv462aZJGwFCIFzAELWNH5ZyRglxyRVpmZv/+NfoUWejXj73zPOZ4fyoJ6ww== + integrity: sha512-a/N4SyoSNDA47nfH9qkSdqMowjwDTaiybSMxMSN0wutejA4ICtJd5mm7ZBUdvUFG3YPJsThY2QS7qAGlAUZjPw== tarball: 'file:projects/map-layers.tgz' version: 0.0.0 'file:projects/mobile-manager.tgz': @@ -21111,7 +21086,7 @@ packages: dev: false name: '@rush-temp/mobile-manager' resolution: - integrity: sha512-/q9PA3hYrQEI4EyehIEV6huM2GrQnGnsYIqb7KSh4MOILce2CFIwPsUFiqfLBjylfSHqKokyrYSNGmOLmewx2g== + integrity: sha512-B2AavOS2F2gVpMIjgQZMVFfzFkzNx227DtytWIP295XWyLlWlkxzQ+LN2Vf9RH0E/+yxBu9dsqRZou1dBO9cgA== tarball: 'file:projects/mobile-manager.tgz' version: 0.0.0 'file:projects/native-app-full-stack-tests.tgz': @@ -21137,7 +21112,7 @@ packages: dev: false name: '@rush-temp/native-app-full-stack-tests' resolution: - integrity: sha512-A7laf2XNohCNHGDqW7yJ3+oBeKSNxJupmVXbZT42EO8fakZRbn1c6OtbWG5hEebCpZdlXg5NCvoFnqr5E2ifAw== + integrity: sha512-H7ZxwlfVaEf25KZWfgy5XO5BaCKG13J90v6O6KSJ77xDKawvj183xCG+x4bmDx8jrCQLzpzgoEJ6XstdANq1Ew== tarball: 'file:projects/native-app-full-stack-tests.tgz' version: 0.0.0 'file:projects/ninezone-test-app.tgz_sass@1.32.8+webpack-cli@3.3.12': @@ -21163,7 +21138,7 @@ packages: sass: '*' webpack-cli: '*' resolution: - integrity: sha512-wX20ue+oJrO7JIV+8jLvOtOx1qMEIls/Tn3vmDmILATz/6NrnuUVR/Sbuad5ISpQ49OFL2el25NpGbP/PX0tjg== + integrity: sha512-8s+I2sbFfvOAN/qmlfKw5wTsUZCFz9pOr2k/Yr574aG0p9/g4U+81a2cPgKvLYXjpInNfBPtpghKiNwH7rTclg== tarball: 'file:projects/ninezone-test-app.tgz' version: 0.0.0 'file:projects/oidc-signin-tool.tgz': @@ -21185,7 +21160,7 @@ packages: dev: false name: '@rush-temp/oidc-signin-tool' resolution: - integrity: sha512-Fst/FcPk9jLeH0Vx4VFqQgz6m3J6G67kuR4J6/IGb5p8kEjuRjgdLp1aEZES4A/brhuZBiR2unXmFVxJjtrnPg== + integrity: sha512-gbj7iOPai7ErMoSV10ECCr7NOnSDoItzmJXyoENtV4S+hQQdHhcoC1uGzEb5fQq26OjQaCCm7IJzSjZrT0Gosw== tarball: 'file:projects/oidc-signin-tool.tgz' version: 0.0.0 'file:projects/orbitgt-core.tgz': @@ -21205,7 +21180,7 @@ packages: dev: false name: '@rush-temp/orbitgt-core' resolution: - integrity: sha512-1s5mhlQ0BbFvhUe/3jyD6mgHfaHCZnWLIvPEW8+S9pfJJ74+02+82F5qcA4nVpGrDQkETA9DSnMWeqOJd3a/cA== + integrity: sha512-1L3sjoHLK+z+Bgp6Cd92dcrzvSYfyMFm3Mlf62occrQjTVvMvChyBQu918Fb27iHEMU1JuqMQKJOCqpNdGIlww== tarball: 'file:projects/orbitgt-core.tgz' version: 0.0.0 'file:projects/perf-tools.tgz': @@ -21218,7 +21193,7 @@ packages: dev: false name: '@rush-temp/perf-tools' resolution: - integrity: sha512-XWQJhbF2vEdIbapDRcT6nCF94/odQIOj2NhkMfeNZYrACV5SYXI/rIEPzbzIQlFQeYuNMHg/l/lpgSMhRuJ5Og== + integrity: sha512-NagVYFW7VuVEABZ0e123bc+mt8NJ0HGGERUWmp1/38zEkQSXnmAKhi2zA47cYdtkD5mpBc6W4/OZj3QsP31Zgg== tarball: 'file:projects/perf-tools.tgz' version: 0.0.0 'file:projects/physical-material-backend.tgz': @@ -21237,7 +21212,7 @@ packages: dev: false name: '@rush-temp/physical-material-backend' resolution: - integrity: sha512-cwSS2R8ecACOSE7T5z3B2yO9+5jDf3aNpdbfXLT8UFyQvl/cigf4ScUO/DLnSxoJca1prZ6udhQ477xcfOapaw== + integrity: sha512-2u1HxxqN3Pu1tWojLidJoIqAB3uuH1BHbz2dald9tTQtZYUb8vAfGkcZDNNt16J2mmb0CUyJdWXrbJv5/xRrhQ== tarball: 'file:projects/physical-material-backend.tgz' version: 0.0.0 'file:projects/presentation-backend.tgz': @@ -21275,7 +21250,7 @@ packages: dev: false name: '@rush-temp/presentation-backend' resolution: - integrity: sha512-vUrJ/xtuvx7lwPUToISVBlIyrClvlxaAUE7ppSAKEdRxfXoOWYWUnh+dLgS9XooCe/ZtAZ/138cHt5Cs+Txq2Q== + integrity: sha512-ShN/cyEKFoBNNYa3n84p4VJegfbZQnLXnPZs7DyypDK7buDAfapfYciocQ3sqg7fJt+majXPbgyktWVYYnereQ== tarball: 'file:projects/presentation-backend.tgz' version: 0.0.0 'file:projects/presentation-common.tgz': @@ -21310,7 +21285,7 @@ packages: dev: false name: '@rush-temp/presentation-common' resolution: - integrity: sha512-Uel5ahki/5m9F3jVrpzcuBKYokggFapv5H9TUHZSvTkuRI5ynFduyMT+vvehmG6aXI3/4oyq5JmLMYd2Pa1cFQ== + integrity: sha512-C+Kdlj4dlN0hHOPQxM3V5dm6OcYVeFu3EitashHqKT6iInRGawH+NaMoKQQz5iVAaZj/G/WeMAX+PlPSspfckA== tarball: 'file:projects/presentation-common.tgz' version: 0.0.0 'file:projects/presentation-components.tgz_jsdom@11.12.0': @@ -21342,7 +21317,7 @@ packages: eslint: 6.8.0 faker: 4.1.0 ignore-styles: 5.0.1 - immer: 8.0.1 + immer: 9.0.1 jsdom-global: 3.0.2_jsdom@11.12.0 lodash: 4.17.21 micro-memoize: 4.0.9 @@ -21352,7 +21327,7 @@ packages: react-dom: 16.14.0_react@16.14.0 react-test-renderer: 16.14.0_react@16.14.0 rimraf: 3.0.2 - rxjs: 6.6.6 + rxjs: 6.6.7 sinon: 9.2.4 sinon-chai: 3.6.0_chai@4.3.4+sinon@9.2.4 typemoq: 2.1.0 @@ -21364,7 +21339,7 @@ packages: peerDependencies: jsdom: '*' resolution: - integrity: sha512-Ew7LiUDVjzpZKCqawIYTNkbj5l5gESpNWe3O4HEAAqRnCrWylZXiEsr+lQPtkd4qjQJYQeGTaEH+567U4IYp+w== + integrity: sha512-ot8EphWJ6+T+N0UgBrMSjw/w5QxWPppBhfxQLU8niqvxvba45i2CO3FAVJEaNcFHnzvnSC9bQLUOjldcZh4+mg== tarball: 'file:projects/presentation-components.tgz' version: 0.0.0 'file:projects/presentation-frontend.tgz_jsdom@11.12.0': @@ -21403,7 +21378,7 @@ packages: peerDependencies: jsdom: '*' resolution: - integrity: sha512-8F3JNyZqZF/kYo4HsIOXyBJfpfUSxbTwByxIOWiiuMfzJnVkXPgU1N9fWd43BXCY3ad055cRrfPe9Z5m1bP75Q== + integrity: sha512-gt0MdsBNpZmqXlUNdOX+sP4gVFaWVLU3aT5a85w6jyNEUQJtAr4vSPu2TVX8heq5Gc1LmE6yrUyOcq7p+/c9WA== tarball: 'file:projects/presentation-frontend.tgz' version: 0.0.0 'file:projects/presentation-full-stack-tests.tgz_jsdom@11.12.0': @@ -21435,7 +21410,7 @@ packages: eslint: 6.8.0 faker: 4.1.0 ignore-styles: 5.0.1 - immer: 8.0.1 + immer: 9.0.1 jsdom-global: 3.0.2_jsdom@11.12.0 mocha: 5.2.0 react: 16.14.0 @@ -21453,7 +21428,7 @@ packages: peerDependencies: jsdom: '*' resolution: - integrity: sha512-UNJ6/lr/d8A/8c1fJNv8fgksm9gCnAymizEeY7wS/od4Dildh95l/BHT+tKLTiUk2gEu9iVZg6t9FAhlNdI4EA== + integrity: sha512-n76kvKkQ0dr9DREQveVk+EgL1e01CMLeWWGWRE+7+Jk73loOAy9hZyVUCTgG1+ULUlHiORBY6lPQPvvK1j+GIg== tarball: 'file:projects/presentation-full-stack-tests.tgz' version: 0.0.0 'file:projects/presentation-test-app.tgz_webpack-cli@3.3.12': @@ -21486,7 +21461,7 @@ packages: peerDependencies: webpack-cli: '*' resolution: - integrity: sha512-koE1mc6Bh9m+YdpTEdmFfq/lLY8ZORF3NUGMA9iixzaLsd8AhMiaS2f9x516pYrdEny5Wugtn5m3T/2t5RLPFg== + integrity: sha512-W7JF2DZ6Ou8/tNY2R9XD3Pd05XOadFETAvJPgxSFSzKnrYpgOSNVlymg3vjnnsDgGpm8P98mLh4zlXTPfptB7g== tarball: 'file:projects/presentation-test-app.tgz' version: 0.0.0 'file:projects/presentation-testing.tgz_jsdom@11.12.0': @@ -21519,7 +21494,7 @@ packages: peerDependencies: jsdom: '*' resolution: - integrity: sha512-NvStnoOY8WttWoioeuGMEYkCM9Bkpblr//m9aVDs2Yuv1HMM1Zknwne3WDbsyg5F+iM3UAoe8dVv6XpPD1ZRtQ== + integrity: sha512-xgMRUIAmuWihpldKaOXZJ6Q5TPeeE7/84AChML8SKKjFYYWiWzkXV6mikj4kPBcw7vJF+Jq3ltTmL1SOjVhWBw== tarball: 'file:projects/presentation-testing.tgz' version: 0.0.0 'file:projects/product-settings-client.tgz': @@ -21538,7 +21513,7 @@ packages: dev: false name: '@rush-temp/product-settings-client' resolution: - integrity: sha512-R90klofEMUEsQ/Q18fSw9H2U64PsW0NW9p7jpsXzCaksnvQQjGLEK7gLp1ea8817ZlAwdP4uBstgBn/TvAbddw== + integrity: sha512-K0AO2I2zPNjlhYJZdAl5/qq1mSXsStNOnIqPiE502T4HNJu1S89nnOtyu1iIBYwAySe81fyoR49CMrP667l65w== tarball: 'file:projects/product-settings-client.tgz' version: 0.0.0 'file:projects/projectshare-client.tgz': @@ -21557,7 +21532,7 @@ packages: dev: false name: '@rush-temp/projectshare-client' resolution: - integrity: sha512-xTPXCdjLjV+tCI/luyw1eQ30PE1II87SXx1bIFjEzBwuEY/5ZwLQwHdBIa5H18kBlW+cK7n7P+Ae/vH/HT8J0g== + integrity: sha512-9Kj9IsU0STy+wJwcDnrtLzXK8IWiVQ2wfjVUli4RRyrP9Ltvgcm0h4ZL4Yy9HKBnZhYdDmf3jYlIX/GTjvhjhw== tarball: 'file:projects/projectshare-client.tgz' version: 0.0.0 'file:projects/rbac-client.tgz': @@ -21576,7 +21551,7 @@ packages: dev: false name: '@rush-temp/rbac-client' resolution: - integrity: sha512-kazS/OrFDj4nwGvfuRzml32UKZn6fz31MW2DRt6LhEFHUlvBmPG5hr7lORjdDWhIETfdCohUwQHHGVzszyYy3A== + integrity: sha512-CLZRGz8clbICn93xeAAs6b0g1OWatVSDXdaSYjZxzt9BhxObNeTeo6l4lcIzBedKz3IvQqA8VSSpnl37U7zG/A== tarball: 'file:projects/rbac-client.tgz' version: 0.0.0 'file:projects/reality-data-client.tgz': @@ -21595,7 +21570,7 @@ packages: dev: false name: '@rush-temp/reality-data-client' resolution: - integrity: sha512-TFhQyGo5RZ9Lf1zJ2UcjD6oiQflyakn8RjrlkLRg0W/Vh2oAhStFEOf7b1DzAW4/fhs8J4fGbHvsLc35UR77FQ== + integrity: sha512-MXcOYiLRMCh8BzUl0RBVacKwQXO/XgJOnQKlId2DDiPPvDvTdZlqYgqxZrxagaCtFIcvvw0zlZ683tssSzTFVg== tarball: 'file:projects/reality-data-client.tgz' version: 0.0.0 'file:projects/rpc-full-stack-tests.tgz': @@ -21621,7 +21596,7 @@ packages: dev: false name: '@rush-temp/rpc-full-stack-tests' resolution: - integrity: sha512-IEkGaW3A4RDw+++DL5/QI5N8FnjteFXkE/oBE8RqRD+RfWKmZMaKHIieUK4Qn8S3aLPFKazRkvAehqlhhFxP8Q== + integrity: sha512-mdwlUzvA6eUiKGbv2gaNgiry+pnSrvRxZGabNrSXEs/tqh5UJH4dPTd+ZcBxiuqCEySR37IpDNYuVgApeRjflA== tarball: 'file:projects/rpc-full-stack-tests.tgz' version: 0.0.0 'file:projects/rpcinterface-full-stack-tests.tgz': @@ -21651,7 +21626,7 @@ packages: dev: false name: '@rush-temp/rpcinterface-full-stack-tests' resolution: - integrity: sha512-vkWYqmpriM4NRPKIRjZDex8/AAhu2XMC3vLrbN++Ty2EyEUKJ9sIU4F3xnkJu0FX9FeEBeFO2zdL/gDezzbtmA== + integrity: sha512-KEF+rF2kEu32lL2Sa8ewOzc0wnAK4G4UcYop2j6rSpDzKSjas7bs6kk7Jh+DP6FTQ6nVoIlYO+NFk8hbVk6HjQ== tarball: 'file:projects/rpcinterface-full-stack-tests.tgz' version: 0.0.0 'file:projects/telemetry-client.tgz': @@ -21670,7 +21645,7 @@ packages: dev: false name: '@rush-temp/telemetry-client' resolution: - integrity: sha512-jyiIqZ8PJHafmPmFCnHNKQEH+PjXuepRwU8Q4IomPaiQABYAgJvoTEllnk6YGrpN6BrfBnP9wR9uaj3N+dQHCQ== + integrity: sha512-FcIldegBjaTIGmkPWKUzUhuy0q4qPD0utkRSMGgzBAMncvugvjh8i6ZufvA0SsALZgpTBpmTuLOTB4AiHs309w== tarball: 'file:projects/telemetry-client.tgz' version: 0.0.0 'file:projects/test-apps-analysis-importer.tgz': @@ -21689,7 +21664,7 @@ packages: dev: false name: '@rush-temp/test-apps-analysis-importer' resolution: - integrity: sha512-EVX3o+GAg8XIsi41NqGzbahtF3qZvj476N8NLZbQ6Rgp89PRxmIyqM0q3zJ5ewQIKDhgawyTVVaj8RUR2uvBhQ== + integrity: sha512-AC9C4fWw0N+tba/EtINaHkcdEkcp0BcqL4fbgEFj2WfDCz9IbcR9UgCaT6NXuBCn9SD27S59SV0ggd7etAQesA== tarball: 'file:projects/test-apps-analysis-importer.tgz' version: 0.0.0 'file:projects/test-apps-synchro-schedule-importer.tgz': @@ -21707,7 +21682,7 @@ packages: dev: false name: '@rush-temp/test-apps-synchro-schedule-importer' resolution: - integrity: sha512-hzGBltGootas0CNpdICDTlmQaVKhYMO5HUo72HVELa73wULiB8gjvqXMe5gMLQB1LGwlhW+N7KEdIsB/F9QiMQ== + integrity: sha512-etdJ9GPHrcwo+Lf0pNGLZRQFtuckdZJV+lqkTr5bMNNX648U+bZIRJ4Nj3jO58aFZ/59esuxMTABioCb7Y7NDw== tarball: 'file:projects/test-apps-synchro-schedule-importer.tgz' version: 0.0.0 'file:projects/ui-abstract.tgz': @@ -21743,7 +21718,7 @@ packages: dev: false name: '@rush-temp/ui-abstract' resolution: - integrity: sha512-tFKCJxc3tM6vaTO7FI092gHUDK6iZdrJMwkbvdsPYVP6VWQ9GzTLAKZzmwuWDTwZNQRLEfFw2WpNh2TVcIK30w== + integrity: sha512-Xb0wI80D1ZuffYro7YpCnmPxNKP6BOzuTE6SmL+3Zuv3QEGDzmyoZlIhM3KJA1Af4ghNsNjqmDpeOk+bZbQXxA== tarball: 'file:projects/ui-abstract.tgz' version: 0.0.0 'file:projects/ui-components.tgz': @@ -21790,7 +21765,7 @@ packages: eventemitter2: 5.0.1 faker: 4.1.0 ignore-styles: 5.0.1 - immer: 8.0.1 + immer: 9.0.1 immutable: 3.8.2 inspire-tree: 5.0.2 jsdom: 11.12.0 @@ -21817,7 +21792,7 @@ packages: react-virtualized-auto-sizer: 1.0.5_react-dom@16.14.0+react@16.14.0 react-window: 1.8.6_react-dom@16.14.0+react@16.14.0 rimraf: 3.0.2 - rxjs: 6.6.6 + rxjs: 6.6.7 shortid: 2.2.16 sinon: 9.2.4 sinon-chai: 3.6.0_chai@4.3.4+sinon@9.2.4 @@ -21830,7 +21805,7 @@ packages: dev: false name: '@rush-temp/ui-components' resolution: - integrity: sha512-h/shUM+Kf6koztCFezdO6jFtolETdWV/Ck4KkZ+9Fv/b/UW9c1/2FgiCYSdbKlZwTk5W2TC9RwC3821dce2WyQ== + integrity: sha512-YqrZ+vbLU4tYVLDmeurWtjlWksB4L6p03luGcMh5kabE1Cc9zRsMZgpUP2QwUmshXpeQ9OFeeVu2KZaGnCTI6A== tarball: 'file:projects/ui-components.tgz' version: 0.0.0 'file:projects/ui-core.tgz_2f645c1465aea7f802b6a9a8594dc28f': @@ -21898,7 +21873,7 @@ packages: webpack: '*' webpack-cli: '*' resolution: - integrity: sha512-z+MxeQ12v/1P1fZFoaL+oeMgdAUshB9tD78uQw/xYh0fNayKNn5eqX3A87GQ2S+tGDGwkPZE6kgaCAobNjZR/A== + integrity: sha512-FuCnviyFd7fGReWKyUv3fgJ83G4sF7R4BdyJTyvaeGy6jR7jyB8OvkF1d483opd4vJLI/W3COjqg4aErQi5cfA== tarball: 'file:projects/ui-core.tgz' version: 0.0.0 'file:projects/ui-framework.tgz_2f645c1465aea7f802b6a9a8594dc28f': @@ -21938,7 +21913,7 @@ packages: eslint: 6.8.0 faker: 4.1.0 ignore-styles: 5.0.1 - immer: 8.0.1 + immer: 9.0.1 jsdom: 11.12.0 jsdom-global: 3.0.2_jsdom@11.12.0 lodash: 4.17.21 @@ -21955,7 +21930,7 @@ packages: react-test-renderer: 16.14.0_react@16.14.0 redux: 4.0.5 rimraf: 3.0.2 - rxjs: 6.6.6 + rxjs: 6.6.7 sinon: 9.2.4 sinon-chai: 3.6.0_chai@4.3.4+sinon@9.2.4 svg-sprite-loader: 4.2.1_webpack@4.42.0 @@ -21972,7 +21947,7 @@ packages: webpack: '*' webpack-cli: '*' resolution: - integrity: sha512-NK/JKuiwvygLCf2hHmg/2mQFyPXZiP7EKr5+SFi2ojtB2WjErvKEZW4+ZynHMIBYBFxISaPtWXyKOJy88YgDHw== + integrity: sha512-XZlWZ3tQ1S14HrFB84u6pZEz3XRqAx/iQbtVYUM7mbJuSByTHKj0MH1SQ2eURh/zdLFrWFFuubkgEcLMNaubWw== tarball: 'file:projects/ui-framework.tgz' version: 0.0.0 'file:projects/ui-ninezone.tgz_react-test-renderer@16.14.0': @@ -22002,7 +21977,7 @@ packages: enzyme-to-json: 3.6.1_enzyme@3.11.0 eslint: 6.8.0 ignore-styles: 5.0.1 - immer: 8.0.1 + immer: 9.0.1 jsdom: 11.12.0 jsdom-global: 3.0.2_jsdom@11.12.0 mocha: 5.2.0 @@ -22023,7 +21998,7 @@ packages: peerDependencies: react-test-renderer: '*' resolution: - integrity: sha512-IjQ5ht1NuvjXn5zxFiwBZD+9f1heVtm0VMBf4V30Bl46jHfBpNDabezxoPzJC6fxPC+j3mF7KI9Huo6/4o4DQQ== + integrity: sha512-1KDG9DSF5KELihkNGrkLMLoplJRA3ceE9h4TDLY70+gaUPmWKrWgEjqhUHM0SxfblnmIv7BtHbV/k9NkaMeFpQ== tarball: 'file:projects/ui-ninezone.tgz' version: 0.0.0 'file:projects/ui-test-app.tgz_webpack-cli@3.3.12': @@ -22071,7 +22046,7 @@ packages: peerDependencies: webpack-cli: '*' resolution: - integrity: sha512-tlqwKM749iI6BFF26Xi7P4NnJUQmfWqcsrClNYkUp9UghZYFkabGppKaftd1zSTyFmkVkGgdeKEL0+kq7ujpnA== + integrity: sha512-Ip8YbjX0T3nz/MXI3Ocn3zP5O6iq6vCcqwNcle86DcNg6nBgMAL/ngH1GtlHsZihIWXMZAqq1XmCIDS2UZ/iQw== tarball: 'file:projects/ui-test-app.tgz' version: 0.0.0 'file:projects/ui-test-extension.tgz_webpack@4.42.0': @@ -22095,7 +22070,7 @@ packages: peerDependencies: webpack: '*' resolution: - integrity: sha512-geimJNnYWZaLFTReKASFzk6W+SCCmn6OoqGtST2sDMjmgKe3vuf8Q7p0dvNyaU+rvTqYHmNit/yTZ7AePjymJA== + integrity: sha512-VVzmLHAwaBMcrMo9WjUtMX2x930bJb1LNugGd0ezjtt4Ysm8kw0fyBQ+Nx/z+pKQcEGVOTbT4Dj14OM8hVnEnw== tarball: 'file:projects/ui-test-extension.tgz' version: 0.0.0 'file:projects/usage-logging-client.tgz': @@ -22114,7 +22089,7 @@ packages: dev: false name: '@rush-temp/usage-logging-client' resolution: - integrity: sha512-HlBqYbJ/aink9KVkyUSefNLC91spX/coXnmoP+iciBCiRJ8patMTCQA6Fod5JxlX6JapiWJglceO3Rj7usQvKQ== + integrity: sha512-Nyk+6/kTIzhJJxvhUbAokXDZLWedgqyx3RWcObk4EIhFC8mlHLBK74Exgk4UuhHt9K4LKfKBxCQSohwQFXqddg== tarball: 'file:projects/usage-logging-client.tgz' version: 0.0.0 'file:projects/webgl-compatibility.tgz': @@ -22133,7 +22108,7 @@ packages: dev: false name: '@rush-temp/webgl-compatibility' resolution: - integrity: sha512-hqQkveP2sf4XGHPBupmwcix/f0ZPlRdsN1bz5OuUYBRLb9JlkcxErsEZs3ytwEe3gbGDQiw0idBKBUJnwbXJLA== + integrity: sha512-swC/3XhTSTodSRNX7GrKKc5/BtA2dl//LzbqLbkGcDknCQAaqiGaO1TK2INj7SAmI+/mob5r1rhh7Sv4KLh9Ow== tarball: 'file:projects/webgl-compatibility.tgz' version: 0.0.0 'file:projects/webpack-tools-core.tgz': @@ -22141,7 +22116,7 @@ packages: '@types/fs-extra': 4.0.11 '@types/glob': 5.0.36 '@types/node': 10.14.1 - '@types/webpack': 4.41.26 + '@types/webpack': 4.41.27 '@types/webpack-sources': 0.1.8 chalk: 3.0.0 cpx: 1.5.0 @@ -22161,7 +22136,7 @@ packages: dev: false name: '@rush-temp/webpack-tools-core' resolution: - integrity: sha512-qjK/WLrhbCoOmujIGNoh0n5AFS3Itd+or1LTvVdjfmoIrgeFpbIZdO9qGggc3wjDnzq12F/wGY6nz8OKsG4HCg== + integrity: sha512-Cnlys5DIdM9KRrSnB2Sd+rmpbH9cB6IYLTb37itdXqBDPKD4ggNC2S2YoFxzdbaSAuLjICFGxl054JtOrnxLkA== tarball: 'file:projects/webpack-tools-core.tgz' version: 0.0.0 specifiers: @@ -22171,7 +22146,7 @@ specifiers: '@babel/core': 7.7.4 '@bentley/icons-generic': ^1.0.15 '@bentley/icons-generic-webfont': ^1.0.15 - '@bentley/imodeljs-native': 2.15.0 + '@bentley/imodeljs-native': 2.15.1 '@bentley/react-scripts': 3.4.9 '@bentley/units-schema': ^1.0.5 '@microsoft/api-extractor': 7.7.3 @@ -22345,7 +22320,7 @@ specifiers: '@types/webpack': ^4.41.2 '@types/webpack-sources': ^0.1.6 '@types/ws': ^6.0.4 - '@types/xmldom': ^0.1.29 + '@types/xmldom': ^0.1.30 '@types/yargs': ^12.0.5 '@typescript-eslint/eslint-plugin': 4.11.1 '@typescript-eslint/parser': 4.11.1 @@ -22425,7 +22400,7 @@ specifiers: i18next-node-fs-backend: ^2.1.3 i18next-xhr-backend: ^2.0.1 ignore-styles: ^5.0.1 - immer: 8.0.1 + immer: ^9.0.1 immutable: ^3.8.2 inspire-tree: ^5.0.1 istanbul-instrumenter-loader: ^3.0.1 @@ -22436,7 +22411,6 @@ specifiers: json5: ^2.1.0 jsonc-parser: ~2.0.3 jsonwebtoken: ^8.5.0 - ldclient-js: ^2.6.0 linkify-it: ~2.2.0 lint-staged: ^10.2.11 lodash: ^4.17.10 @@ -22553,7 +22527,7 @@ specifiers: wms-capabilities: 0.4.0 ws: ^7.2.0 xml-js: ~1.6.11 - xmldom: ^0.1.27 + xmldom: ^0.5.0 xmlhttprequest: ^1.8.0 xpath: 0.0.27 yargonaut: ^1.1.2 diff --git a/common/config/rush/version-policies.json b/common/config/rush/version-policies.json index dfeea262195..d97f31a6b3b 100644 --- a/common/config/rush/version-policies.json +++ b/common/config/rush/version-policies.json @@ -2,7 +2,7 @@ { "policyName": "prerelease-monorepo-lockStep", "definitionName": "lockStepVersion", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "nextBump": "prerelease" } ] diff --git a/core/backend-itwin-client/package.json b/core/backend-itwin-client/package.json index a9620a1b097..e20880f5f41 100644 --- a/core/backend-itwin-client/package.json +++ b/core/backend-itwin-client/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/backend-itwin-client", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "Clients for various Bentley Services used by iModel.js at the backend", "main": "lib/backend-itwin-client.js", "browser": { @@ -38,33 +38,33 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/frontend-authorization-client": "^2.15.0-dev.6", - "@bentley/geometry-core": "^2.15.0-dev.6", - "@bentley/imodelhub-client": "^2.15.0-dev.6", - "@bentley/imodeljs-common": "^2.15.0-dev.6", - "@bentley/itwin-client": "^2.15.0-dev.6", - "@bentley/rbac-client": "^2.15.0-dev.6", - "@bentley/telemetry-client": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/frontend-authorization-client": "^2.15.0-dev.13", + "@bentley/geometry-core": "^2.15.0-dev.13", + "@bentley/imodelhub-client": "^2.15.0-dev.13", + "@bentley/imodeljs-common": "^2.15.0-dev.13", + "@bentley/itwin-client": "^2.15.0-dev.13", + "@bentley/rbac-client": "^2.15.0-dev.13", + "@bentley/telemetry-client": "^2.15.0-dev.13" }, "//devDependencies": [ "NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install", "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/context-registry-client": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/frontend-authorization-client": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodelhub-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/oidc-signin-tool": "2.15.0-dev.6", - "@bentley/rbac-client": "2.15.0-dev.6", - "@bentley/telemetry-client": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/context-registry-client": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/frontend-authorization-client": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodelhub-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/oidc-signin-tool": "2.15.0-dev.13", + "@bentley/rbac-client": "2.15.0-dev.13", + "@bentley/telemetry-client": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/deep-assign": "^0.1.0", "@types/fs-extra": "^4.0.7", @@ -91,7 +91,7 @@ }, "dependencies": { "@azure-tools/azcopy-node": "^2.0.0", - "@bentley/usage-logging-client": "2.15.0-dev.6", + "@bentley/usage-logging-client": "2.15.0-dev.13", "applicationinsights": "^1.7.5", "deep-assign": "^2.0.0", "fs-extra": "^8.1.0", diff --git a/core/backend/package.json b/core/backend/package.json index f5d08bc33d7..a3614b9dbfd 100644 --- a/core/backend/package.json +++ b/core/backend/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/imodeljs-backend", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js backend components", "main": "lib/imodeljs-backend.js", "typings": "lib/imodeljs-backend", @@ -55,35 +55,35 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/backend-itwin-client": "^2.15.0-dev.6", - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/ecschema-metadata": "^2.15.0-dev.6", - "@bentley/geometry-core": "^2.15.0-dev.6", - "@bentley/imodelhub-client": "^2.15.0-dev.6", - "@bentley/imodeljs-common": "^2.15.0-dev.6", - "@bentley/itwin-client": "^2.15.0-dev.6", - "@bentley/rbac-client": "^2.15.0-dev.6", - "@bentley/telemetry-client": "^2.15.0-dev.6" + "@bentley/backend-itwin-client": "^2.15.0-dev.13", + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/ecschema-metadata": "^2.15.0-dev.13", + "@bentley/geometry-core": "^2.15.0-dev.13", + "@bentley/imodelhub-client": "^2.15.0-dev.13", + "@bentley/imodeljs-common": "^2.15.0-dev.13", + "@bentley/itwin-client": "^2.15.0-dev.13", + "@bentley/rbac-client": "^2.15.0-dev.13", + "@bentley/telemetry-client": "^2.15.0-dev.13" }, "//devDependencies": [ "NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install", "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/backend-itwin-client": "2.15.0-dev.6", - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/ecschema-metadata": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodelhub-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/oidc-signin-tool": "2.15.0-dev.6", - "@bentley/perf-tools": "2.15.0-dev.6", - "@bentley/rbac-client": "2.15.0-dev.6", - "@bentley/telemetry-client": "2.15.0-dev.6", + "@bentley/backend-itwin-client": "2.15.0-dev.13", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/ecschema-metadata": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodelhub-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/oidc-signin-tool": "2.15.0-dev.13", + "@bentley/perf-tools": "2.15.0-dev.13", + "@bentley/rbac-client": "2.15.0-dev.13", + "@bentley/telemetry-client": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", "@types/deep-assign": "^0.1.0", @@ -119,10 +119,10 @@ "webpack": "4.42.0" }, "dependencies": { - "@bentley/imodeljs-native": "2.15.0", + "@bentley/imodeljs-native": "2.15.1", "@azure/storage-blob": "10.4.0", - "@bentley/context-registry-client": "2.15.0-dev.6", - "@bentley/usage-logging-client": "2.15.0-dev.6", + "@bentley/context-registry-client": "2.15.0-dev.13", + "@bentley/usage-logging-client": "2.15.0-dev.13", "deep-assign": "^2.0.0", "form-data": "^2.3.2", "fs-extra": "^8.1.0", diff --git a/core/backend/src/BriefcaseManager.ts b/core/backend/src/BriefcaseManager.ts index 97ee7a5a657..a8286d615cf 100644 --- a/core/backend/src/BriefcaseManager.ts +++ b/core/backend/src/BriefcaseManager.ts @@ -86,7 +86,7 @@ export class BriefcaseManager { } /** @internal */ - public static getChangeSetFolderNameFromId(changeSetId: GuidString): string { + public static getChangeSetFolderNameFromId(changeSetId: string): string { return changeSetId || this._firstChangeSetDir; } @@ -155,8 +155,9 @@ export class BriefcaseManager { if (briefcaseName.endsWith(".bim")) { try { const fileName = path.join(bcPath, briefcaseName); + const fileSize = IModelJsFs.lstatSync(fileName)?.size ?? 0; const db = IModelDb.openDgnDb({ path: fileName }, OpenMode.Readonly); - briefcaseList.push({ fileName, contextId: db.queryProjectGuid(), iModelId: db.getDbGuid(), briefcaseId: db.getBriefcaseId(), changeSetId: db.getParentChangeSetId() }); + briefcaseList.push({ fileName, contextId: db.queryProjectGuid(), iModelId: db.getDbGuid(), briefcaseId: db.getBriefcaseId(), changeSetId: db.getParentChangeSetId(), fileSize }); db.closeIModel(); } catch (_err) { } @@ -171,7 +172,7 @@ export class BriefcaseManager { public static get cacheDir() { return this._cacheDir; } /** Get the index of the change set from its id */ - private static async getChangeSetIndexFromId(requestContext: AuthorizedClientRequestContext, iModelId: GuidString, changeSetId: GuidString): Promise { + private static async getChangeSetIndexFromId(requestContext: AuthorizedClientRequestContext, iModelId: GuidString, changeSetId: string): Promise { requestContext.enter(); if (changeSetId === "") return 0; // the first version @@ -185,7 +186,7 @@ export class BriefcaseManager { /** Determine whether the supplied briefcaseId is a standalone briefcase */ public static isStandaloneBriefcaseId(id: BriefcaseId) { // eslint-disable-next-line deprecation/deprecation - return id === BriefcaseIdValue.Standalone || id === BriefcaseIdValue.DeprecatedStandalone; + return id === BriefcaseIdValue.Unassigned || id === BriefcaseIdValue.DeprecatedStandalone; } /** Determine whether the supplied briefcaseId is in the range of BriefcaseIds issued by iModelHub @@ -229,7 +230,7 @@ export class BriefcaseManager { * a filename, the local briefcase cache is used by creating a file with the briefcaseId as its name in the `briefcases` folder below the folder named * for the IModelId. * @note *It is invalid to edit briefcases on a shared network drive* and that is a sure way to corrupt your briefcase (see https://www.sqlite.org/howtocorrupt.html) - * @note The special briefcaseId [[BriefcaseIdValue.Standalone]] (0) can be used for a local briefcase that can accept changesets but may not be changed locally. + * @note The special briefcaseId [[BriefcaseIdValue.Unassigned]] (0) can be used for a local briefcase that can accept changesets but may not be changed locally. * @see CheckpointManager.downloadCheckpoint */ public static async downloadBriefcase(requestContext: AuthorizedClientRequestContext, request: RequestNewBriefcaseArg): Promise { @@ -249,12 +250,14 @@ export class BriefcaseManager { }; await CheckpointManager.downloadCheckpoint(args); + const fileSize = IModelJsFs.lstatSync(fileName)?.size ?? 0; const response: LocalBriefcaseProps = { fileName, briefcaseId, iModelId: request.iModelId, contextId: request.contextId, changeSetId: args.checkpoint.changeSetId, + fileSize, }; // now open the downloaded checkpoint and reset its BriefcaseId diff --git a/core/backend/src/ChangeSummaryManager.ts b/core/backend/src/ChangeSummaryManager.ts index 2cf1c7fa64b..3be1a215265 100644 --- a/core/backend/src/ChangeSummaryManager.ts +++ b/core/backend/src/ChangeSummaryManager.ts @@ -151,10 +151,10 @@ export class ChangeSummaryManager { const ctx = new ChangeSummaryExtractContext(iModel); - const endChangeSetId: GuidString = iModel.changeSetId; + const endChangeSetId = iModel.changeSetId; assert(endChangeSetId.length !== 0); - let startChangeSetId: GuidString = ""; + let startChangeSetId = ""; if (options) { if (options.startVersion) { startChangeSetId = await options.startVersion.evaluateChangeSet(requestContext, ctx.iModelId, IModelHost.iModelClient); @@ -199,7 +199,7 @@ export class ChangeSummaryManager { const summaries: Id64String[] = []; for (let i = endChangeSetIx; i >= 0; i--) { const currentChangeSetInfo: ChangeSet = changeSetInfos[i]; - const currentChangeSetId: GuidString = currentChangeSetInfo.wsgId; + const currentChangeSetId: string = currentChangeSetInfo.wsgId; Logger.logInfo(loggerCategory, `Started Change Summary extraction for changeset #${i + 1}...`, () => ({ iModelId: ctx.iModelId, changeSetId: currentChangeSetId })); const existingSummaryId: Id64String | undefined = ChangeSummaryManager.isSummaryAlreadyExtracted(changesFile, currentChangeSetId); @@ -264,7 +264,7 @@ export class ChangeSummaryManager { public static async downloadChangeSets(requestContext: AuthorizedClientRequestContext, ctx: ChangeSummaryExtractContext, startChangeSetId: GuidString, endChangeSetId: GuidString): Promise { requestContext.enter(); // Get the change set before the startChangeSet so that startChangeSet is included in the download and processing - let beforeStartChangeSetId: GuidString; + let beforeStartChangeSetId: string; if (startChangeSetId.length === 0) beforeStartChangeSetId = ""; else { diff --git a/core/backend/src/ChangedElementsDb.ts b/core/backend/src/ChangedElementsDb.ts index 4e0d7b01461..7f93d7a3438 100644 --- a/core/backend/src/ChangedElementsDb.ts +++ b/core/backend/src/ChangedElementsDb.ts @@ -7,7 +7,7 @@ */ import * as path from "path"; -import { DbResult, GuidString, IDisposable, IModelStatus, OpenMode } from "@bentley/bentleyjs-core"; +import { DbResult, IDisposable, IModelStatus, OpenMode } from "@bentley/bentleyjs-core"; import { ChangeSet } from "@bentley/imodelhub-client"; import { ChangeData, ChangedElements, ChangedModels, IModelError } from "@bentley/imodeljs-common"; import { IModelJsNative } from "@bentley/imodeljs-native"; @@ -144,7 +144,7 @@ export class ChangedElementsDb implements IDisposable { * @returns Returns the changed elements between the changesets provided * @throws [IModelError]($common) if the operation failed. */ - public getChangedElements(startChangesetId: GuidString, endChangesetId: GuidString): ChangedElements | undefined { + public getChangedElements(startChangesetId: string, endChangesetId: string): ChangedElements | undefined { const result: IModelJsNative.ErrorStatusOrResult = this.nativeDb.getChangedElements(startChangesetId, endChangesetId); if (result.error || !result.result) throw new IModelError(result.error ? result.error.status : -1, result.error ? result.error.message : "Problem getting changed elements"); @@ -157,7 +157,7 @@ export class ChangedElementsDb implements IDisposable { * @returns Returns the changed models between the changesets provided * @throws [IModelError]($common) if the operation failed. */ - public getChangedModels(startChangesetId: GuidString, endChangesetId: GuidString): ChangedModels | undefined { + public getChangedModels(startChangesetId: string, endChangesetId: string): ChangedModels | undefined { const result: IModelJsNative.ErrorStatusOrResult = this.nativeDb.getChangedElements(startChangesetId, endChangesetId); if (result.error || !result.result) throw new IModelError(result.error ? result.error.status : -1, result.error ? result.error.message : "Problem getting changed models"); @@ -170,7 +170,7 @@ export class ChangedElementsDb implements IDisposable { * @returns Returns the changed models between the changesets provided * @throws [IModelError]($common) if the operation failed. */ - public getChangeData(startChangesetId: GuidString, endChangesetId: GuidString): ChangeData | undefined { + public getChangeData(startChangesetId: string, endChangesetId: string): ChangeData | undefined { const result: IModelJsNative.ErrorStatusOrResult = this.nativeDb.getChangedElements(startChangesetId, endChangesetId); if (result.error) throw new IModelError(result.error.status, result.error.message); @@ -181,7 +181,7 @@ export class ChangedElementsDb implements IDisposable { public get isOpen(): boolean { return this.nativeDb.isOpen(); } /** Returns true if the cache already contains this changeset Id */ - public isProcessed(changesetId: GuidString): boolean { return this.nativeDb.isProcessed(changesetId); } + public isProcessed(changesetId: string): boolean { return this.nativeDb.isProcessed(changesetId); } /** Close the Db after saving any uncommitted changes. * @throws [IModelError]($common) if the database is not open. diff --git a/core/backend/src/CheckpointManager.ts b/core/backend/src/CheckpointManager.ts index b48dee985c6..88fdefe87f4 100644 --- a/core/backend/src/CheckpointManager.ts +++ b/core/backend/src/CheckpointManager.ts @@ -14,7 +14,7 @@ import { } from "@bentley/bentleyjs-core"; import { CheckpointQuery, CheckpointV2Query } from "@bentley/imodelhub-client"; import { BriefcaseIdValue, DownloadBriefcaseStatus, IModelError } from "@bentley/imodeljs-common"; -import { BlobDaemon, BlobDaemonCommandArg } from "@bentley/imodeljs-native"; +import { BlobDaemon, BlobDaemonCommandArg, IModelJsNative } from "@bentley/imodeljs-native"; import { AuthorizedClientRequestContext, ProgressCallback, UserCancelledError } from "@bentley/itwin-client"; import { BackendLoggerCategory } from "./BackendLoggerCategory"; import { BriefcaseManager } from "./BriefcaseManager"; @@ -37,8 +37,10 @@ export interface CheckpointProps { /** Id of the iModel */ iModelId: GuidString; - /** Id of the change set */ - changeSetId: GuidString; + /** Id of the change set + * @note ChangeSet Ids are string hash values based on the ChangeSet's content and parent. + */ + changeSetId: string; requestContext: AuthorizedClientRequestContext; } @@ -48,7 +50,7 @@ export interface CheckpointProps { export type ProgressFunction = (loaded: number, total: number) => number; /** The parameters that specify a request to download a checkpoint file from iModelHub. - * @beta + * @internal */ export interface DownloadRequest { /** name of local file to hold the downloaded data. */ @@ -262,8 +264,8 @@ export class V1CheckpointManager { nativeDb.deleteAllTxns(); } - if (nativeDb.getBriefcaseId() !== BriefcaseIdValue.Standalone) - nativeDb.resetBriefcaseId(BriefcaseIdValue.Standalone); + if (nativeDb.getBriefcaseId() !== BriefcaseIdValue.Unassigned) + nativeDb.resetBriefcaseId(BriefcaseIdValue.Unassigned); // Validate the native briefcase against the checkpoint meta-data try { @@ -271,17 +273,7 @@ export class V1CheckpointManager { if (dbChangeSetId !== checkpoint.mergedChangeSetId) throw new IModelError(IModelStatus.ValidationFailed, "ParentChangeSetId of the checkpoint was not correctly setup", Logger.logError, loggerCategory, () => ({ ...traceInfo, ...checkpoint, dbChangeSetId })); - const dbGuid = Guid.normalize(nativeDb.getDbGuid()); - if (dbGuid !== Guid.normalize(requestedCkp.iModelId)) { - Logger.logWarning(loggerCategory, "iModelId is not properly setup in the checkpoint. Updated checkpoint to the correct iModelId.", () => ({ ...traceInfo, ...checkpoint, dbGuid })); - nativeDb.setDbGuid(Guid.normalize(requestedCkp.iModelId)); - // Required to reset the ChangeSetId because setDbGuid clears the value. - nativeDb.saveLocalValue("ParentChangeSetId", dbChangeSetId); - } - - const dbContextGuid = Guid.normalize(nativeDb.queryProjectGuid()); - if (dbContextGuid !== Guid.normalize(requestedCkp.contextId)) - throw new IModelError(IModelStatus.ValidationFailed, "ContextId was not properly setup in the checkpoint", Logger.logError, loggerCategory, () => ({ ...traceInfo, dbContextGuid })); + CheckpointManager.validateCheckpointGuids(requestedCkp, nativeDb); // Apply change sets if necessary if (dbChangeSetId !== requestedCkp.changeSetId) { @@ -290,6 +282,7 @@ export class V1CheckpointManager { requestContext.enter(); } } finally { + db.saveChanges(); db.close(); } @@ -343,6 +336,27 @@ export class CheckpointManager { return V1CheckpointManager.downloadCheckpoint(request); } + /** checks a file's dbGuid & contextId for consistency, and updates the dbGuid when possible */ + public static validateCheckpointGuids(checkpoint: CheckpointProps, nativeDb: IModelJsNative.DgnDb) { + const traceInfo = { contextId: checkpoint.contextId, iModelId: checkpoint.iModelId }; + + const dbChangeSetId = nativeDb.getParentChangeSetId(); + const dbGuid = Guid.normalize(nativeDb.getDbGuid()); + if (dbGuid !== Guid.normalize(checkpoint.iModelId)) { + if (nativeDb.isReadonly()) + throw new IModelError(IModelStatus.ValidationFailed, "iModelId is not properly setup in the checkpoint", Logger.logError, loggerCategory, () => ({ ...traceInfo, dbGuid })); + + Logger.logWarning(loggerCategory, "iModelId is not properly setup in the checkpoint. Updated checkpoint to the correct iModelId.", () => ({ ...traceInfo, dbGuid })); + nativeDb.setDbGuid(Guid.normalize(checkpoint.iModelId)); + // Required to reset the ChangeSetId because setDbGuid clears the value. + nativeDb.saveLocalValue("ParentChangeSetId", dbChangeSetId); + } + + const dbContextGuid = Guid.normalize(nativeDb.queryProjectGuid()); + if (dbContextGuid !== Guid.normalize(checkpoint.contextId)) + throw new IModelError(IModelStatus.ValidationFailed, "ContextId was not properly setup in the checkpoint", Logger.logError, loggerCategory, () => ({ ...traceInfo, dbContextGuid })); + } + /** @returns true if the file is the checkpoint requested */ public static verifyCheckpoint(checkpoint: CheckpointProps, fileName: string): boolean { if (!IModelJsFs.existsSync(fileName)) diff --git a/core/backend/src/IModelDb.ts b/core/backend/src/IModelDb.ts index 6950a3fb790..b32a54d57d4 100644 --- a/core/backend/src/IModelDb.ts +++ b/core/backend/src/IModelDb.ts @@ -1447,7 +1447,7 @@ export namespace IModelDb { // eslint-disable-line no-redeclare /** Mark the geometry of [[GeometricModel]] as having changed, by recording an indirect change to its GeometryGuid property. * Typically the GeometryGuid changes automatically when [[GeometricElement]]s within the model are modified, but - * explicitly updating it is occassionally useful after modifying definition elements like line styles or materials that indirectly affect the appearance of + * explicitly updating it is occasionally useful after modifying definition elements like line styles or materials that indirectly affect the appearance of * [[GeometricElement]]s that reference those definition elements in their geometry streams. * @note This will throw IModelError with [IModelStatus.VersionTooOld]($bentleyjs-core) if a version of the BisCore schema older than 1.0.11 is present in the iModel. * @throws IModelError if unable to update the geometry guid. @@ -2254,7 +2254,7 @@ export class BriefcaseDb extends IModelDb { this.concurrencyControl.onSavedChanges(); } - private static async lockSchema(requestContext: AuthorizedClientRequestContext, iModelId: GuidString, changeSetId: GuidString, briefcaseId: number): Promise { + private static async lockSchema(requestContext: AuthorizedClientRequestContext, iModelId: GuidString, changeSetId: string, briefcaseId: number): Promise { requestContext.enter(); const lock = new Lock(); lock.briefcaseId = briefcaseId; @@ -2512,7 +2512,7 @@ export class SnapshotDb extends IModelDb { if (DbResult.BE_SQLITE_OK !== status) throw new IModelError(status, `Could not create snapshot iModel ${filePath}`, Logger.logError, loggerCategory); - status = nativeDb.resetBriefcaseId(BriefcaseIdValue.Standalone); + status = nativeDb.resetBriefcaseId(BriefcaseIdValue.Unassigned); if (DbResult.BE_SQLITE_OK !== status) throw new IModelError(status, `Could not set briefcaseId for snapshot iModel ${filePath}`, Logger.logError, loggerCategory); @@ -2559,7 +2559,7 @@ export class SnapshotDb extends IModelDb { nativeDb.deleteLocalValue(IModelDb._edit); nativeDb.saveChanges(); nativeDb.deleteAllTxns(); - nativeDb.resetBriefcaseId(BriefcaseIdValue.Standalone); + nativeDb.resetBriefcaseId(BriefcaseIdValue.Unassigned); nativeDb.saveChanges(); const snapshotDb = new SnapshotDb(nativeDb, Guid.createValue()); // WIP: clean up copied file on error? if (options?.createClassViews) @@ -2614,6 +2614,12 @@ export class SnapshotDb extends IModelDb { const filePath = await V2CheckpointManager.attach(checkpoint); const snapshot = SnapshotDb.openFile(filePath, { lazyBlockCache: true, key: CheckpointManager.getKey(checkpoint) }); snapshot._contextId = checkpoint.contextId; + try { + CheckpointManager.validateCheckpointGuids(checkpoint, snapshot.nativeDb); + } catch (err) { + snapshot.close(); + throw err; + } return snapshot; } @@ -2642,21 +2648,21 @@ export class SnapshotDb extends IModelDb { } /** Standalone iModels are read/write files that are not managed by iModelHub. - * They are relevant only for single-practitioner scenarios where team collaboration is necessary. - * However, Standalone iModels are designed such that the API interaction between Standalone iModels and Briefcase + * They are relevant only for small-scale single-user scenarios. + * Standalone iModels are designed such that the API for Standalone iModels and Briefcase * iModels (those synchronized with iModelHub) are as similar and consistent as possible. - * This leads to a straightforward process where the practitioner can optionally choose to upgrade to iModelHub. + * This leads to a straightforward process where the a user starts with StandaloneDb and can + * optionally choose to upgrade to iModelHub. * * Some additional details. Standalone iModels: * - always have [Guid.empty]($bentley) for their contextId (they are "unassociated" files) - * - always have BriefcaseId === [BriefcaseIdValue.Standalone]($backend) + * - always have BriefcaseId === [BriefcaseIdValue.Unassigned]($common) * - are connected to the frontend via [BriefcaseConnection.openStandalone]($frontend) * - may be opened without supplying any user credentials * - may be opened read/write - * - may optionally support undo/redo via [[TxmManager]] - * - cannot apply a changeset to nor generate a changesets - * - are only available to authorized applications - * @internal + * - may optionally support undo/redo via [[TxnManager]] + * - cannot apply a changeset to nor generate a changesets (since there is no timeline from which to get/push changesets) + * @public */ export class StandaloneDb extends IModelDb { public get isStandalone(): boolean { return true; } @@ -2700,7 +2706,7 @@ export class StandaloneDb extends IModelDb { nativeDb.saveLocalValue(IModelDb._edit, undefined === args.allowEdit ? "" : args.allowEdit); nativeDb.saveProjectGuid(Guid.empty); - status = nativeDb.resetBriefcaseId(BriefcaseIdValue.Standalone); + status = nativeDb.resetBriefcaseId(BriefcaseIdValue.Unassigned); if (DbResult.BE_SQLITE_OK !== status) { nativeDb.closeIModel(); throw new IModelError(status, `Could not set briefcaseId for iModel: ${filePath}`, Logger.logError, loggerCategory); @@ -2732,6 +2738,7 @@ export class StandaloneDb extends IModelDb { * @param openMode Optional open mode for the standalone iModel. The default is read/write. * @returns a new StandaloneDb if the file is not currently open, and the existing StandaloneDb if it is already * @throws [[IModelError]] if the file is not a standalone iModel. + * @see [BriefcaseConnection.openStandalone]($frontend) to open a StandaloneDb from the frontend */ public static openFile(filePath: string, openMode: OpenMode = OpenMode.ReadWrite, options?: StandaloneOpenOptions): StandaloneDb { const file = { path: filePath, key: options?.key }; diff --git a/core/backend/src/IModelExporter.ts b/core/backend/src/IModelExporter.ts index d00738e5643..77544946410 100644 --- a/core/backend/src/IModelExporter.ts +++ b/core/backend/src/IModelExporter.ts @@ -6,7 +6,7 @@ * @module iModels */ import * as path from "path"; -import { DbResult, GuidString, Id64, Id64String, IModelStatus, Logger } from "@bentley/bentleyjs-core"; +import { DbResult, Id64, Id64String, IModelStatus, Logger } from "@bentley/bentleyjs-core"; import { Schema } from "@bentley/ecschema-metadata"; import { ChangeSet } from "@bentley/imodelhub-client"; import { CodeSpec, FontProps, IModel, IModelError } from "@bentley/imodeljs-common"; @@ -252,7 +252,7 @@ export class IModelExporter { * If this parameter is not provided, then just the current changeset will be exported. * @note To form a range of versions to export, set `startChangeSetId` for the start of the desired range and open the source iModel as of the end of the desired range. */ - public async exportChanges(requestContext: AuthorizedClientRequestContext, startChangeSetId?: GuidString): Promise { + public async exportChanges(requestContext: AuthorizedClientRequestContext, startChangeSetId?: string): Promise { requestContext.enter(); if (!this.sourceDb.isBriefcaseDb()) { throw new IModelError(IModelStatus.BadRequest, "Must be a briefcase to export changes", Logger.logError, loggerCategory); @@ -714,7 +714,7 @@ class ChangedInstanceIds { public relationship = new ChangedInstanceOps(); public font = new ChangedInstanceOps(); private constructor() { } - public static async initialize(requestContext: AuthorizedClientRequestContext, iModelDb: BriefcaseDb, startChangeSetId: GuidString): Promise { + public static async initialize(requestContext: AuthorizedClientRequestContext, iModelDb: BriefcaseDb, startChangeSetId: string): Promise { requestContext.enter(); const extractContext = new ChangeSummaryExtractContext(iModelDb); // NOTE: ChangeSummaryExtractContext is nothing more than a wrapper around IModelDb that has a method to get the iModelId // NOTE: ChangeSummaryManager.downloadChangeSets has nothing really to do with change summaries but has the desired behavior of including the start changeSet (unlike BriefcaseManager.downloadChangeSets) diff --git a/core/backend/src/IModelHost.ts b/core/backend/src/IModelHost.ts index d2592743450..0f2b6bea12c 100644 --- a/core/backend/src/IModelHost.ts +++ b/core/backend/src/IModelHost.ts @@ -448,6 +448,7 @@ export class IModelHost { UsageLoggingUtilities.configure({ hostApplicationId: IModelHost.applicationId, hostApplicationVersion: IModelHost.applicationVersion, clientAuthManager: this._clientAuthIntrospectionManager }); process.once("beforeExit", IModelHost.shutdown); + IModelHost.onAfterStartup.raiseEvent(); } private static _briefcaseCacheDir: string; @@ -487,10 +488,11 @@ export class IModelHost { /** This method must be called when an iModel.js services is shut down. Raises [[onBeforeShutdown]] */ public static async shutdown(): Promise { - if (!this._isValid) + // NB: This method is set as a node listener where `this` is unbound + if (!IModelHost._isValid) return; - this._isValid = false; + IModelHost._isValid = false; IModelHost.onBeforeShutdown.raiseEvent(); IModelHost.platform.shutdown(); IModelHost.configuration = undefined; diff --git a/core/backend/src/IModelTransformer.ts b/core/backend/src/IModelTransformer.ts index 192b1253ca4..29570a795d2 100644 --- a/core/backend/src/IModelTransformer.ts +++ b/core/backend/src/IModelTransformer.ts @@ -6,7 +6,7 @@ * @module iModels */ import * as path from "path"; -import { ClientRequestContext, DbResult, Guid, GuidString, Id64, Id64Set, Id64String, IModelStatus, Logger, LogLevel } from "@bentley/bentleyjs-core"; +import { ClientRequestContext, DbResult, Guid, Id64, Id64Set, Id64String, IModelStatus, Logger, LogLevel } from "@bentley/bentleyjs-core"; import { Point3d, Transform } from "@bentley/geometry-core"; import { Code, CodeSpec, ElementAspectProps, ElementProps, ExternalSourceAspectProps, FontProps, GeometricElement2dProps, GeometricElement3dProps, IModel, @@ -784,7 +784,7 @@ export class IModelTransformer extends IModelExportHandler { * If this parameter is not provided, then just the current changeset will be exported. * @note To form a range of versions to process, set `startChangeSetId` for the start of the desired range and open the source iModel as of the end of the desired range. */ - public async processChanges(requestContext: AuthorizedClientRequestContext, startChangeSetId?: GuidString): Promise { + public async processChanges(requestContext: AuthorizedClientRequestContext, startChangeSetId?: string): Promise { requestContext.enter(); Logger.logTrace(loggerCategory, "processChanges()"); this.logSettings(); diff --git a/core/backend/src/NativeAppStorage.ts b/core/backend/src/NativeAppStorage.ts index 1baaaa0f6ef..e78effe2f87 100644 --- a/core/backend/src/NativeAppStorage.ts +++ b/core/backend/src/NativeAppStorage.ts @@ -2,8 +2,12 @@ * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import * as path from "path"; -import { DbResult } from "@bentley/bentleyjs-core"; +/** @packageDocumentation + * @module NativeApp + */ + +import { join } from "path"; +import { DbResult, IModelStatus } from "@bentley/bentleyjs-core"; import { IModelError, StorageValue } from "@bentley/imodeljs-common"; import { ECDb, ECDbOpenMode } from "./ECDb"; import { IModelHost } from "./IModelHost"; @@ -11,21 +15,18 @@ import { IModelJsFs } from "./IModelJsFs"; import { NativeHost } from "./NativeHost"; /** - * Native app storage allow key value pair to be persisted in a sqlite db in app cache. - * This is exposed to frontend through [[NativeApp]] - * @internal + * A local file stored in the [[NativeHost.appSettingsCacheDir]] for storing key/value pairs. + * @beta */ export class NativeAppStorage { - private static readonly _version = 1; - private static readonly _ext = `.v${NativeAppStorage._version}.ecdb`; + private static readonly _ext = ".settings-db"; private static _storages = new Map(); private static _init: boolean = false; private constructor(private _ecdb: ECDb, public readonly id: string) { } + + /** Set the value for a key */ public setData(key: string, value: StorageValue): void { - if (!this._ecdb.isOpen) { - throw new IModelError(DbResult.BE_SQLITE_ERROR, "Cache is not open or disposed"); - } - const rc = this._ecdb.withPreparedSqliteStatement("INSERT INTO [app_cache]([key],[type],[val])VALUES(?,?,?) ON CONFLICT([key]) DO UPDATE SET [type]=excluded.[type], [val]=excluded.[val]", (stmt) => { + const rc = this._ecdb.withPreparedSqliteStatement("INSERT INTO app_setting(key,type,val)VALUES(?,?,?) ON CONFLICT(key) DO UPDATE SET type=excluded.type,val=excluded.val", (stmt) => { let type: string | undefined = value === null ? "null" : typeof value; if (type === "object") { if (value instanceof Uint8Array) { @@ -42,56 +43,72 @@ export class NativeAppStorage { stmt.bindValue(3, value); return stmt.step(); }); - if (rc !== DbResult.BE_SQLITE_DONE) { + if (rc !== DbResult.BE_SQLITE_DONE) throw new IModelError(rc, "SQLite error"); - } else { - this._ecdb.saveChanges(); - } + + this._ecdb.saveChanges(); } + /** Get the value for a key from this Storage. If key is not present, return undefined. */ public getData(key: string): StorageValue | undefined { - if (!this._ecdb.isOpen) { - throw new IModelError(DbResult.BE_SQLITE_ERROR, "Cache is not open or disposed"); - } - return this._ecdb.withPreparedSqliteStatement("SELECT [type],[val] FROM [app_cache] WHERE [key] = ?", (stmt) => { + return this._ecdb.withPreparedSqliteStatement("SELECT type,val FROM app_setting WHERE key=?", (stmt) => { stmt.bindValue(1, key); - const rc = stmt.step(); - if (rc === DbResult.BE_SQLITE_ROW) { - const type = stmt.getValue(0).getString(); - if (type === "number") { + if (DbResult.BE_SQLITE_ROW !== stmt.step()) + return undefined; + switch (stmt.getValue(0).getString()) { + case "number": return stmt.getValue(1).getDouble(); - } else if (type === "string") { + case "string": return stmt.getValue(1).getString(); - } else if (type === "boolean") { + case "boolean": return Boolean(stmt.getValue(1).getInteger()); - } else if (type === "Uint8Array") { + case "Uint8Array": return stmt.getValue(1).getBlob(); - } else if (type === "null") { + case "null": return null; - } else { - throw new IModelError(DbResult.BE_SQLITE_ERROR, "Unsupported value type in cache"); - } } - return undefined; + throw new IModelError(DbResult.BE_SQLITE_ERROR, "Unsupported type in cache"); }); } + + /** Get the value for a key as a string. If it is not present, or not of type string, return undefined */ + public getString(key: string): string | undefined { + const val = this.getData(key); + return typeof val === "string" ? val : undefined; + } + + /** Get the value for a key as a number. If it is not present, or not of type number, return undefined */ + public getNumber(key: string): number | undefined { + const val = this.getData(key); + return typeof val === "number" ? val : undefined; + } + + /** Get the value for a key as a boolean. If it is not present, or not of type boolean, return undefined */ + public getBoolean(key: string): boolean | undefined { + const val = this.getData(key); + return typeof val === "boolean" ? val : undefined; + } + + /** Get the value for a key as a Uint8Array. If it is not present, or not of type Uint8Array, return undefined */ + public getUint8Array(key: string): Uint8Array | undefined { + const val = this.getData(key); + return val instanceof Uint8Array ? val : undefined; + } + + /** Get all key names in this Storage */ public getKeys(): string[] { - if (!this._ecdb.isOpen) { - throw new IModelError(DbResult.BE_SQLITE_ERROR, "Cache is not open or disposed"); - } const keys = new Array(); - this._ecdb.withPreparedSqliteStatement("SELECT [key] FROM [app_cache]", (stmt) => { + this._ecdb.withPreparedSqliteStatement("SELECT key FROM app_setting", (stmt) => { while (DbResult.BE_SQLITE_ROW === stmt.step()) { keys.push(stmt.getValue(0).getString()); } }); return keys; } + + /** Remove a key/value pair from this Storage */ public removeData(key: string) { - if (!this._ecdb.isOpen) { - throw new IModelError(DbResult.BE_SQLITE_ERROR, "Cache is not open or disposed"); - } - const rc = this._ecdb.withPreparedSqliteStatement("DELETE FROM [app_cache] WHERE [key] = ?", (stmt) => { + const rc = this._ecdb.withPreparedSqliteStatement("DELETE FROM app_setting WHERE key=?", (stmt) => { stmt.bindValue(1, key); return stmt.step(); }); @@ -99,78 +116,82 @@ export class NativeAppStorage { throw new IModelError(rc, "SQLite error"); } } + + /** Remove all key/value pairs */ public removeAll() { - if (!this._ecdb.isOpen) { - throw new IModelError(DbResult.BE_SQLITE_ERROR, "Cache is not open or disposed"); - } - const rc = this._ecdb.withPreparedSqliteStatement("DELETE FROM [app_cache]", (stmt) => { + const rc = this._ecdb.withPreparedSqliteStatement("DELETE FROM app_setting", (stmt) => { return stmt.step(); }); if (rc !== DbResult.BE_SQLITE_DONE) { throw new IModelError(rc, "SQLite error"); } } + + /** Close this Storage. */ public close(deleteFile: boolean = false) { - if (!this._ecdb.isOpen) { - throw new IModelError(DbResult.BE_SQLITE_ERROR, "Cache is not open or disposed"); - } - const storageFile = path.join(NativeHost.appSettingsCacheDir, this.id); + const storageFile = join(NativeHost.appSettingsCacheDir, this.id); this._ecdb.saveChanges(); this._ecdb.closeDb(); - if (deleteFile) { + (this._ecdb as any) = undefined; + if (deleteFile) IModelJsFs.removeSync(storageFile); - } NativeAppStorage._storages.delete(this.id); } + private static init(ecdb: ECDb): DbResult { - if (!ecdb.isOpen) { - throw new IModelError(DbResult.BE_SQLITE_ERROR, "Cache is not open or disposed"); - } - return ecdb.withPreparedSqliteStatement("CREATE TABLE [app_cache]([key] PRIMARY KEY, [type], [val]);", (stmt) => { + return ecdb.withPreparedSqliteStatement("CREATE TABLE app_setting(key PRIMARY KEY,type,val);", (stmt) => { return stmt.step(); }); } - public static find(name: string): NativeAppStorage | undefined { - return this._storages.get(name); + + /** find and open storage by its name. */ + public static find(name: string): NativeAppStorage { + const storage = this._storages.get(name); + if (undefined === storage) + throw new IModelError(IModelStatus.FileNotFound, `Storage ${name} not open`); + return storage; } + + /** Close all opened Storages. + * @internal + */ public static closeAll() { - this._storages.forEach((value) => { - value.close(); - }); + this._storages.forEach((value) => value.close()); this._storages.clear(); } + + /** @internal */ public static getStorageNames(): string[] { - return IModelJsFs.readdirSync(NativeHost.appSettingsCacheDir).filter((_) => _.endsWith(this._ext)); + return IModelJsFs.readdirSync(NativeHost.appSettingsCacheDir).filter((name) => name.endsWith(this._ext)); } + + /** Open or find a Storage by name. */ public static open(name: string): NativeAppStorage { if (!this._init) { - IModelHost.onBeforeShutdown.addOnce(() => { - this.closeAll(); - }); + IModelHost.onBeforeShutdown.addOnce(() => this.closeAll()); this._init = true; } const fileName = name + this._ext; - if (!IModelJsFs.existsSync(NativeHost.appSettingsCacheDir)) { + if (!IModelJsFs.existsSync(NativeHost.appSettingsCacheDir)) IModelJsFs.recursiveMkDirSync(NativeHost.appSettingsCacheDir); - } - const storageFile = path.join(NativeHost.appSettingsCacheDir, fileName); - let storage = this.find(fileName); - if (!storage) { - const ecdb: ECDb = new ECDb(); + + const storageFile = join(NativeHost.appSettingsCacheDir, fileName); + try { + return this.find(fileName); // see if it's already open + } catch (err) { + const ecdb = new ECDb(); if (IModelJsFs.existsSync(storageFile)) { ecdb.openDb(storageFile, ECDbOpenMode.ReadWrite); } else { ecdb.createDb(storageFile); const rc = this.init(ecdb); - if (rc !== DbResult.BE_SQLITE_DONE) { + if (rc !== DbResult.BE_SQLITE_DONE) throw new IModelError(rc, "SQLite error"); - } else { - ecdb.saveChanges(); - } + ecdb.saveChanges(); } - storage = new NativeAppStorage(ecdb, fileName); + const storage = new NativeAppStorage(ecdb, fileName); this._storages.set(fileName, storage); + return storage; } - return storage; } } diff --git a/core/backend/src/NativeHost.ts b/core/backend/src/NativeHost.ts index a72886c1d2f..3c9e75ed605 100644 --- a/core/backend/src/NativeHost.ts +++ b/core/backend/src/NativeHost.ts @@ -6,7 +6,7 @@ * @module NativeApp */ -import * as path from "path"; +import { join } from "path"; import { BeEvent, ClientRequestContext, Config, GuidString, SessionProps } from "@bentley/bentleyjs-core"; import { BriefcaseProps, InternetConnectivityStatus, LocalBriefcaseProps, NativeAppAuthorizationConfiguration, nativeAppChannel, NativeAppFunctions, @@ -33,8 +33,10 @@ export abstract class NativeAppAuthorizationBackend extends ImsAuthorizationClie return undefined !== this._accessToken && !this._accessToken.isExpired(this._expireSafety); } public setAccessToken(token?: AccessToken) { + if (token === this._accessToken) + return; this._accessToken = token; - NativeHost.onUserStateChanged.raiseEvent(this._accessToken); + NativeHost.onUserStateChanged.raiseEvent(token); } public async getAccessToken(): Promise { if (!this.isAuthorized) @@ -135,7 +137,7 @@ class NativeAppHandler extends IpcHandler implements NativeAppFunctions { } public async storageMgrClose(storageId: string, deleteIt: boolean): Promise { - NativeAppStorage.find(storageId)?.close(deleteIt); + NativeAppStorage.find(storageId).close(deleteIt); } public async storageMgrNames(): Promise { @@ -143,30 +145,33 @@ class NativeAppHandler extends IpcHandler implements NativeAppFunctions { } public async storageGet(storageId: string, key: string): Promise { - return NativeAppStorage.find(storageId)?.getData(key); + return NativeAppStorage.find(storageId).getData(key); } public async storageSet(storageId: string, key: string, value: StorageValue): Promise { - NativeAppStorage.find(storageId)?.setData(key, value); + NativeAppStorage.find(storageId).setData(key, value); } public async storageRemove(storageId: string, key: string): Promise { - NativeAppStorage.find(storageId)?.removeData(key); + NativeAppStorage.find(storageId).removeData(key); } public async storageKeys(storageId: string): Promise { - const storage = NativeAppStorage.find(storageId)!; - return storage.getKeys(); + return NativeAppStorage.find(storageId).getKeys(); } public async storageRemoveAll(storageId: string): Promise { - const storage = NativeAppStorage.find(storageId)!; - storage.removeAll(); + NativeAppStorage.find(storageId).removeAll(); } } /** @beta */ -export type NativeHostOpts = IpcHostOpts; +export interface NativeHostOpts extends IpcHostOpts { + nativeHost?: { + /** Application named. Used to name settings file */ + applicationName?: string; + }; +} /** * Used by desktop/mobile native applications @@ -174,6 +179,7 @@ export type NativeHostOpts = IpcHostOpts; */ export class NativeHost { private static _reachability?: InternetConnectivityStatus; + private static _applicationName: string; private constructor() { } /** @internal */ @@ -188,9 +194,8 @@ export class NativeHost { /** Get the local cache folder for application settings */ public static get appSettingsCacheDir(): string { - if (this._appSettingsCacheDir === undefined) { - this._appSettingsCacheDir = path.join(IModelHost.cacheDir, "appSettings"); - } + if (this._appSettingsCacheDir === undefined) + this._appSettingsCacheDir = join(IModelHost.cacheDir, "appSettings"); return this._appSettingsCacheDir; } @@ -201,6 +206,10 @@ export class NativeHost { private static _isValid = false; public static get isValid(): boolean { return this._isValid; } + public static get settingsStore() { + return NativeAppStorage.open(this._applicationName); + } + /** * Start the backend of a native app. * @param opt @@ -213,7 +222,9 @@ export class NativeHost { NativeHost.notifyNativeFrontend("notifyInternetConnectivityChanged", status)); this.onUserStateChanged.addListener((token?: AccessToken) => NativeHost.notifyNativeFrontend("notifyUserStateChanged", token?.toJSON())); + this._applicationName = opt?.nativeHost?.applicationName ?? "iTwinApp"; } + await IpcHost.startup(opt); if (IpcHost.isValid) // for tests, we use NativeHost but don't have a frontend NativeAppHandler.register(); diff --git a/core/backend/src/imodeljs-backend.ts b/core/backend/src/imodeljs-backend.ts index 90f8b6fa5f8..5920e228663 100644 --- a/core/backend/src/imodeljs-backend.ts +++ b/core/backend/src/imodeljs-backend.ts @@ -39,12 +39,13 @@ export * from "./domains/GenericElements"; export { IModelJsNative, NativeLoggerCategory } from "@bentley/imodeljs-native"; export * from "./IModelCloneContext"; export * from "./IModelHost"; -export * from "./IpcHost"; -export * from "./NativeHost"; export * from "./IModelExporter"; export * from "./IModelImporter"; -export * from "./IModelTransformer"; export * from "./IModelSchemaLoader"; +export * from "./IModelTransformer"; +export * from "./IpcHost"; +export * from "./NativeAppStorage"; +export * from "./NativeHost"; export * from "./AutoPush"; export * from "./BackendRequestContext"; export * from "./CloudStorageBackend"; diff --git a/core/backend/src/perftest/ElementCRUD.test.ts b/core/backend/src/perftest/ElementCRUD.test.ts index a265eda2fd5..d5e0ba6eef2 100644 --- a/core/backend/src/perftest/ElementCRUD.test.ts +++ b/core/backend/src/perftest/ElementCRUD.test.ts @@ -134,7 +134,7 @@ describe("PerformanceElementsTests", () => { const seedIModel = SnapshotDb.createEmpty(IModelTestUtils.prepareOutputFile("ElementCRUDPerformance", fileName), { rootSubject: { name: "PerfTest" } }); const testSchemaName = path.join(KnownTestLocations.assetsDir, "PerfTestDomain.ecschema.xml"); await seedIModel.importSchemas(new BackendRequestContext(), [testSchemaName]); - const result: DbResult = seedIModel.nativeDb.resetBriefcaseId(BriefcaseIdValue.Standalone); + const result: DbResult = seedIModel.nativeDb.resetBriefcaseId(BriefcaseIdValue.Unassigned); assert.equal(DbResult.BE_SQLITE_OK, result); assert.isDefined(seedIModel.getMetaData(`PerfTestDomain:${name}`), `${name}is present in iModel.`); const [, newModelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(seedIModel, Code.createEmpty(), true); @@ -342,7 +342,7 @@ describe("PerformanceElementsTests2d", () => { const seedIModel = SnapshotDb.createEmpty(IModelTestUtils.prepareOutputFile("ElementCRUDPerformance2d", fileName), { rootSubject: { name: "PerfTest" } }); const testSchemaName = path.join(KnownTestLocations.assetsDir, "PerfTestDomain.ecschema.xml"); await seedIModel.importSchemas(new BackendRequestContext(), [testSchemaName]); - const result: DbResult = seedIModel.nativeDb.resetBriefcaseId(BriefcaseIdValue.Standalone); + const result: DbResult = seedIModel.nativeDb.resetBriefcaseId(BriefcaseIdValue.Unassigned); assert.equal(DbResult.BE_SQLITE_OK, result); assert.isDefined(seedIModel.getMetaData(`PerfTestDomain:${name}`), `${name}is present in iModel.`); diff --git a/core/backend/src/perftest/MixinImpact.test.ts b/core/backend/src/perftest/MixinImpact.test.ts index 6b7b1614757..c2b0830971c 100644 --- a/core/backend/src/perftest/MixinImpact.test.ts +++ b/core/backend/src/perftest/MixinImpact.test.ts @@ -127,7 +127,7 @@ describe("SchemaDesignPerf Impact of Mixins", () => { if (!IModelJsFs.existsSync(seedName)) { const seedIModel = SnapshotDb.createEmpty(IModelTestUtils.prepareOutputFile("MixinPerformance", `mixin_${hCount}.bim`), { rootSubject: { name: "PerfTest" } }); await seedIModel.importSchemas(new BackendRequestContext(), [st]); - const result: DbResult = seedIModel.nativeDb.resetBriefcaseId(BriefcaseIdValue.Standalone); + const result: DbResult = seedIModel.nativeDb.resetBriefcaseId(BriefcaseIdValue.Unassigned); assert.equal(DbResult.BE_SQLITE_OK, result); assert.isDefined(seedIModel.getMetaData("TestMixinSchema:MixinElement"), "Mixin Class is not present in iModel."); const [, newModelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(seedIModel, Code.createEmpty(), true); diff --git a/core/backend/src/perftest/PerfTestUtils.ts b/core/backend/src/perftest/PerfTestUtils.ts index b256df3575a..705bf2eaeb8 100644 --- a/core/backend/src/perftest/PerfTestUtils.ts +++ b/core/backend/src/perftest/PerfTestUtils.ts @@ -44,7 +44,7 @@ export class PerfTestDataMgr { if (undefined === this.catId) { this.catId = SpatialCategory.insert(this.db, IModel.dictionaryId, "MySpatialCategory", new SubCategoryAppearance({ color: ColorDef.fromString("rgb(255,0,0)").toJSON() })); } - const result: DbResult = this.db.nativeDb.resetBriefcaseId(BriefcaseIdValue.Standalone); + const result: DbResult = this.db.nativeDb.resetBriefcaseId(BriefcaseIdValue.Unassigned); assert.equal(DbResult.BE_SQLITE_OK, result); this.db.saveChanges(); } diff --git a/core/backend/src/perftest/PolymorphicQuery.test.ts b/core/backend/src/perftest/PolymorphicQuery.test.ts index 8e125294f4e..cb2503ec5bc 100644 --- a/core/backend/src/perftest/PolymorphicQuery.test.ts +++ b/core/backend/src/perftest/PolymorphicQuery.test.ts @@ -135,7 +135,7 @@ describe("SchemaDesignPerf Polymorphic query", () => { let spatialCategoryId = SpatialCategory.queryCategoryIdByName(seedIModel, IModel.dictionaryId, "MySpatialCategory"); if (undefined === spatialCategoryId) spatialCategoryId = SpatialCategory.insert(seedIModel, IModel.dictionaryId, "MySpatialCategory", new SubCategoryAppearance({ color: ColorDef.fromString("rgb(255,0,0)").toJSON() })); - const result: DbResult = seedIModel.nativeDb.resetBriefcaseId(BriefcaseIdValue.Standalone); + const result: DbResult = seedIModel.nativeDb.resetBriefcaseId(BriefcaseIdValue.Unassigned); assert.equal(DbResult.BE_SQLITE_OK, result); assert.isDefined(seedIModel.getMetaData("TestPolySchema:TestElement"), "Base Class is not present in iModel."); // create base class elements @@ -173,7 +173,7 @@ describe("SchemaDesignPerf Polymorphic query", () => { let spatialCategoryId = SpatialCategory.queryCategoryIdByName(seedIModel2, IModel.dictionaryId, "MySpatialCategory"); if (undefined === spatialCategoryId) spatialCategoryId = SpatialCategory.insert(seedIModel2, IModel.dictionaryId, "MySpatialCategory", new SubCategoryAppearance({ color: ColorDef.fromString("rgb(255,0,0)").toJSON() })); - const result: DbResult = seedIModel2.nativeDb.resetBriefcaseId(BriefcaseIdValue.Standalone); + const result: DbResult = seedIModel2.nativeDb.resetBriefcaseId(BriefcaseIdValue.Unassigned); assert.equal(DbResult.BE_SQLITE_OK, result); assert.isDefined(seedIModel2.getMetaData("TestPolySchema:TestElement"), "Base Class is not present in iModel."); // create base class elements diff --git a/core/backend/src/perftest/PropertiesImpact.test.ts b/core/backend/src/perftest/PropertiesImpact.test.ts index 040dc2ee2bb..83783486345 100644 --- a/core/backend/src/perftest/PropertiesImpact.test.ts +++ b/core/backend/src/perftest/PropertiesImpact.test.ts @@ -95,7 +95,7 @@ describe("SchemaDesignPerf Impact of Properties", () => { if (!IModelJsFs.existsSync(seedName)) { const seedIModel = SnapshotDb.createEmpty(IModelTestUtils.prepareOutputFile("PropPerformance", `props_${pCount}.bim`), { rootSubject: { name: "PerfTest" } }); await seedIModel.importSchemas(new BackendRequestContext(), [st]); - const result: DbResult = seedIModel.nativeDb.resetBriefcaseId(BriefcaseIdValue.Standalone); + const result: DbResult = seedIModel.nativeDb.resetBriefcaseId(BriefcaseIdValue.Unassigned); assert.equal(DbResult.BE_SQLITE_OK, result); assert.isDefined(seedIModel.getMetaData("TestPropsSchema:PropElement"), "PropsClass is present in iModel."); const [, newModelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(seedIModel, Code.createEmpty(), true); @@ -347,7 +347,7 @@ describe("SchemaDesignPerf Number of Indices", () => { if (!IModelJsFs.existsSync(seedName)) { const seedIModel = SnapshotDb.createEmpty(IModelTestUtils.prepareOutputFile("IndexPerformance", `index_${iCount}.bim`), { rootSubject: { name: "PerfTest" } }); await seedIModel.importSchemas(new BackendRequestContext(), [st]); - const result: DbResult = seedIModel.nativeDb.resetBriefcaseId(BriefcaseIdValue.Standalone); + const result: DbResult = seedIModel.nativeDb.resetBriefcaseId(BriefcaseIdValue.Unassigned); assert.equal(DbResult.BE_SQLITE_OK, result); assert.isDefined(seedIModel.getMetaData("TestIndexSchema:PropElement"), "PropsClass is present in iModel."); const [, newModelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(seedIModel, Code.createEmpty(), true); @@ -375,7 +375,7 @@ describe("SchemaDesignPerf Number of Indices", () => { if (!IModelJsFs.existsSync(seedName)) { const seedIModel = SnapshotDb.createEmpty(IModelTestUtils.prepareOutputFile("IndexPerformance", `index_perclass_${iCount}.bim`), { rootSubject: { name: "PerfTest" } }); await seedIModel.importSchemas(new BackendRequestContext(), [st]); - const result: DbResult = seedIModel.nativeDb.resetBriefcaseId(BriefcaseIdValue.Standalone); + const result: DbResult = seedIModel.nativeDb.resetBriefcaseId(BriefcaseIdValue.Unassigned); assert.equal(DbResult.BE_SQLITE_OK, result); assert.isDefined(seedIModel.getMetaData("TestIndexSchema:PropElement0"), "PropsClass is present in iModel."); const [, newModelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(seedIModel, Code.createEmpty(), true); diff --git a/core/backend/src/perftest/RelationshipImpact.test.ts b/core/backend/src/perftest/RelationshipImpact.test.ts index 3673eaec102..616c0988a5a 100644 --- a/core/backend/src/perftest/RelationshipImpact.test.ts +++ b/core/backend/src/perftest/RelationshipImpact.test.ts @@ -155,7 +155,7 @@ describe("SchemaDesignPerf Relationship Comparison", () => { if (!IModelJsFs.existsSync(seedName)) { const seedIModel = SnapshotDb.createEmpty(IModelTestUtils.prepareOutputFile("RelationshipPerformance", "relationship.bim"), { rootSubject: { name: "PerfTest" } }); await seedIModel.importSchemas(new BackendRequestContext(), [st]); - const result: DbResult = seedIModel.nativeDb.resetBriefcaseId(BriefcaseIdValue.Standalone); + const result: DbResult = seedIModel.nativeDb.resetBriefcaseId(BriefcaseIdValue.Unassigned); assert.equal(DbResult.BE_SQLITE_OK, result); // first create Elements and then Relationship const [, newModelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(seedIModel, Code.createEmpty(), true); diff --git a/core/backend/src/rpc-impl/RpcBriefcaseUtility.ts b/core/backend/src/rpc-impl/RpcBriefcaseUtility.ts index 81b751a43b9..d642b378f9f 100644 --- a/core/backend/src/rpc-impl/RpcBriefcaseUtility.ts +++ b/core/backend/src/rpc-impl/RpcBriefcaseUtility.ts @@ -12,7 +12,7 @@ import { BriefcaseProps, IModelConnectionProps, IModelError, IModelRpcOpenProps, import { AuthorizedClientRequestContext } from "@bentley/itwin-client"; import { BackendLoggerCategory } from "../BackendLoggerCategory"; import { BriefcaseManager } from "../BriefcaseManager"; -import { CheckpointProps, V1CheckpointManager } from "../CheckpointManager"; +import { CheckpointManager, CheckpointProps, V1CheckpointManager } from "../CheckpointManager"; import { BriefcaseDb, IModelDb, SnapshotDb } from "../IModelDb"; import { IModelHost } from "../IModelHost"; import { IModelJsFs } from "../IModelJsFs"; @@ -127,12 +127,21 @@ export class RpcBriefcaseUtility { // opening a checkpoint, readonly. let db: SnapshotDb | void; + // first check if it's already open + db = SnapshotDb.tryFindByKey(CheckpointManager.getKey(checkpoint)); + if (db) { + Logger.logTrace(loggerCategory, "Checkpoint was already open", () => ({ ...tokenProps })); + return db; + } + try { - // first try V2 checkpoint + // now try V2 checkpoint db = await SnapshotDb.openCheckpointV2(checkpoint); requestContext.enter(); Logger.logTrace(loggerCategory, "using V2 checkpoint briefcase", () => ({ ...tokenProps })); } catch (e) { + Logger.logTrace(loggerCategory, "unable to open V2 checkpoint - falling back to V1 checkpoint", () => ({ ...tokenProps })); + // this isn't a v2 checkpoint. Set up a race between the specified timeout period and the open. Throw an RpcPendingResponse exception if the timeout happens first. const request = { checkpoint, diff --git a/core/backend/src/test/IModelTestUtils.ts b/core/backend/src/test/IModelTestUtils.ts index dbf5497f448..2612794dc9a 100644 --- a/core/backend/src/test/IModelTestUtils.ts +++ b/core/backend/src/test/IModelTestUtils.ts @@ -262,6 +262,14 @@ export class IModelTestUtils { } } + public static generateChangeSetId(): string { + let result = ""; + for (let i = 0; i < 20; ++i) { + result += Math.floor(Math.random() * 256).toString(16).padStart(2, "0"); + } + return result; + } + /** Create and insert a PhysicalPartition element (in the repositoryModel) and an associated PhysicalModel. */ public static createAndInsertPhysicalPartition(testDb: IModelDb, newModelCode: CodeProps, parentId?: Id64String): Id64String { const model = parentId ? testDb.elements.getElement(parentId).model : IModel.repositoryModelId; diff --git a/core/backend/src/test/IModelTransformerUtils.ts b/core/backend/src/test/IModelTransformerUtils.ts index b9d2a1b6f2b..57a671f14a1 100644 --- a/core/backend/src/test/IModelTransformerUtils.ts +++ b/core/backend/src/test/IModelTransformerUtils.ts @@ -1591,7 +1591,7 @@ export class IModelToTextFileExporter extends IModelExportHandler { this.writeSeparator(); await this.exporter.exportAll(); } - public async exportChanges(requestContext: AuthorizedClientRequestContext, startChangeSetId?: GuidString): Promise { + public async exportChanges(requestContext: AuthorizedClientRequestContext, startChangeSetId?: string): Promise { this._shouldIndent = false; return this.exporter.exportChanges(requestContext, startChangeSetId); } diff --git a/core/backend/src/test/integration/Agent.test.ts b/core/backend/src/test/integration/Agent.test.ts index 12b2f1e31d4..4d0a02cf5be 100644 --- a/core/backend/src/test/integration/Agent.test.ts +++ b/core/backend/src/test/integration/Agent.test.ts @@ -13,8 +13,7 @@ import { HubUtility } from "./HubUtility"; // imjs_agent_test_client_id // imjs_agent_test_client_secret -describe("Agent (#integration)", () => { - // iOS does not support agent test +describe("Agent iModel Download (#integration)", () => { let testProjectId: string; let testReadIModelId: string; let testWriteIModelId: string; @@ -26,30 +25,42 @@ describe("Agent (#integration)", () => { const agentConfiguration: AgentAuthorizationClientConfiguration = { clientId: Config.App.getString("imjs_agent_test_client_id"), clientSecret: Config.App.getString("imjs_agent_test_client_secret"), - scope: "imodelhub rbac-user:external-client reality-data:read urlps-third-party context-registry-service:read-only imodeljs-backend-2686", + scope: "imodelhub context-registry-service:read-only", }; const agentClient = new AgentAuthorizationClient(agentConfiguration); const jwt = await agentClient.getAccessToken(new ClientRequestContext()); requestContext = new AuthorizedBackendRequestContext(jwt); + requestContext.enter(); - testProjectId = await HubUtility.queryProjectIdByName(requestContext, "iModelJsIntegrationTest"); - testReadIModelId = await HubUtility.queryIModelIdByName(requestContext, testProjectId, "ReadOnlyTest"); - testWriteIModelId = await HubUtility.queryIModelIdByName(requestContext, testProjectId, "ReadWriteTest"); + testProjectId = await HubUtility.getTestContextId(requestContext); + requestContext.enter(); + + testReadIModelId = await HubUtility.getTestIModelId(requestContext, HubUtility.testIModelNames.readOnly); + requestContext.enter(); + + testWriteIModelId = await HubUtility.getTestIModelId(requestContext, HubUtility.testIModelNames.readWrite); + requestContext.enter(); }); after(async () => { + requestContext.enter(); + // Purge briefcases that are close to reaching the acquire limit - await HubUtility.purgeAcquiredBriefcases(requestContext, "iModelJsIntegrationTest", "ReadOnlyTest"); - await HubUtility.purgeAcquiredBriefcases(requestContext, "iModelJsIntegrationTest", "ReadWriteTest"); + await HubUtility.purgeAcquiredBriefcasesById(requestContext, testReadIModelId); + requestContext.enter(); + await HubUtility.purgeAcquiredBriefcasesById(requestContext, testWriteIModelId); + requestContext.enter(); }); it("Agent should be able to open a checkpoint", async () => { + requestContext.enter(); const iModelDb = await IModelTestUtils.downloadAndOpenCheckpoint({ requestContext, contextId: testProjectId, iModelId: testReadIModelId }); assert.isDefined(iModelDb); }); it("Agent should be able to open a briefcase", async () => { + requestContext.enter(); const iModelDb = await IModelTestUtils.downloadAndOpenBriefcase({ requestContext, contextId: testProjectId, iModelId: testWriteIModelId }); assert.isDefined(iModelDb); }); diff --git a/core/backend/src/test/integration/BriefcaseManager.test.ts b/core/backend/src/test/integration/BriefcaseManager.test.ts index 9a9a60ff2ab..7a3640b89c5 100644 --- a/core/backend/src/test/integration/BriefcaseManager.test.ts +++ b/core/backend/src/test/integration/BriefcaseManager.test.ts @@ -395,10 +395,10 @@ describe("BriefcaseManager (#integration)", () => { it("should set appropriate briefcase ids for FixedVersion, PullOnly and PullAndPush workflows", async () => { const args = { requestContext, contextId: testContextId, iModelId: readOnlyTestIModelId, deleteFirst: true }; const iModel1 = await IModelTestUtils.openCheckpointUsingRpc(args); - assert.equal(BriefcaseIdValue.Standalone, iModel1.nativeDb.getBriefcaseId(), "checkpoint should be 0"); + assert.equal(BriefcaseIdValue.Unassigned, iModel1.nativeDb.getBriefcaseId(), "checkpoint should be 0"); const iModel2 = await IModelTestUtils.openBriefcaseUsingRpc({ ...args, briefcaseId: 0 }); - assert.equal(BriefcaseIdValue.Standalone, iModel2.briefcaseId, "pullOnly should be 0"); + assert.equal(BriefcaseIdValue.Unassigned, iModel2.briefcaseId, "pullOnly should be 0"); const iModel3 = await IModelTestUtils.openBriefcaseUsingRpc(args); assert.isTrue(iModel3.briefcaseId >= BriefcaseIdValue.FirstValid && iModel3.briefcaseId <= BriefcaseIdValue.LastValid, "valid briefcaseId"); diff --git a/core/backend/src/test/integration/ChangedElements.test.ts b/core/backend/src/test/integration/ChangedElements.test.ts index 13b6b86405f..eea95db3a27 100644 --- a/core/backend/src/test/integration/ChangedElements.test.ts +++ b/core/backend/src/test/integration/ChangedElements.test.ts @@ -3,7 +3,7 @@ * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ import { DbResult, GuidString } from "@bentley/bentleyjs-core"; -import { IModelError } from "@bentley/imodeljs-common"; +import { IModelError, IModelVersion } from "@bentley/imodeljs-common"; import { TestUsers, TestUtility } from "@bentley/oidc-signin-tool"; import { assert } from "chai"; import { ChangedElementsManager } from "../../ChangedElementsManager"; @@ -11,7 +11,7 @@ import { AuthorizedBackendRequestContext, BriefcaseManager, ChangedElementsDb, I import { IModelTestUtils } from "../IModelTestUtils"; import { HubUtility } from "./HubUtility"; -describe.skip("ChangedElements (#integration)", () => { +describe("ChangedElements (#integration)", () => { let requestContext: AuthorizedBackendRequestContext; let testContextId: GuidString; let testIModelId: GuidString; @@ -34,7 +34,7 @@ describe.skip("ChangedElements (#integration)", () => { if (IModelJsFs.existsSync(cacheFilePath)) IModelJsFs.removeSync(cacheFilePath); - const iModel = await IModelTestUtils.downloadAndOpenCheckpoint({ requestContext, contextId: testContextId, iModelId: testIModelId }); + const iModel = await IModelTestUtils.downloadAndOpenCheckpoint({ requestContext, contextId: testContextId, iModelId: testIModelId, asOf: IModelVersion.first().toJSON() }); const changeSets = await IModelHost.iModelClient.changeSets.get(requestContext, testIModelId); assert.exists(iModel); diff --git a/core/backend/src/test/integration/Checkpoints.test.ts b/core/backend/src/test/integration/Checkpoints.test.ts index d20a21271c0..e9275c16059 100644 --- a/core/backend/src/test/integration/Checkpoints.test.ts +++ b/core/backend/src/test/integration/Checkpoints.test.ts @@ -18,7 +18,7 @@ describe.skip("Checkpoints (#integration)", () => { let requestContext: AuthorizedBackendRequestContext; let testIModelId: GuidString; let testContextId: GuidString; - let testChangeSetId: GuidString; + let testChangeSetId: string; const blockcacheDir = path.join(KnownTestLocations.outputDir, "blockcachevfs"); let daemonProc: ChildProcess; diff --git a/core/backend/src/test/integration/HubUtility.ts b/core/backend/src/test/integration/HubUtility.ts index 0b2bdfa1bb8..ca7d6923e03 100644 --- a/core/backend/src/test/integration/HubUtility.ts +++ b/core/backend/src/test/integration/HubUtility.ts @@ -529,7 +529,7 @@ export class HubUtility { if (DbResult.BE_SQLITE_OK !== status) throw new IModelError(status, "Could not open iModel"); nativeDb.deleteAllTxns(); - nativeDb.resetBriefcaseId(BriefcaseIdValue.Standalone); + nativeDb.resetBriefcaseId(BriefcaseIdValue.Unassigned); if (nativeDb.queryLocalValue("StandaloneEdit")) nativeDb.deleteLocalValue("StandaloneEdit"); nativeDb.saveChanges(); diff --git a/core/backend/src/test/integration/IModelOpen.test.ts b/core/backend/src/test/integration/IModelOpen.test.ts index 8a4faf76201..f66b7723837 100644 --- a/core/backend/src/test/integration/IModelOpen.test.ts +++ b/core/backend/src/test/integration/IModelOpen.test.ts @@ -17,7 +17,7 @@ describe("IModelOpen (#integration)", () => { let requestContext: AuthorizedBackendRequestContext; let testIModelId: GuidString; let testContextId: GuidString; - let testChangeSetId: GuidString; + let testChangeSetId: string; before(async () => { requestContext = await TestUtility.getAuthorizedClientRequestContext(TestUsers.regular); diff --git a/core/backend/src/test/integration/IModelTransformerHub.test.ts b/core/backend/src/test/integration/IModelTransformerHub.test.ts index 17e5e4965d2..c8e7e43b9ab 100644 --- a/core/backend/src/test/integration/IModelTransformerHub.test.ts +++ b/core/backend/src/test/integration/IModelTransformerHub.test.ts @@ -6,15 +6,16 @@ import { assert } from "chai"; import * as path from "path"; import * as semver from "semver"; -import { DbResult, Guid, GuidString, Id64, Id64String, Logger, LogLevel } from "@bentley/bentleyjs-core"; +import { DbResult, Guid, GuidString, Id64, Id64String, IModelStatus, Logger, LogLevel } from "@bentley/bentleyjs-core"; import { Point3d, YawPitchRollAngles } from "@bentley/geometry-core"; -import { Code, ColorDef, IModel, PhysicalElementProps, SubCategoryAppearance } from "@bentley/imodeljs-common"; +import { ChangesType } from "@bentley/imodelhub-client"; +import { Code, ColorDef, IModel, IModelVersion, PhysicalElementProps, SubCategoryAppearance } from "@bentley/imodeljs-common"; import { AuthorizedClientRequestContext } from "@bentley/itwin-client"; import { TestUsers, TestUtility } from "@bentley/oidc-signin-tool"; import { - BackendLoggerCategory, BisCoreSchema, ConcurrencyControl, ECSqlStatement, Element, ElementRefersToElements, ExternalSourceAspect, GenericSchema, - IModelDb, IModelExporter, IModelHost, IModelJsFs, IModelTransformer, NativeLoggerCategory, PhysicalModel, PhysicalObject, PhysicalPartition, - SnapshotDb, SpatialCategory, Subject, + BackendLoggerCategory, BisCoreSchema, BriefcaseDb, BriefcaseManager, ConcurrencyControl, ECSqlStatement, Element, ElementRefersToElements, + ExternalSourceAspect, GenericSchema, IModelDb, IModelExporter, IModelHost, IModelJsFs, IModelJsNative, IModelTransformer, NativeLoggerCategory, + PhysicalModel, PhysicalObject, PhysicalPartition, SnapshotDb, SpatialCategory, Subject, } from "../../imodeljs-backend"; import { IModelTestUtils } from "../IModelTestUtils"; import { CountingIModelImporter, IModelToTextFileExporter, IModelTransformerUtils, TestIModelTransformer } from "../IModelTransformerUtils"; @@ -251,6 +252,11 @@ describe("IModelTransformerHub (#integration)", () => { assert.equal(targetDbChanges.model.deleteIds.size, 0); } + const sourceIModelChangeSets = await IModelHost.iModelClient.changeSets.get(requestContext, sourceIModelId); + const targetIModelChangeSets = await IModelHost.iModelClient.changeSets.get(requestContext, targetIModelId); + assert.equal(sourceIModelChangeSets.length, 2); + assert.equal(targetIModelChangeSets.length, 2); + await IModelTestUtils.closeAndDeleteBriefcaseDb(requestContext, sourceDb); await IModelTestUtils.closeAndDeleteBriefcaseDb(requestContext, targetDb); @@ -408,6 +414,19 @@ describe("IModelTransformerHub (#integration)", () => { assertPhysicalObjects(branchDb2, state0); const changeSetBranch2First = branchDb2.changeSetId; + // create empty iModel meant to contain replayed master history + const replayedIModelName = HubUtility.generateUniqueName("Replayed"); + await deleteIModelByName(requestContext, projectId, replayedIModelName); + const replayedIModel = await IModelHost.iModelClient.iModels.create(requestContext, projectId, replayedIModelName, { + description: `Replay of ${masterIModelName}`, + }); + assert.isDefined(replayedIModel?.id); + const replayedIModelId: GuidString = replayedIModel!.id!; // eslint-disable-line + const replayedDb = await IModelTestUtils.downloadAndOpenBriefcase({ requestContext, contextId: projectId, iModelId: replayedIModelId }); + replayedDb.concurrencyControl.setPolicy(new ConcurrencyControl.OptimisticPolicy()); + assert.isTrue(replayedDb.isBriefcaseDb()); + assert.equal(replayedDb.contextId, projectId); + try { // record provenance in Branch1 and Branch2 iModels const provenanceInserterB1 = new IModelTransformer(masterDb, branchDb1, { @@ -425,12 +444,8 @@ describe("IModelTransformerHub (#integration)", () => { assert.isAbove(count(branchDb2, ExternalSourceAspect.classFullName), state0.length); // push Branch1 and Branch2 provenance changes - await branchDb1.concurrencyControl.request(requestContext); - await branchDb2.concurrencyControl.request(requestContext); - branchDb1.saveChanges(); - branchDb2.saveChanges(); - await branchDb1.pushChanges(requestContext, "State0"); - await branchDb2.pushChanges(requestContext, "State0"); + await saveAndPushChanges(requestContext, branchDb1, "State0"); + await saveAndPushChanges(requestContext, branchDb2, "State0"); const changeSetBranch1State0 = branchDb1.changeSetId; const changeSetBranch2State0 = branchDb2.changeSetId; assert.notEqual(changeSetBranch1State0, changeSetBranch1First); @@ -441,9 +456,7 @@ describe("IModelTransformerHub (#integration)", () => { const state1 = [1, 2, 3, 4]; maintainPhysicalObjects(branchDb1, delta01); assertPhysicalObjects(branchDb1, state1); - await branchDb1.concurrencyControl.request(requestContext); - branchDb1.saveChanges(); - await branchDb1.pushChanges(requestContext, "State0 -> State1"); + await saveAndPushChanges(requestContext, branchDb1, "State0 -> State1"); const changeSetBranch1State1 = branchDb1.changeSetId; assert.notEqual(changeSetBranch1State1, changeSetBranch1State0); @@ -452,9 +465,7 @@ describe("IModelTransformerHub (#integration)", () => { const state2 = [1, 2, -3, 4, 5, 6]; maintainPhysicalObjects(branchDb1, delta12); assertPhysicalObjects(branchDb1, state2); - await branchDb1.concurrencyControl.request(requestContext); - branchDb1.saveChanges(); - await branchDb1.pushChanges(requestContext, "State1 -> State2"); + await saveAndPushChanges(requestContext, branchDb1, "State1 -> State2"); const changeSetBranch1State2 = branchDb1.changeSetId; assert.notEqual(changeSetBranch1State2, changeSetBranch1State1); @@ -468,9 +479,7 @@ describe("IModelTransformerHub (#integration)", () => { assertPhysicalObjectUpdated(masterDb, 1); assertPhysicalObjectUpdated(masterDb, 2); assert.equal(count(masterDb, ExternalSourceAspect.classFullName), 0); - await masterDb.concurrencyControl.request(requestContext); - masterDb.saveChanges(); - await masterDb.pushChanges(requestContext, "State0 -> State2"); // a squash of 2 branch changes into 1 in the masterDb change ledger + await saveAndPushChanges(requestContext, masterDb, "State0 -> State2"); // a squash of 2 branch changes into 1 in the masterDb change ledger const changeSetMasterState2 = masterDb.changeSetId; assert.notEqual(changeSetMasterState2, changeSetMasterState0); branchDb1.saveChanges(); // saves provenance locally in case of re-merge @@ -480,9 +489,7 @@ describe("IModelTransformerHub (#integration)", () => { await masterToBranch2.processChanges(requestContext, changeSetMasterState2); masterToBranch2.dispose(); assertPhysicalObjects(branchDb2, state2); - await branchDb2.concurrencyControl.request(requestContext); - branchDb2.saveChanges(); - await branchDb2.pushChanges(requestContext, "State0 -> State2"); + await saveAndPushChanges(requestContext, branchDb2, "State0 -> State2"); const changeSetBranch2State2 = branchDb2.changeSetId; assert.notEqual(changeSetBranch2State2, changeSetBranch2State0); @@ -491,9 +498,7 @@ describe("IModelTransformerHub (#integration)", () => { const state3 = [1, 2, -3, 4, 5, 6, 7, 8]; maintainPhysicalObjects(branchDb2, delta23); assertPhysicalObjects(branchDb2, state3); - await branchDb2.concurrencyControl.request(requestContext); - branchDb2.saveChanges(); - await branchDb2.pushChanges(requestContext, "State2 -> State3"); + await saveAndPushChanges(requestContext, branchDb2, "State2 -> State3"); const changeSetBranch2State3 = branchDb2.changeSetId; assert.notEqual(changeSetBranch2State3, changeSetBranch2State2); @@ -505,9 +510,7 @@ describe("IModelTransformerHub (#integration)", () => { branch2ToMaster.dispose(); assertPhysicalObjects(masterDb, state3); assert.equal(count(masterDb, ExternalSourceAspect.classFullName), 0); - await masterDb.concurrencyControl.request(requestContext); - masterDb.saveChanges(); - await masterDb.pushChanges(requestContext, "State2 -> State3"); + await saveAndPushChanges(requestContext, masterDb, "State2 -> State3"); const changeSetMasterState3 = masterDb.changeSetId; assert.notEqual(changeSetMasterState3, changeSetMasterState2); branchDb2.saveChanges(); // saves provenance locally in case of re-merge @@ -517,9 +520,7 @@ describe("IModelTransformerHub (#integration)", () => { const state4 = [1, 2, -3, 4, 5, 6, -7, 8]; maintainPhysicalObjects(masterDb, delta34); assertPhysicalObjects(masterDb, state4); - await masterDb.concurrencyControl.request(requestContext); - masterDb.saveChanges(); - await masterDb.pushChanges(requestContext, "State3 -> State4"); + await saveAndPushChanges(requestContext, masterDb, "State3 -> State4"); const changeSetMasterState4 = masterDb.changeSetId; assert.notEqual(changeSetMasterState4, changeSetMasterState3); @@ -529,20 +530,89 @@ describe("IModelTransformerHub (#integration)", () => { masterToBranch1.dispose(); assertPhysicalObjects(branchDb1, state4); assertPhysicalObjectUpdated(branchDb1, 6); - await branchDb1.concurrencyControl.request(requestContext); - branchDb1.saveChanges(); - await branchDb1.pushChanges(requestContext, "State2 -> State4"); + await saveAndPushChanges(requestContext, branchDb1, "State2 -> State4"); const changeSetBranch1State4 = branchDb1.changeSetId; assert.notEqual(changeSetBranch1State4, changeSetBranch1State2); + // test for consistency between `IModelHost.iModelClient.changeSets.get` and `BriefcaseManager.downloadChangeSets` (a real app would only call one or the other) + let masterDbChangeSets = await IModelHost.iModelClient.changeSets.get(requestContext, masterIModelId); // returns changeSet info + assert.equal(masterDbChangeSets.length, 4); + for (const masterDbChangeSet of masterDbChangeSets) { + assert.isDefined(masterDbChangeSet.id); + assert.isFalse(Guid.isGuid(masterDbChangeSet.id!) || Id64.isValidId64(masterDbChangeSet.id!)); // a changeSetId is a hash value based on the contents and its parentId + assert.isDefined(masterDbChangeSet.description); // test code above always included a change description when pushChanges was called + assert.isAbove(masterDbChangeSet.fileSizeNumber, 0); + } + masterDbChangeSets = await BriefcaseManager.downloadChangeSets(requestContext, masterIModelId, "", masterDb.changeSetId); // downloads actual changeSets + assert.equal(masterDbChangeSets.length, 4); + const masterDeletedElementIds = new Set(); + for (const masterDbChangeSet of masterDbChangeSets) { + assert.isDefined(masterDbChangeSet.id); + assert.isDefined(masterDbChangeSet.description); // test code above always included a change description when pushChanges was called + assert.isAbove(masterDbChangeSet.fileSizeNumber, 0); + const changeSetPath = path.join(BriefcaseManager.getChangeSetsPath(masterIModelId), masterDbChangeSet.fileName!); + assert.isTrue(IModelJsFs.existsSync(changeSetPath)); + // below is one way of determining the set of elements that were deleted in a specific changeSet + const statusOrResult: IModelJsNative.ErrorStatusOrResult = masterDb.nativeDb.extractChangedInstanceIdsFromChangeSet(changeSetPath); + assert.isUndefined(statusOrResult.error); + const result: IModelJsNative.ChangedInstanceIdsProps = JSON.parse(statusOrResult.result); + assert.isDefined(result.element); + if (result.element?.delete) { + result.element.delete.forEach((id: Id64String) => masterDeletedElementIds.add(id)); + } + } + assert.isAtLeast(masterDeletedElementIds.size, 1); + + // replay master history to create replayed iModel + const sourceDb = await IModelTestUtils.downloadAndOpenBriefcase({ requestContext, contextId: projectId, iModelId: masterIModelId, asOf: IModelVersion.first().toJSON() }); + const replayTransformer = new IModelTransformer(sourceDb, replayedDb); + // this replay strategy pretends that deleted elements never existed + for (const elementId of masterDeletedElementIds) { + replayTransformer.exporter.excludeElement(elementId); + } + // note: this test knows that there were no schema changes, so does not call `processSchemas` + await replayTransformer.processAll(); // process any elements that were part of the "seed" + await saveAndPushChanges(requestContext, replayedDb, "changes from source seed"); + for (const masterDbChangeSet of masterDbChangeSets) { + await sourceDb.pullAndMergeChanges(requestContext, IModelVersion.asOfChangeSet(masterDbChangeSet.id!)); + await replayTransformer.processChanges(requestContext, sourceDb.changeSetId); + await saveAndPushChanges(requestContext, replayedDb, masterDbChangeSet.description ?? "", masterDbChangeSet.changesType); + } + replayTransformer.dispose(); + sourceDb.close(); + assertPhysicalObjects(replayedDb, state4); // should have same ending state as masterDb + + // make sure there are no deletes in the replay history (all elements that were eventually deleted from masterDb were excluded) + const replayedDbChangeSets = await BriefcaseManager.downloadChangeSets(requestContext, replayedIModelId, "", replayedDb.changeSetId); // downloads actual changeSets + assert.isAtLeast(replayedDbChangeSets.length, masterDbChangeSets.length); // replayedDb will have more changeSets when seed contains elements + const replayedDeletedElementIds = new Set(); + for (const replayedDbChangeSet of replayedDbChangeSets) { + assert.isDefined(replayedDbChangeSet.id); + assert.isDefined(replayedDbChangeSet.description); // test code above always included a change description when pushChanges was called + assert.isAbove(replayedDbChangeSet.fileSizeNumber, 0); + const changeSetPath = path.join(BriefcaseManager.getChangeSetsPath(replayedIModelId), replayedDbChangeSet.fileName!); + assert.isTrue(IModelJsFs.existsSync(changeSetPath)); + // below is one way of determining the set of elements that were deleted in a specific changeSet + const statusOrResult: IModelJsNative.ErrorStatusOrResult = replayedDb.nativeDb.extractChangedInstanceIdsFromChangeSet(changeSetPath); + assert.isUndefined(statusOrResult.error); + const result: IModelJsNative.ChangedInstanceIdsProps = JSON.parse(statusOrResult.result); + assert.isDefined(result.element); + if (result.element?.delete) { + result.element.delete.forEach((id: Id64String) => replayedDeletedElementIds.add(id)); + } + } + assert.equal(replayedDeletedElementIds.size, 0); + masterDb.close(); branchDb1.close(); branchDb2.close(); + replayedDb.close(); } finally { await IModelHost.iModelClient.iModels.delete(requestContext, projectId, masterIModelId); await IModelHost.iModelClient.iModels.delete(requestContext, projectId, branchIModelId1); await IModelHost.iModelClient.iModels.delete(requestContext, projectId, branchIModelId2); + await IModelHost.iModelClient.iModels.delete(requestContext, projectId, replayedIModelId); } }); @@ -552,6 +622,12 @@ describe("IModelTransformerHub (#integration)", () => { }); } + async function saveAndPushChanges(requestContext: AuthorizedClientRequestContext, briefcaseDb: BriefcaseDb, description: string, changesType?: ChangesType): Promise { + await briefcaseDb.concurrencyControl.request(requestContext); + briefcaseDb.saveChanges(description); + return briefcaseDb.pushChanges(requestContext, description, changesType); + } + function populateMaster(iModelDb: IModelDb, numbers: number[]): void { SpatialCategory.insert(iModelDb, IModel.dictionaryId, "SpatialCategory", new SubCategoryAppearance()); PhysicalModel.insert(iModelDb, IModel.rootSubjectId, "PhysicalModel"); diff --git a/core/backend/src/test/standalone/Category.test.ts b/core/backend/src/test/standalone/Category.test.ts index f1281d514b0..a44f6448c2a 100644 --- a/core/backend/src/test/standalone/Category.test.ts +++ b/core/backend/src/test/standalone/Category.test.ts @@ -10,8 +10,6 @@ import { } from "../../imodeljs-backend"; import { IModelTestUtils } from "../IModelTestUtils"; -// spell-checker: disable - describe("Category", () => { let imodel: StandaloneDb; before(() => { @@ -42,7 +40,7 @@ describe("Category", () => { const materialId = RenderMaterialElement.insert(imodel, IModelDb.dictionaryId, "FieldWeldMaterial", params); expect(Id64.isValidId64(materialId)).to.be.true; - const appearance = new SubCategoryAppearance({material: materialId, priority: 100, transp: 0.75}); + const appearance = new SubCategoryAppearance({ material: materialId, priority: 100, transp: 0.75 }); const priCategoryId = SpatialCategory.insert(imodel, IModelDb.dictionaryId, "FieldWeld", appearance); expect(Id64.isValidId64(priCategoryId)).to.be.true; diff --git a/core/backend/src/test/standalone/CheckpointManager.test.ts b/core/backend/src/test/standalone/CheckpointManager.test.ts new file mode 100644 index 00000000000..105e212a685 --- /dev/null +++ b/core/backend/src/test/standalone/CheckpointManager.test.ts @@ -0,0 +1,152 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Bentley Systems, Incorporated. All rights reserved. +* See LICENSE.md in the project root for license terms and full copyright notice. +*--------------------------------------------------------------------------------------------*/ + +import { assert } from "chai"; +import * as path from "path"; +import * as sinon from "sinon"; +import { ClientRequestContext, Guid } from "@bentley/bentleyjs-core"; +import { AccessToken, AuthorizedClientRequestContext } from "@bentley/itwin-client"; +import { CheckpointManager, V1CheckpointManager } from "../../CheckpointManager"; +import { IModelHost } from "../../imodeljs-backend"; +import { SnapshotDb } from "../../IModelDb"; +import { IModelJsFs } from "../../IModelJsFs"; +import { IModelTestUtils } from "../IModelTestUtils"; + +describe("V1 Checkpoint Manager", () => { + it("empty props", async () => { + const props = { + contextId: "", + iModelId: "", + changeSetId: "", + requestContext: new AuthorizedClientRequestContext(new AccessToken()), + }; + assert.equal(V1CheckpointManager.getFileName(props), path.join(IModelHost.cacheDir, "imodels", "checkpoints", "first.bim")); + }); + + it("changeset only props", async () => { + const props = { + contextId: "", + iModelId: "", + changeSetId: "1234", + requestContext: new AuthorizedClientRequestContext(new AccessToken()), + }; + assert.equal(V1CheckpointManager.getFileName(props), path.join(IModelHost.cacheDir, "imodels", "checkpoints", "1234.bim")); + }); + + it("changeset+context props", async () => { + const props = { + contextId: "5678", + iModelId: "", + changeSetId: "1234", + requestContext: new AuthorizedClientRequestContext(new AccessToken()), + }; + assert.equal(V1CheckpointManager.getFileName(props), path.join(IModelHost.cacheDir, "imodels", "checkpoints", "1234.bim")); + }); + + it("changeset+context+imodel props", async () => { + const props = { + contextId: "5678", + iModelId: "910", + changeSetId: "1234", + requestContext: new AuthorizedClientRequestContext(new AccessToken()), + }; + assert.equal(V1CheckpointManager.getFileName(props), path.join(IModelHost.cacheDir, "imodels", "910", "checkpoints", "1234.bim")); + }); + + it("getFolder", async () => { + assert.equal(V1CheckpointManager.getFolder("1234"), path.join(IModelHost.cacheDir, "imodels", "1234", "checkpoints")); + assert.equal(V1CheckpointManager.getFolder(""), path.join(IModelHost.cacheDir, "imodels", "checkpoints")); + }); + + it("should fix invalid dbGuid during download", async () => { + const dbPath = IModelTestUtils.prepareOutputFile("IModel", "TestCheckpoint.bim"); + const snapshot = SnapshotDb.createEmpty(dbPath, { rootSubject: { name: "test" } }); + const iModelId = Guid.createValue(); // This is wrong - it should be `snapshot.getGuid()`! + const contextId = Guid.createValue(); + const changeSetId = IModelTestUtils.generateChangeSetId(); + snapshot.nativeDb.saveProjectGuid(Guid.normalize(contextId)); + snapshot.nativeDb.saveLocalValue("ParentChangeSetId", changeSetId); + snapshot.saveChanges(); + + assert.notEqual(iModelId, snapshot.nativeDb.getDbGuid()); // Ensure the Snapshot dbGuid and iModelId are different + + snapshot.close(); + + const mockCheckpoint = { + wsgId: "INVALID", + ecId: "INVALID", + changeSetId, + downloadUrl: `INVALID`, + mergedChangeSetId: changeSetId, + }; + + const checkpointsHandler = IModelHost.iModelClient.checkpoints; + sinon.stub(checkpointsHandler, "get").callsFake(async () => [mockCheckpoint]); + sinon.stub(IModelHost.iModelClient, "checkpoints").get(() => checkpointsHandler); + + const fileHandler = IModelHost.iModelClient.fileHandler!; + sinon.stub(fileHandler, "downloadFile").callsFake(async (_requestContext: AuthorizedClientRequestContext, _downloadUrl: string, downloadPath: string) => { + IModelJsFs.copySync(dbPath, downloadPath); + }); + sinon.stub(IModelHost.iModelClient, "fileHandler").get(() => fileHandler); + + const ctx = ClientRequestContext.current as AuthorizedClientRequestContext; + const downloadedDbPath = IModelTestUtils.prepareOutputFile("IModel", "TestCheckpoint2.bim"); + await V1CheckpointManager.downloadCheckpoint({ localFile: downloadedDbPath, checkpoint: { requestContext: ctx, contextId, iModelId, changeSetId } }); + const db = SnapshotDb.openCheckpointV1(downloadedDbPath, { requestContext: ctx, contextId, iModelId, changeSetId }); + assert.equal(iModelId, db.nativeDb.getDbGuid(), "expected the V1 Checkpoint download to fix the improperly set dbGuid."); + }); +}); + +describe("Checkpoint Manager", () => { + + afterEach(() => { + sinon.restore(); + }); + + it("open missing local file should return undefined", async () => { + const checkpoint = { + contextId: "5678", + iModelId: "910", + changeSetId: "1234", + requestContext: new AuthorizedClientRequestContext(new AccessToken()), // Why is this on CheckpointProps rather than DownloadRequest + }; + const request = { + localFile: V1CheckpointManager.getFileName(checkpoint), + checkpoint, + }; + const db = CheckpointManager.tryOpenLocalFile(request); + assert.isUndefined(db); + }); + + it("open a bad bim file should return undefined", async () => { + const checkpoint = { + contextId: "5678", + iModelId: "910", + changeSetId: "1234", + requestContext: new AuthorizedClientRequestContext(new AccessToken()), // Why is this on CheckpointProps rather than DownloadRequest + }; + + // Setup a local file + const folder = V1CheckpointManager.getFolder(checkpoint.iModelId); + if (!IModelJsFs.existsSync(folder)) + IModelJsFs.recursiveMkDirSync(folder); + + const outputFile = V1CheckpointManager.getFileName(checkpoint); + if (IModelJsFs.existsSync(outputFile)) + IModelJsFs.unlinkSync(outputFile); + + IModelJsFs.writeFileSync(outputFile, "Testing"); + + // Attempt to open the file + const request = { + localFile: V1CheckpointManager.getFileName(checkpoint), + checkpoint, + }; + const db = CheckpointManager.tryOpenLocalFile(request); + assert.isUndefined(db); + }); + +}); diff --git a/core/backend/src/test/standalone/ElementRoundTrip.test.ts b/core/backend/src/test/standalone/ElementRoundTrip.test.ts index 2c1c6b0f8e7..35a6c18e15c 100644 --- a/core/backend/src/test/standalone/ElementRoundTrip.test.ts +++ b/core/backend/src/test/standalone/ElementRoundTrip.test.ts @@ -462,7 +462,7 @@ describe("Element and ElementAspect roundtrip test for all type of properties", const imodel = SnapshotDb.createEmpty(iModelPath, { rootSubject: { name: "RoundTripTest" } }); await imodel.importSchemas(new BackendRequestContext(), [testSchemaPath]); - const result: DbResult = imodel.nativeDb.resetBriefcaseId(BriefcaseIdValue.Standalone); + const result: DbResult = imodel.nativeDb.resetBriefcaseId(BriefcaseIdValue.Unassigned); assert.equal(DbResult.BE_SQLITE_OK, result); IModelTestUtils.createAndInsertPhysicalPartitionAndModel(imodel, Code.createEmpty(), true); let spatialCategoryId = SpatialCategory.queryCategoryIdByName(imodel, IModel.dictionaryId, categoryName); diff --git a/core/backend/src/test/standalone/IModel.test.ts b/core/backend/src/test/standalone/IModel.test.ts index 1e232b9988a..2b28c8079cc 100644 --- a/core/backend/src/test/standalone/IModel.test.ts +++ b/core/backend/src/test/standalone/IModel.test.ts @@ -64,14 +64,6 @@ function exerciseGc() { } } -function generateChangeSetId(): string { - let result = ""; - for (let i = 0; i < 20; ++i) { - result += Math.floor(Math.random() * 256).toString(16).padStart(2, "0"); - } - return result; -} - describe("iModel", () => { let imodel1: SnapshotDb; let imodel2: SnapshotDb; @@ -207,6 +199,7 @@ describe("iModel", () => { } stmt.dispose(); + Logger.initializeToConsole(); // reset back to console so future tests will log correctly }); it("should be able to get properties of an iIModel", () => { @@ -1708,7 +1701,8 @@ describe("iModel", () => { const snapshot = SnapshotDb.createEmpty(dbPath, { rootSubject: { name: "test" } }); const iModelId = snapshot.getGuid(); const contextId = Guid.createValue(); - const changeSetId = generateChangeSetId(); + const changeSetId = IModelTestUtils.generateChangeSetId(); + snapshot.nativeDb.saveProjectGuid(Guid.normalize(contextId)); snapshot.nativeDb.saveLocalValue("ParentChangeSetId", changeSetId); // even fake checkpoints need a changeSetId! snapshot.saveChanges(); snapshot.close(); @@ -1755,10 +1749,47 @@ describe("iModel", () => { checkpoint.close(); }); + it("should throw for invalid dbGuid in v2 checkpoint", async () => { + const dbPath = IModelTestUtils.prepareOutputFile("IModel", "TestCheckpoint.bim"); + const snapshot = SnapshotDb.createEmpty(dbPath, { rootSubject: { name: "test" } }); + const iModelId = Guid.createValue(); // This is wrong - it should be `snapshot.getGuid()`! + const contextId = Guid.createValue(); + const changeSetId = IModelTestUtils.generateChangeSetId(); + snapshot.nativeDb.saveProjectGuid(Guid.normalize(contextId)); + snapshot.nativeDb.saveLocalValue("ParentChangeSetId", changeSetId); + snapshot.saveChanges(); + snapshot.close(); + + // Mock iModelHub + const mockCheckpointV2: CheckpointV2 = { + wsgId: "INVALID", + ecId: "INVALID", + changeSetId, + containerAccessKeyAccount: "testAccount", + containerAccessKeyContainer: `imodelblocks-${iModelId}`, + containerAccessKeySAS: "testSAS", + containerAccessKeyDbName: "testDb", + }; + const checkpointsV2Handler = IModelHost.iModelClient.checkpointsV2; + sinon.stub(checkpointsV2Handler, "get").callsFake(async () => [mockCheckpointV2]); + sinon.stub(IModelHost.iModelClient, "checkpointsV2").get(() => checkpointsV2Handler); + + // Mock blockcacheVFS daemon + sinon.stub(BlobDaemon, "getDbFileName").callsFake(() => dbPath); + const daemonSuccessResult = { result: DbResult.BE_SQLITE_OK, errMsg: "" }; + sinon.stub(BlobDaemon, "command").callsFake(async () => daemonSuccessResult); + + process.env.BLOCKCACHE_DIR = "/foo/"; + const ctx = ClientRequestContext.current as AuthorizedClientRequestContext; + + const error = await getIModelError(SnapshotDb.openCheckpointV2({ requestContext: ctx, contextId, iModelId, changeSetId })); + expectIModelError(IModelStatus.ValidationFailed, error); + }); + it("should throw when opening checkpoint without blockcache dir env", async () => { process.env.BLOCKCACHE_DIR = ""; const ctx = ClientRequestContext.current as AuthorizedClientRequestContext; - const error = await getIModelError(SnapshotDb.openCheckpointV2({ requestContext: ctx, contextId: Guid.createValue(), iModelId: Guid.createValue(), changeSetId: generateChangeSetId() })); + const error = await getIModelError(SnapshotDb.openCheckpointV2({ requestContext: ctx, contextId: Guid.createValue(), iModelId: Guid.createValue(), changeSetId: IModelTestUtils.generateChangeSetId() })); expectIModelError(IModelStatus.BadRequest, error); }); @@ -1769,11 +1800,11 @@ describe("iModel", () => { sinon.stub(IModelHost.iModelClient, "checkpointsV2").get(() => checkpointsV2Handler); const ctx = ClientRequestContext.current as AuthorizedClientRequestContext; - let error = await getIModelError(SnapshotDb.openCheckpointV2({ requestContext: ctx, contextId: Guid.createValue(), iModelId: Guid.createValue(), changeSetId: generateChangeSetId() })); + let error = await getIModelError(SnapshotDb.openCheckpointV2({ requestContext: ctx, contextId: Guid.createValue(), iModelId: Guid.createValue(), changeSetId: IModelTestUtils.generateChangeSetId() })); expectIModelError(IModelStatus.NotFound, error); hubMock.callsFake(async () => [{} as any]); - error = await getIModelError(SnapshotDb.openCheckpointV2({ requestContext: ctx, contextId: Guid.createValue(), iModelId: Guid.createValue(), changeSetId: generateChangeSetId() })); + error = await getIModelError(SnapshotDb.openCheckpointV2({ requestContext: ctx, contextId: Guid.createValue(), iModelId: Guid.createValue(), changeSetId: IModelTestUtils.generateChangeSetId() })); expectIModelError(IModelStatus.BadRequest, error); }); @@ -1923,7 +1954,7 @@ describe("iModel", () => { assert.isTrue(standaloneDb1.isStandaloneDb()); assert.isTrue(standaloneDb1.isStandalone); assert.isFalse(standaloneDb1.isReadonly, "Expect standalone iModels to be read-write during create"); - assert.equal(standaloneDb1.getBriefcaseId(), BriefcaseIdValue.Standalone); + assert.equal(standaloneDb1.getBriefcaseId(), BriefcaseIdValue.Unassigned); assert.equal(standaloneDb1.pathName, standaloneFile1); assert.equal(standaloneDb1, StandaloneDb.tryFindByKey(standaloneDb1.key), "Should be in the list of open StandaloneDbs"); assert.isFalse(standaloneDb1.nativeDb.isEncrypted()); @@ -1961,10 +1992,10 @@ describe("iModel", () => { assert.isFalse(snapshotDb1.isReadonly, "Expect snapshots to be read-write during create"); assert.isFalse(snapshotDb2.isReadonly, "Expect snapshots to be read-write during create"); assert.isFalse(snapshotDb3.isReadonly, "Expect snapshots to be read-write during create"); - assert.equal(snapshotDb1.getBriefcaseId(), BriefcaseIdValue.Standalone); - assert.equal(snapshotDb2.getBriefcaseId(), BriefcaseIdValue.Standalone); - assert.equal(snapshotDb3.getBriefcaseId(), BriefcaseIdValue.Standalone); - assert.equal(imodel1.getBriefcaseId(), BriefcaseIdValue.Standalone); + assert.equal(snapshotDb1.getBriefcaseId(), BriefcaseIdValue.Unassigned); + assert.equal(snapshotDb2.getBriefcaseId(), BriefcaseIdValue.Unassigned); + assert.equal(snapshotDb3.getBriefcaseId(), BriefcaseIdValue.Unassigned); + assert.equal(imodel1.getBriefcaseId(), BriefcaseIdValue.Unassigned); assert.equal(snapshotDb1.pathName, snapshotFile1); assert.equal(snapshotDb2.pathName, snapshotFile2); assert.equal(snapshotDb3.pathName, snapshotFile3); @@ -2044,7 +2075,7 @@ describe("iModel", () => { // create snapshot from scratch without a password, then unnecessarily specify a password to open let snapshotDb1 = SnapshotDb.createFrom(imodel1, snapshotFile1); - assert.equal(snapshotDb1.getBriefcaseId(), BriefcaseIdValue.Standalone); + assert.equal(snapshotDb1.getBriefcaseId(), BriefcaseIdValue.Unassigned); snapshotDb1.close(); snapshotDb1 = SnapshotDb.openFile(snapshotFile1, { password: "unnecessaryPassword" }); assert.isTrue(snapshotDb1.isSnapshotDb()); @@ -2055,7 +2086,7 @@ describe("iModel", () => { // create snapshot from scratch and give it a password let snapshotDb2 = SnapshotDb.createEmpty(snapshotFile2, { rootSubject: { name: "Password-Protected" }, password: "password", createClassViews: true }); - assert.equal(snapshotDb2.getBriefcaseId(), BriefcaseIdValue.Standalone); + assert.equal(snapshotDb2.getBriefcaseId(), BriefcaseIdValue.Unassigned); const subjectName2 = "TestSubject2"; const subjectId2: Id64String = Subject.insert(snapshotDb2, IModel.rootSubjectId, subjectName2); assert.isTrue(Id64.isValidId64(subjectId2)); @@ -2070,7 +2101,7 @@ describe("iModel", () => { // create a new snapshot from a non-password-protected snapshot and then give it a password let snapshotDb3 = SnapshotDb.createFrom(imodel1, snapshotFile3, { password: "password" }); - assert.equal(snapshotDb3.getBriefcaseId(), BriefcaseIdValue.Standalone); + assert.equal(snapshotDb3.getBriefcaseId(), BriefcaseIdValue.Unassigned); snapshotDb3.close(); snapshotDb3 = SnapshotDb.openFile(snapshotFile3, { password: "password" }); assert.isTrue(snapshotDb3.isSnapshotDb()); diff --git a/core/backend/src/test/standalone/IModelHost.test.ts b/core/backend/src/test/standalone/IModelHost.test.ts index fb0bad2ec86..1eb4c9b5389 100644 --- a/core/backend/src/test/standalone/IModelHost.test.ts +++ b/core/backend/src/test/standalone/IModelHost.test.ts @@ -9,10 +9,12 @@ import { BriefcaseManager } from "../../BriefcaseManager"; import { RpcRegistry } from "@bentley/imodeljs-common"; import { IModelTestUtils } from "../IModelTestUtils"; import { Schemas } from "../../Schema"; +import sinon = require("sinon"); describe("IModelHost", () => { afterEach(async () => { + sinon.restore(); // Restore the backend to the initial state. await IModelTestUtils.shutdownBackend(); await IModelTestUtils.startBackend(); @@ -36,6 +38,34 @@ describe("IModelHost", () => { expect(Schemas.getRegisteredSchema("Functional")).to.exist; }); + it("should raise onAfterStartup events", async () => { + await IModelTestUtils.shutdownBackend(); + + const eventHandler = sinon.spy(); + IModelHost.onAfterStartup.addOnce(eventHandler); + const promise = IModelHost.startup(); + expect(eventHandler.called).to.be.false; + await promise; + expect(eventHandler.calledOnce).to.be.true; + }); + + it("should raise onBeforeShutdown events", async () => { + const eventHandler = sinon.spy(); + IModelHost.onBeforeShutdown.addOnce(eventHandler); + await IModelTestUtils.shutdownBackend(); + expect(eventHandler.calledOnce).to.be.true; + }); + + it("should auto-shutdown on process beforeExit event", async () => { + expect(IModelHost.isValid).to.be.true; + const eventHandler = sinon.spy(); + IModelHost.onBeforeShutdown.addOnce(eventHandler); + process.emit("beforeExit", 0); + await new Promise((resolve) => setImmediate(resolve)); + expect(eventHandler.calledOnce).to.be.true; + expect(IModelHost.isValid).to.be.false; + }); + it("should set the briefcase cache directory to expected locations", async () => { // Shutdown IModelHost to allow this test to use it. await IModelTestUtils.shutdownBackend(); diff --git a/core/backend/src/test/standalone/NativeAppStorage.test.ts b/core/backend/src/test/standalone/NativeAppStorage.test.ts index a0d9a69ab67..168a7938651 100644 --- a/core/backend/src/test/standalone/NativeAppStorage.test.ts +++ b/core/backend/src/test/standalone/NativeAppStorage.test.ts @@ -69,25 +69,42 @@ describe("NativeApp storage backend", () => { test1.setData("key1", 2222); assert.isNumber(test1.getData("key1")); assert.equal(test1.getData("key1"), 2222); + assert.equal(test1.getNumber("key1"), 2222); + assert.equal(test1.getString("key1"), undefined); + assert.equal(test1.getUint8Array("key1"), undefined); + assert.equal(test1.getBoolean("key1"), undefined); test1.setData("key1", "Hello, World"); assert.isString(test1.getData("key1")); assert.equal(test1.getData("key1"), "Hello, World"); + assert.equal(test1.getString("key1"), "Hello, World"); + assert.equal(test1.getUint8Array("key1"), undefined); + assert.equal(test1.getBoolean("key1"), undefined); + assert.equal(test1.getNumber("key1"), undefined); test1.setData("key1", true); assert.isBoolean(test1.getData("key1")); assert.equal(test1.getData("key1"), true); + assert.equal(test1.getBoolean("key1"), true); + assert.equal(test1.getString("key1"), undefined); + assert.equal(test1.getUint8Array("key1"), undefined); + assert.equal(test1.getNumber("key1"), undefined); test1.setData("key1", false); assert.isBoolean(test1.getData("key1")); assert.equal(test1.getData("key1"), false); + assert.equal(test1.getBoolean("key1"), false); const testArray = new Uint8Array([1, 2, 3, 4, 5]); test1.setData("key1", testArray); assert.isTrue(test1.getData("key1") instanceof Uint8Array); - assert.equal((test1.getData("key1") as Uint8Array).length, testArray.length); + assert.equal(test1.getUint8Array("key1")!.length, testArray.length); + assert.equal(test1.getBoolean("key1"), undefined); + assert.equal(test1.getString("key1"), undefined); + assert.equal(test1.getNumber("key1"), undefined); test1.removeAll(); assert.equal(test1.getKeys().length, 0); + test1.close(true); }); it("Storage open/close test", () => { @@ -103,4 +120,5 @@ describe("NativeApp storage backend", () => { storage.close(true); }); }); + }); diff --git a/core/backend/src/test/standalone/TxnManager.test.ts b/core/backend/src/test/standalone/TxnManager.test.ts index e85397a677c..e8a8d6aede2 100644 --- a/core/backend/src/test/standalone/TxnManager.test.ts +++ b/core/backend/src/test/standalone/TxnManager.test.ts @@ -15,7 +15,8 @@ import { } from "../../imodeljs-backend"; import { IModelTestUtils, TestElementDrivesElement, TestPhysicalObject, TestPhysicalObjectProps } from "../IModelTestUtils"; -describe("TxnManager", () => { +// TEMPORARILY disabled until we can figure out why they fail on CI jobs randomly +describe.skip("TxnManager", () => { let imodel: StandaloneDb; let props: TestPhysicalObjectProps; let testFileName: string; @@ -35,6 +36,8 @@ describe("TxnManager", () => { }; before(async () => { + IModelTestUtils.setupDebugLogLevels(); // temporary, to debug random errors on CI job + IModelTestUtils.registerTestBimSchema(); testFileName = IModelTestUtils.prepareOutputFile("TxnManager", "TxnManagerTest.bim"); const seedFileName = IModelTestUtils.resolveAssetFile("test.bim"); @@ -64,7 +67,11 @@ describe("TxnManager", () => { imodel.nativeDb.deleteAllTxns(); }); - after(() => imodel.close()); + after(() => { + imodel.close(); + IModelTestUtils.resetDebugLogLevels(); // temporary, to debug random errors on CI job + + }); it("TxnManager", async () => { const models = imodel.models; @@ -395,7 +402,7 @@ describe("TxnManager", () => { id2 = elements.insertElement(props); imodel.saveChanges("2 inserts"); accum.expectNumValidations(1); - accum.expectChanges({ inserted: [ id1, id2 ] }); + accum.expectChanges({ inserted: [id1, id2] }); }); let elem1: TestPhysicalObject; @@ -409,7 +416,7 @@ describe("TxnManager", () => { elem2.update(); imodel.saveChanges("2 updates"); accum.expectNumValidations(1); - accum.expectChanges({ updated: [ id1, id2 ] }); + accum.expectChanges({ updated: [id1, id2] }); }); EventAccumulator.testElements(imodel, (accum) => { @@ -417,14 +424,14 @@ describe("TxnManager", () => { elem2.delete(); imodel.saveChanges("2 deletes"); accum.expectNumValidations(1); - accum.expectChanges({ deleted: [ id1, id2 ] }); + accum.expectChanges({ deleted: [id1, id2] }); }); // Undo EventAccumulator.testElements(imodel, (accum) => { imodel.txns.reverseSingleTxn(); accum.expectNumUndoRedo(1); - accum.expectChanges({ inserted: [ id1, id2 ] }); + accum.expectChanges({ inserted: [id1, id2] }); accum.expectNumApplyChanges(1); accum.expectNumValidations(0); }); @@ -432,32 +439,32 @@ describe("TxnManager", () => { EventAccumulator.testElements(imodel, (accum) => { imodel.txns.reverseSingleTxn(); accum.expectNumUndoRedo(1); - accum.expectChanges({ updated: [ id1, id2 ] }); + accum.expectChanges({ updated: [id1, id2] }); }); EventAccumulator.testElements(imodel, (accum) => { imodel.txns.reverseSingleTxn(); accum.expectNumUndoRedo(1); - accum.expectChanges({ deleted: [ id1, id2 ] }); + accum.expectChanges({ deleted: [id1, id2] }); }); // Redo EventAccumulator.testElements(imodel, (accum) => { imodel.txns.reinstateTxn(); accum.expectNumUndoRedo(1); - accum.expectChanges({ inserted: [ id1, id2 ] }); + accum.expectChanges({ inserted: [id1, id2] }); }); EventAccumulator.testElements(imodel, (accum) => { imodel.txns.reinstateTxn(); accum.expectNumUndoRedo(1); - accum.expectChanges({ updated: [ id1, id2 ] }); + accum.expectChanges({ updated: [id1, id2] }); }); EventAccumulator.testElements(imodel, (accum) => { imodel.txns.reinstateTxn(); accum.expectNumUndoRedo(1); - accum.expectChanges({ deleted: [ id1, id2 ] }); + accum.expectChanges({ deleted: [id1, id2] }); accum.expectNumApplyChanges(1); accum.expectNumValidations(0); }); @@ -471,9 +478,9 @@ describe("TxnManager", () => { // We received 3 separate "elements changed" events - one for each txn - and just concatenated the lists. accum.expectChanges({ - inserted: [ id1, id2 ], - updated: [ id1, id2 ], - deleted: [ id1, id2 ], + inserted: [id1, id2], + updated: [id1, id2], + deleted: [id1, id2], }); }); @@ -486,9 +493,9 @@ describe("TxnManager", () => { // We received 3 separate "elements changed" events - one for each txn - and just concatenated the lists. accum.expectChanges({ - inserted: [ id1, id2 ], - updated: [ id1, id2 ], - deleted: [ id1, id2 ], + inserted: [id1, id2], + updated: [id1, id2], + deleted: [id1, id2], }); }); }); @@ -501,7 +508,7 @@ describe("TxnManager", () => { newModelId = PhysicalModel.insert(imodel, IModel.rootSubjectId, Guid.createValue()); imodel.saveChanges("1 insert"); accum.expectNumValidations(1); - accum.expectChanges({ inserted: [ newModelId ] }); + accum.expectChanges({ inserted: [newModelId] }); }); // NB: Updates to existing models never produce events. I don't think I want to change that as part of this PR. @@ -514,21 +521,21 @@ describe("TxnManager", () => { imodel.models.updateGeometryGuid(existingModelId); imodel.saveChanges("1 update"); accum.expectNumValidations(1); - accum.expectChanges({ }); + accum.expectChanges({}); }); EventAccumulator.testModels(imodel, (accum) => { imodel.elements.insertElement(props); imodel.saveChanges("insert 1 geometric element"); accum.expectNumValidations(1); - accum.expectChanges({ }); + accum.expectChanges({}); }); EventAccumulator.testModels(imodel, (accum) => { newModel.delete(); imodel.saveChanges("1 delete"); accum.expectNumValidations(1); - accum.expectChanges({ deleted: [ newModelId ] }); + accum.expectChanges({ deleted: [newModelId] }); accum.expectNumApplyChanges(0); }); @@ -538,28 +545,28 @@ describe("TxnManager", () => { imodel.txns.reverseSingleTxn(); accum.expectNumUndoRedo(1); accum.expectNumApplyChanges(1); - accum.expectChanges({ inserted: [ newModelId ] }); + accum.expectChanges({ inserted: [newModelId] }); }); EventAccumulator.testModels(imodel, (accum) => { imodel.txns.reverseSingleTxn(); accum.expectNumUndoRedo(1); accum.expectNumApplyChanges(1); - accum.expectChanges({ }); + accum.expectChanges({}); }); EventAccumulator.testModels(imodel, (accum) => { imodel.txns.reverseSingleTxn(); accum.expectNumUndoRedo(1); accum.expectNumApplyChanges(1); - accum.expectChanges({ }); + accum.expectChanges({}); }); EventAccumulator.testModels(imodel, (accum) => { imodel.txns.reverseSingleTxn(); accum.expectNumUndoRedo(1); accum.expectNumApplyChanges(1); - accum.expectChanges({ deleted: [ newModelId ] }); + accum.expectChanges({ deleted: [newModelId] }); }); // Redo @@ -569,7 +576,7 @@ describe("TxnManager", () => { accum.expectNumUndoRedo(4); accum.expectNumApplyChanges(4); - accum.expectChanges({ inserted: [ newModelId ], deleted: [ newModelId ] }); + accum.expectChanges({ inserted: [newModelId], deleted: [newModelId] }); }); }); diff --git a/core/bentley/package.json b/core/bentley/package.json index b7bceec5164..ff63a5e098d 100644 --- a/core/bentley/package.json +++ b/core/bentley/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/bentleyjs-core", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "Bentley JavaScript core components", "main": "lib/bentleyjs-core.js", "typings": "lib/bentleyjs-core", @@ -32,8 +32,8 @@ "url": "http://www.bentley.com" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", "@types/mocha": "^5.2.5", diff --git a/core/bentley/src/Assert.ts b/core/bentley/src/Assert.ts index 53502651a5c..166aa06a47d 100644 --- a/core/bentley/src/Assert.ts +++ b/core/bentley/src/Assert.ts @@ -6,15 +6,16 @@ * @module Utils */ -// @todo Needs to be commented out in a production environment. /** - * Assert by throwing a programmer error. + * Asserts that a condition is `true`, and in development builds throws an error if it is not. + * This is an [assertion function](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functions) that alters the + * behavior of the TypeScript compiler. * @param condition The result of a boolean expression. * @param msg An optional message to include in the thrown exception. Defaults to "Programmer Error". * @throws Error containing the specified message if condition is false. * @note This function should be used to validate conditions that should never realistically occur, or * which indicate a misuse of the API which should be eliminated during development. - * @beta Need strategy for removing assert in production builds + * @public */ export function assert(condition: boolean, msg?: string): asserts condition { if (!condition) diff --git a/core/bentley/src/BeEvent.ts b/core/bentley/src/BeEvent.ts index 84a99815fc9..a4e3bd768ec 100644 --- a/core/bentley/src/BeEvent.ts +++ b/core/bentley/src/BeEvent.ts @@ -138,15 +138,14 @@ export class BeUiEvent extends BeEvent<(args: TEventArgs) => void> { /** * A list of BeEvent objects, accessible by an event name. * This class may be used instead of explicitly declaring each BeEvent as a member of a containing class. - * @beta Is this class used? + * @public */ export class BeEventList { private _events: { [name: string]: BeEvent | undefined } = {}; /** - * Gets the BeEvent associated with a name. + * Gets the event associated with the specified name, creating the event if it does not already exist. * @param name The name of the event. - * @note the BeEvent will be created if none existed before this call. */ public get(name: string): BeEvent { let event = this._events[name]; @@ -159,7 +158,7 @@ export class BeEventList { } /** - * Removes the BeEvent associated with a name. + * Removes the event associated with a name. * @param name The name of the event. */ public remove(name: string): void { diff --git a/core/bentley/src/ByteStream.ts b/core/bentley/src/ByteStream.ts index b5b4886355c..ac7c638961e 100644 --- a/core/bentley/src/ByteStream.ts +++ b/core/bentley/src/ByteStream.ts @@ -9,11 +9,12 @@ import { assert } from "./Assert"; import { Id64, Id64String } from "./Id"; -/** Allows the contents of an ArrayBuffer to be consumed sequentially using methods to extract +/** Allows the contents of an [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) + * to be consumed sequentially using methods to extract * data of a particular type from the bytes beginning at the current read position. * Methods and properties beginning with 'next' consume data at the current read position and advance it * by the size of the data read. The read position can also be directly adjusted by the caller. - * @beta + * @public */ export class ByteStream { private readonly _view: DataView; @@ -39,33 +40,35 @@ export class ByteStream { /** Returns true if the current read position has been advanced past the end of the stream */ public get isPastTheEnd(): boolean { return this.curPos > this.length; } - /** The current read position as an index into the stream of bytes */ + /** The current read position as an index into the stream of bytes. */ public get curPos(): number { return this._curPos; } public set curPos(pos: number) { this._curPos = pos; assert(!this.isPastTheEnd); } - /** Add the specified number of bytes to the current read position */ + /** Adds the specified number of bytes to the current read position */ public advance(numBytes: number): boolean { this.curPos = (this.curPos + numBytes); return !this.isPastTheEnd; } /** Subtracts the specified number of bytes from the current read position */ public rewind(numBytes: number): boolean { if (this.curPos - numBytes < 0) return false; this.curPos = this.curPos - numBytes; return true; } /** Resets the current read position to the beginning of the stream */ public reset(): void { this.curPos = 0; } - /** Read a uint8 at the current read position and advance by 1 byte. */ + /** Read a unsigned 8-bit integer from the current read position and advance by 1 byte. */ public get nextUint8(): number { return this.read(1, (view) => view.getUint8(this.curPos)); } - /** Read a uint16 at the current read position and advance by 2 bytes. */ + /** Read an unsigned 16-bit integer from the current read position and advance by 2 bytes. */ public get nextUint16(): number { return this.read(2, (view) => view.getUint16(this.curPos, true)); } - /** Read a uint32 at the current read position and advance by 4 bytes. */ + /** Read an unsigned 32-bit integer from the current read position and advance by 4 bytes. */ public get nextUint32(): number { return this.read(4, (view) => view.getUint32(this.curPos, true)); } - /** Read an int32 at the current read position and advance by 4 bytes. */ + /** Read a signed 32-bit integer from the current read position and advance by 4 bytes. */ public get nextInt32(): number { return this.read(4, (view) => view.getInt32(this.curPos, true)); } - /** Read a 32-bit floating point number at the current read position and advance by 4 bytes. */ + /** Read a 32-bit floating point number from the current read position and advance by 4 bytes. */ public get nextFloat32(): number { return this.read(4, (view) => view.getFloat32(this.curPos, true)); } - /** Read a 64-bit floating point number at the current read position and advance by 8 bytes. */ + /** Read a 64-bit floating point number from the current read position and advance by 8 bytes. */ public get nextFloat64(): number { return this.read(8, (view) => view.getFloat64(this.curPos, true)); } - /** Read a uint64 at the current read position, advance by 8 bytes, and return the uint64 value as an Id64String. */ + /** Read an unsigned 64-bit integer from the current read position, advance by 8 bytes, and return the 64-bit value as an Id64String. */ public get nextId64(): Id64String { return Id64.fromUint32Pair(this.nextUint32, this.nextUint32); } - /** Read the next numBytes bytes into a Uint8Array and advance by numBytes. */ + /** Read the specified number of bytes beginning at the current read position into a Uint8Array and advance by the specified number of byte. + * @param numBytes The number of bytes to read. + */ public nextBytes(numBytes: number): Uint8Array { const bytes = new Uint8Array(this.arrayBuffer, this.curPos + this._byteOffset, numBytes); this.advance(numBytes); @@ -77,6 +80,7 @@ export class ByteStream { return new Uint8Array(this.arrayBuffer, readPos + this._byteOffset, numBytes); } + /** Read the specified number of unsigned 32-bit integers from the current read position and advance the read position. */ public nextUint32s(numUint32s: number): Uint32Array { const numBytes = numUint32s * 4; const uint32s = new Uint32Array(this.arrayBuffer, this.curPos + this._byteOffset, numUint32s); diff --git a/core/bentley/src/CompressedId64Set.ts b/core/bentley/src/CompressedId64Set.ts index 9bf3c2bb37e..70fa898db81 100644 --- a/core/bentley/src/CompressedId64Set.ts +++ b/core/bentley/src/CompressedId64Set.ts @@ -11,14 +11,7 @@ import { Id64, Id64Array, Id64Set, Id64String } from "./Id"; import { OrderedId64Iterable } from "./OrderedId64Iterable"; import { SortedArray } from "./SortedArray"; -/** A compact string representation of an [[Id64Set]]. Such a representation is useful when serializing potentially very large - * sets of Ids. - * @see [[CompressedId64Set.iterable]] to efficiently iterate the Ids represented by a compact string. - * @see [[CompressedId64Set.compressSet]] and [[CompressedId64Set.compressArray]] to produce a compact string from a collection of Ids. - * @see [[CompressedId64Set.decompressSet]] and [[CompressedId64Set.decompressArray]] to produce a collection of Ids from a compact string. - * @see [[OrderedId64Iterable]] for a generic representation of an ordered set of Ids (compressed or otherwise). - * @beta - */ +/** @public */ export type CompressedId64Set = string; /** A compact string representation of an [[Id64Set]]. Such a representation is useful when serializing potentially very large @@ -27,7 +20,8 @@ export type CompressedId64Set = string; * @see [[CompressedId64Set.compressSet]] and [[CompressedId64Set.compressArray]] to produce a compact string from a collection of Ids. * @see [[CompressedId64Set.decompressSet]] and [[CompressedId64Set.decompressArray]] to produce a collection of Ids from a compact string. * @see [[OrderedId64Iterable]] for a generic representation of an ordered set of Ids (compressed or otherwise). - * @beta + * @see [[MutableCompressedId64Set]] for a mutable version. + * @public */ export namespace CompressedId64Set { // eslint-disable-line @typescript-eslint/no-redeclare function isHexDigit(ch: number): boolean { @@ -54,7 +48,6 @@ export namespace CompressedId64Set { // eslint-disable-line @typescript-eslint/n * @note Invalid Ids are ignored. * @see [[CompressedId64Set.compressArray]] to perform the same operation on an [[Id64Array]]. * @see [[CompressedId64Set.decompressSet]] to perform the inverse operation. - * @beta */ export function compressSet(ids: Id64Set): CompressedId64Set { const arr = Array.from(ids); @@ -69,7 +62,6 @@ export namespace CompressedId64Set { // eslint-disable-line @typescript-eslint/n * @note Invalid Ids are ignored. * @see [[CompressedId64Set.decompressArray]] to perform the inverse operation. * @see [[OrderedId64Iterable.sortArray]] to ensure the Ids are properly sorted. - * @beta */ export function compressArray(ids: Id64Array): CompressedId64Set { return compressIds(ids); @@ -82,7 +74,6 @@ export namespace CompressedId64Set { // eslint-disable-line @typescript-eslint/n * @note Invalid Ids are ignored. * @see [[CompressedId64Set.iterable]] to perform the inverse operation. * @see [[OrderedId64Iterable.sortArray]] or [[OrderedId64Iterable.compare]] to ensure the Ids are properly sorted. - * @beta */ export function compressIds(ids: OrderedId64Iterable): CompressedId64Set { if ("string" === typeof ids) @@ -209,7 +200,6 @@ export namespace CompressedId64Set { // eslint-disable-line @typescript-eslint/n /** Supplies an iterator over the [[Id64String]]s in a [[CompressedId64Set]]. * The Ids are iterated in ascending order based on their unsigned 64-bit integer values. - * @alpha */ export function* iterator(ids: CompressedId64Set): Iterator { if (0 === ids.length) @@ -305,7 +295,6 @@ export namespace CompressedId64Set { // eslint-disable-line @typescript-eslint/n /** Supplies an iterable over the [[Id64String]]s in a [[CompressedId64Set]]. * The Ids are iterated in ascending order based on their unsigned 64-bit integer values. - * @alpha */ export function iterable(ids: CompressedId64Set): OrderedId64Iterable { return { @@ -321,7 +310,6 @@ export namespace CompressedId64Set { // eslint-disable-line @typescript-eslint/n * @see [[CompressedId64Set.compressSet]] to perform the inverse operation. * @see [[CompressedId64Set.decompressArray]] to decompress as an [[Id64Array]] instead. * @see [[CompressedId64Set.iterable]] to efficiently iterate the Ids. - * @beta */ export function decompressSet(compressedIds: CompressedId64Set, out?: Id64Set): Id64Set { const set = out ?? new Set(); @@ -340,7 +328,6 @@ export namespace CompressedId64Set { // eslint-disable-line @typescript-eslint/n * @see [[CompressedId64Set.compressArray]] to perform the inverse operation. * @see [[CompressedId64Set.decompressSet]] to decompress as an [[Id64Set]] instead. * @see [[CompressedId64Set.iterable]] to efficiently iterate the Ids. - * @beta */ export function decompressArray(compressedIds: CompressedId64Set, out?: Id64Array): Id64Array { const arr = out ?? []; @@ -351,12 +338,17 @@ export namespace CompressedId64Set { // eslint-disable-line @typescript-eslint/n } } -/** @alpha */ +/** A [[SortedArray]] of unique [[Id64String]]s sorted in ascending order by the 64-bit unsigned integer values of the Ids. + * @see [[CompressedId64Set]] for an immutable compact string representation. + * @public + */ export class OrderedId64Array extends SortedArray { + /** Construct a new, empty array. */ public constructor() { super((lhs, rhs) => OrderedId64Iterable.compare(lhs, rhs)); } + /** An iterable that iterates over the Ids in sorted order. */ public get ids(): OrderedId64Iterable { return this._array; } } @@ -364,7 +356,7 @@ export class OrderedId64Array extends SortedArray { * Internally the set of Ids is maintained as a [[CompressedId64Set]] string representation. * Insertions and removals are buffered until the string representation needs to be recomputed. The string representation is recomputed by every public method except [[add]] and [[delete]] - * therefore, if multiple removals and/or insertions are required, it is most efficient to perform them all before invoking other methods. - * @alpha + * @public */ export class MutableCompressedId64Set implements OrderedId64Iterable { private _ids: CompressedId64Set; @@ -382,7 +374,9 @@ export class MutableCompressedId64Set implements OrderedId64Iterable { return this._ids; } - /** Add the specified Id to the set. */ + /** Add the specified Id to the set. + * @throws Error if `id` is not a valid [[Id64String]]. + */ public add(id: Id64String): void { if (!Id64.isValidId64(id)) throw new Error("MutableCompressedId64Set.add: invalid Id"); @@ -391,7 +385,9 @@ export class MutableCompressedId64Set implements OrderedId64Iterable { this._inserted.insert(id); } - /** Remove the specified Id from the set. */ + /** Remove the specified Id from the set. + * @throws Error if `id` is not a valid [[Id64String]]. + */ public delete(id: Id64String): void { if (!Id64.isValidId64(id)) throw new Error("MutableCompressedId64Set.delete: invalid Id"); diff --git a/core/bentley/src/ObservableSet.ts b/core/bentley/src/ObservableSet.ts index 7b4a50ae8e2..a206f0bafe1 100644 --- a/core/bentley/src/ObservableSet.ts +++ b/core/bentley/src/ObservableSet.ts @@ -8,8 +8,8 @@ import { BeEvent } from "./BeEvent"; -/** A standard Set that emits events when its contents change. - * @beta +/** A standard [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) that emits events when its contents change. + * @public */ export class ObservableSet extends Set { /** Emitted after `item` is added to this set. */ @@ -19,7 +19,9 @@ export class ObservableSet extends Set { /** Emitted after this set's contents are cleared. */ public readonly onCleared = new BeEvent<() => void>(); - /** Construct a new ObservableSet from the specified elements. */ + /** Construct a new ObservableSet. + * @param elements Optional elements with which to populate the new set. + */ public constructor(elements?: Iterable | undefined) { // NB: Set constructor will invoke add(). Do not override until initialized. super(elements); diff --git a/core/bentley/src/OrderedId64Iterable.ts b/core/bentley/src/OrderedId64Iterable.ts index af48def95e1..7e1cf229fc3 100644 --- a/core/bentley/src/OrderedId64Iterable.ts +++ b/core/bentley/src/OrderedId64Iterable.ts @@ -10,13 +10,7 @@ import { assert } from "./Assert"; import { CompressedId64Set } from "./CompressedId64Set"; import { Id64Array, Id64String } from "./Id"; -/** A collection of **valid** [[Id64String]]s sorted in ascending order by the unsigned 64-bit integer value of the Ids. - * This ordering is a requirement for several groups of APIs including [[CompressedId64Set]]. - * When used as input to a function, duplicate Ids are ignored; when returned as a function output, no duplicates are present. - * @see [[CompressedId64Set]] for a compact string representation of such an ordered collection. - * @see [[OrderedId64Iterable.compare]] for a function that compares Ids based on this criterion. - * @beta - */ +/** @public */ export type OrderedId64Iterable = Iterable; /** A collection of **valid** [[Id64String]]s sorted in ascending order by the unsigned 64-bit integer value of the Ids. @@ -24,15 +18,16 @@ export type OrderedId64Iterable = Iterable; * When used as input to a function, duplicate Ids are ignored; when returned as a function output, no duplicates are present. * @see [[CompressedId64Set]] for a compact string representation of such an ordered collection. * @see [[OrderedId64Iterable.compare]] for a function that compares Ids based on this criterion. - * @beta + * @see [[OrderedId64Array]] for a mutable implementation. + * @public */ export namespace OrderedId64Iterable { // eslint-disable-line @typescript-eslint/no-redeclare - /** An ordered comparison of [[Id64String]]s suitable for use with sorting routines like `Array.sort` and sorted containers + /** An ordered comparison of [[Id64String]]s suitable for use with sorting routines like + * [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) and sorted containers * like [[SortedArray]] and [[Dictionary]]. The comparison compares the 64-bit numerical values of the two Ids, returning a negative number if lhs < rhs, * a positive number if lhs > rhs, or zero if lhs == rhs. * The default string comparison is fine (and more efficient) when numerical ordering is not required; use this instead if you want e.g., "0x100" to be greater than "0xf". * @see [[OrderedId64Iterable.sortArray]] for a convenient way to sort an array of Id64Strings. - * @beta */ export function compare(lhs: Id64String, rhs: Id64String): number { if (lhs.length !== rhs.length) @@ -49,17 +44,17 @@ export namespace OrderedId64Iterable { // eslint-disable-line @typescript-eslint /** Sort an array of [[Id64String]]s **in-place** in ascending order by their 64-bit numerical values. * @see [[OrderedId64Iterable.compare]] for the comparison routine used. * @returns the input array. - * @note This function returns its input for consistency with Javascript's `Array.sort` method. It **does not** create a **new** array. - * @beta + * @note This function returns its input for consistency with Javascript's + * [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) method. + * It **does not** create a **new** array. */ export function sortArray(ids: Id64Array): Id64Array { ids.sort((x, y) => compare(x, y)); return ids; } - /** Given two ordered collections of Ids, determine whether they are identical sets. Duplicate Ids are ignored. + /** Given two ordered collections of [[Id64String]]s, determine whether they are identical sets. Duplicate Ids are ignored. * @note If the inputs are not ordered as required by [[OrderedId64Iterable]], the results are unpredictable. - * @beta */ export function areEqualSets(ids1: OrderedId64Iterable, ids2: OrderedId64Iterable): boolean { const leftIter = uniqueIterator(ids1); @@ -83,10 +78,9 @@ export namespace OrderedId64Iterable { // eslint-disable-line @typescript-eslint return false; } - /** Given an ordered collection of Ids, determine if it contains any Ids. + /** Given an ordered collection of [[Id64String]]s, determine if it contains any Ids. * @param ids A well-formed, ordered collection of zero or more valid Ids. * @returns true if the input represents an empty set of Ids. The result is unspecified if the input does not meet the criteria for the input type. - * @beta */ export function isEmptySet(ids: OrderedId64Iterable | CompressedId64Set): boolean { if (typeof ids === "string") @@ -95,17 +89,15 @@ export namespace OrderedId64Iterable { // eslint-disable-line @typescript-eslint return true === ids[Symbol.iterator]().next().done; } - /** Given an ordered collection of Ids possibly containing duplicates, produce an ordered collection containing no duplicates. + /** Given an ordered collection of [[Id64String]]s possibly containing duplicates, produce an ordered collection containing no duplicates. * @note If the inputs are not ordered as required by [[OrderedId64Iterable]], the results are unpredictable. - * @beta */ export function unique(ids: OrderedId64Iterable): OrderedId64Iterable { return { [Symbol.iterator]: () => uniqueIterator(ids) }; } - /** Given an ordered collection of Ids possibly containing duplicates, produce an ordered iterator over the distinct Ids, eliminating duplicates. + /** Given an ordered collection of [[Id64String]]s possibly containing duplicates, produce an ordered iterator over the distinct Ids, eliminating duplicates. * @note If the inputs are not ordered as required by [[OrderedId64Iterable]], the results are unpredictable. - * @beta */ export function* uniqueIterator(ids: OrderedId64Iterable) { const iter = ids[Symbol.iterator](); @@ -122,33 +114,29 @@ export namespace OrderedId64Iterable { // eslint-disable-line @typescript-eslint } } - /** Given two ordered collections of Ids, produce a collection representing their union - i.e., the Ids that are present in either or both collections. + /** Given two ordered collections of [[Id64String]]s, produce a collection representing their union - i.e., the Ids that are present in either or both collections. * @note If the inputs are not ordered as required by [[OrderedId64Iterable]], the results are unpredictable. - * @beta */ export function union(ids1: OrderedId64Iterable, ids2: OrderedId64Iterable): OrderedId64Iterable { return { [Symbol.iterator]: () => unionIterator(ids1, ids2) }; } - /** Given two ordered collections of Ids, produce an iterator representing their intersection - i.e., the Ids that are present in both collections. + /** Given two ordered collections of [[Id64String]]s, produce an iterator representing their intersection - i.e., the Ids that are present in both collections. * @note If the inputs are not ordered as required by [[OrderedId64Iterable]], the results are unpredictable. - * @beta */ export function intersection(ids1: OrderedId64Iterable, ids2: OrderedId64Iterable): OrderedId64Iterable { return { [Symbol.iterator]: () => intersectionIterator(ids1, ids2) }; } - /** Given two ordered collections of Ids, produce an iterator representing their difference - i.e., the Ids that are present in `ids1` but not present in `ids2`. + /** Given two ordered collections of [[Id64String]]s, produce an iterator representing their difference - i.e., the Ids that are present in `ids1` but not present in `ids2`. * @note If the inputs are not ordered as required by [[OrderedId64Iterable]], the results are unpredictable. - * @beta */ export function difference(ids1: OrderedId64Iterable, ids2: OrderedId64Iterable): OrderedId64Iterable { return { [Symbol.iterator]: () => differenceIterator(ids1, ids2) }; } - /** Given two ordered collections of Ids, produce an iterator representing their union - i.e., the Ids that are present in either or both collections. + /** Given two ordered collections of [[Id64String]]s, produce an iterator representing their union - i.e., the Ids that are present in either or both collections. * @note If the inputs are not ordered as required by [[OrderedId64Iterable]], the results are unpredictable. - * @beta */ export function* unionIterator(ids1: OrderedId64Iterable, ids2: OrderedId64Iterable) { const leftIter = ids1[Symbol.iterator](); @@ -194,9 +182,8 @@ export namespace OrderedId64Iterable { // eslint-disable-line @typescript-eslint } } - /** Given two ordered collections of Ids, produce an iterator representing their intersection - i.e., the Ids that are present in both collections. + /** Given two ordered collections of [[Id64String]]s, produce an iterator representing their intersection - i.e., the Ids that are present in both collections. * @note If the inputs are not ordered as required by [[OrderedId64Iterable]], the results are unpredictable. - * @beta */ export function* intersectionIterator(ids1: OrderedId64Iterable, ids2: OrderedId64Iterable) { const leftIter = ids1[Symbol.iterator](); @@ -229,9 +216,8 @@ export namespace OrderedId64Iterable { // eslint-disable-line @typescript-eslint } } - /** Given two ordered collections of Ids, produce an iterator representing their difference - i.e., the Ids that are present in `ids1` but not present in `ids2`. + /** Given two ordered collections of [[Id64String]]s, produce an iterator representing their difference - i.e., the Ids that are present in `ids1` but not present in `ids2`. * @note If the inputs are not ordered as required by [[OrderedId64Iterable]], the results are unpredictable. - * @beta */ export function* differenceIterator(ids1: OrderedId64Iterable, ids2: OrderedId64Iterable) { const leftIter = ids1[Symbol.iterator](); diff --git a/core/bentley/src/ProcessDetector.ts b/core/bentley/src/ProcessDetector.ts index df80e330994..efb326347eb 100644 --- a/core/bentley/src/ProcessDetector.ts +++ b/core/bentley/src/ProcessDetector.ts @@ -9,7 +9,8 @@ // Portions modified from package 'detect-gpu': https://github.com/TimvanScherpenzeel/detect-gpu/blob/master/src/index.ts /** Functions to determine the type of JavaScript process currently executing - * @beta + * Portions derived from the [detect-gpu package](https://github.com/TimvanScherpenzeel/detect-gpu/blob/master/src/index.ts) + * @public */ export class ProcessDetector { diff --git a/core/bentley/src/partitionArray.ts b/core/bentley/src/partitionArray.ts index a5e1e653e84..e2d581113d1 100644 --- a/core/bentley/src/partitionArray.ts +++ b/core/bentley/src/partitionArray.ts @@ -21,7 +21,7 @@ * for (let i = 0; i < list.length; i++) * assert(isEven(list[i]) === i < firstOddIndex); * ``` - * @beta + * @public */ export function partitionArray(array: T[], criterion: (element: T) => boolean): number { let index = 0; diff --git a/core/common/package.json b/core/common/package.json index 299a4c948ae..895dccdf9bd 100644 --- a/core/common/package.json +++ b/core/common/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/imodeljs-common", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js components common to frontend and backend", "main": "lib/imodeljs-common.js", "typings": "lib/imodeljs-common", @@ -37,20 +37,20 @@ "semver": "^5.5.0" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/geometry-core": "^2.15.0-dev.6", - "@bentley/imodelhub-client": "^2.15.0-dev.6", - "@bentley/itwin-client": "^2.15.0-dev.6", - "@bentley/rbac-client": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/geometry-core": "^2.15.0-dev.13", + "@bentley/imodelhub-client": "^2.15.0-dev.13", + "@bentley/itwin-client": "^2.15.0-dev.13", + "@bentley/rbac-client": "^2.15.0-dev.13" }, "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodelhub-client": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/rbac-client": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodelhub-client": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/rbac-client": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/flatbuffers": "~1.10.0", "@types/js-base64": "^2.3.1", diff --git a/core/common/src/BriefcaseTypes.ts b/core/common/src/BriefcaseTypes.ts index f2de28c201e..2cd5685a951 100644 --- a/core/common/src/BriefcaseTypes.ts +++ b/core/common/src/BriefcaseTypes.ts @@ -25,7 +25,7 @@ export enum DownloadBriefcaseStatus { Error, } -/** The reserved BriefcaseId values used to identify special kinds of IModelDbs. +/** Values of BriefcaseId that have special meaning. * @see [[BriefcaseId]] * @public */ @@ -42,14 +42,27 @@ export enum BriefcaseIdValue { /** All valid iModelHub issued BriefcaseIds will be equal or lower than this */ LastValid = BriefcaseIdValue.Max - 11, - /** A Standalone copy of an iModel. Standalone files may accept changesets, but can never create new changesets. - * Checkpoints are Standalone files that may not accept any new changesets after they are created. + /** + * The briefcase has not been assigned a unique Id by iModelHub. Only briefcases that have been assigned a unique BriefcaseId may create changesets, + * because BriefcaseId is used to create unique ElementIds for new elements. + * + * The `Unassigned` briefcaseId is used for several purposes: + * - **Snapshots**. Snapshot files are immutable copies of an iModel for archival or data exchange purposes. They can neither generate nor accept new changesets. + * - **Checkpoints**. Checkpoints are Snapshots that represent a specific version on an iModel's timeline. + * - **PullOnly**. A local briefcase file that may be used to "slide" along a timeline by applying incoming changesets. + * They are always opened readonly except to apply changesets. + * - **Standalone**. Standalone iModels are local files that are not connected to iModelHub, and therefore cannot accept or create changesets. + */ + Unassigned = 0, + + /** Alias for `Unassigned`. + * @deprecated use Unassigned */ Standalone = 0, /** * @internal - * @deprecated use Standalone + * @deprecated use Unassigned */ DeprecatedStandalone = 1, } @@ -62,7 +75,7 @@ export enum SyncMode { FixedVersion = 1, /** A briefcase that can be edited. A unique briefcaseId must be assigned by iModelHub. */ PullAndPush = 2, - /** use [BriefcaseIdValue.Standalone](%backend). This makes a briefcase that can accept changesets from iModelHub but can never create changesets. */ + /** use [BriefcaseIdValue.Unassigned](%backend). This makes a briefcase that can accept changesets from iModelHub but can never create changesets. */ PullOnly = 3, } @@ -114,8 +127,13 @@ export interface LocalBriefcaseProps { /** The briefcaseId. */ briefcaseId: number; - /** The current changeSetId. */ - changeSetId: GuidString; + /** The current changeSetId. + * @note ChangeSet Ids are string hash values based on the ChangeSet's content and parent. + */ + changeSetId: string; + + /** Size of the briefcase file in bytes */ + fileSize: number; } /** Properties for downloading a briefcase to a local file, from iModelHub. diff --git a/core/common/src/ColorDef.ts b/core/common/src/ColorDef.ts index 436558422e9..585bc76f88e 100644 --- a/core/common/src/ColorDef.ts +++ b/core/common/src/ColorDef.ts @@ -32,7 +32,7 @@ export type ColorDefProps = number; * 1. The ordering of the Red, Green, Blue bytes; and * 2. Whether to specify transparency or opacity (sometimes referred to as "alpha"). * - * Generally, iModel.js prefers to use `0xTTBBGGRR` (red in the low byte. 0==fully opaque in high byte), but this class provides methods + * ColorDef uses `0xTTBBGGRR` (red in the low byte. 0==fully opaque in high byte) internally, but it also provides methods * to convert to `0xRRGGBB` (see [[getRgb]]) and `0xAABBGGRR` (red in the low byte, 0==fully transparent in high byte. see [[getAbgr]]). * * The [[create]] method also accepts strings in the common HTML formats. diff --git a/core/common/src/IModel.ts b/core/common/src/IModel.ts index d7c0a140e7f..8049b695c3b 100644 --- a/core/common/src/IModel.ts +++ b/core/common/src/IModel.ts @@ -22,8 +22,10 @@ export interface IModelRpcOpenProps { readonly contextId?: GuidString; /** Guid of the iModel. */ readonly iModelId?: GuidString; - /** Id of the last ChangeSet that was applied to the iModel - must be defined for briefcases that are synchronized with iModelHub. An empty string indicates the first version */ - changeSetId?: GuidString; + /** Id of the last ChangeSet that was applied to the iModel - must be defined for briefcases that are synchronized with iModelHub. An empty string indicates the first version. + * @note ChangeSet Ids are string hash values based on the ChangeSet's content and parent. + */ + changeSetId?: string; /** Mode used to open the iModel */ openMode?: OpenMode; } @@ -58,7 +60,7 @@ export interface RootSubjectProps { description?: string; } -/** Properties that are about an iModel. +/** Properties of an iModel that are always held in memory whenever one is opened, both on the frontend and on the backend . * @public */ export interface IModelProps { @@ -76,7 +78,11 @@ export interface IModelProps { name?: string; } -/** @alpha */ +/** + * The properties returned by the backend when creating a new [[IModelConnection]] from the frontend, either with Rpc or with Ipc. + * These properties describe the iModel held on the backend for thew newly formed connection and are used to construct a new + * [[IModelConnection]] instance on the frontend to access it. + * @public */ export type IModelConnectionProps = IModelProps & IModelRpcProps; /** The properties that can be supplied when creating a *new* iModel. @@ -102,7 +108,7 @@ export interface IModelEncryptionProps { } /** - * A key used to identify an opened IModelDb between the frontend and backend for RPC communications. + * A key used to identify an opened [IModelDb]($backend) between the frontend and backend for Rpc and Ipc communications. * Keys must be unique - that is there can never be two IModelDbs opened with the same key at any given time. * If no key is supplied in a call to open an IModelDb, one is generated and returned. * It is only necessary to supply a key if you have some reason to assign a specific value to identify an IModelDb. @@ -113,7 +119,7 @@ export interface OpenDbKey { key?: string; } -/** Options that can be supplied when opening an existing SnapshotDb. +/** Options to open a [SnapshotDb]($backend). * @public */ export interface SnapshotOpenOptions extends IModelEncryptionProps, OpenDbKey { @@ -123,8 +129,9 @@ export interface SnapshotOpenOptions extends IModelEncryptionProps, OpenDbKey { autoUploadBlocks?: boolean; } -/** Options that can be supplied when opening an existing StandaloneDb. - * @beta +/** Options to open a [StandaloneDb]($backend) via [StandaloneDb.openFile]($backend) from the backend, + * or [BriefcaseConnection.openStandalone]($frontend) from the frontend. + * @public */ export type StandaloneOpenOptions = OpenDbKey; diff --git a/core/common/src/IModelVersion.ts b/core/common/src/IModelVersion.ts index e35df4d914f..2aa0e87a7df 100644 --- a/core/common/src/IModelVersion.ts +++ b/core/common/src/IModelVersion.ts @@ -17,7 +17,7 @@ import { IModelError } from "./IModelError"; export type IModelVersionProps = { first: true, latest?: never, afterChangeSetId?: never, versionName?: never } | { latest: true, first?: never, afterChangeSetId?: never, versionName?: never } | - { afterChangeSetId: GuidString, first?: never, latest?: never, versionName?: never } | + { afterChangeSetId: string, first?: never, latest?: never, versionName?: never } | { versionName: string, first?: never, latest?: never, afterChangeSetId?: never }; /** Option to specify the version of the iModel to be acquired and used @@ -26,7 +26,7 @@ export type IModelVersionProps = export class IModelVersion { private _first?: boolean; private _latest?: boolean; - private _afterChangeSetId?: GuidString; + private _afterChangeSetId?: string; private _versionName?: string; private constructor() { } @@ -52,7 +52,7 @@ export class IModelVersion { * If the changeSetId is an empty string, it is assumed to be the first version * before any change sets have been applied. */ - public static asOfChangeSet(changeSetId: GuidString): IModelVersion { + public static asOfChangeSet(changeSetId: string): IModelVersion { const version = new IModelVersion(); if (changeSetId === "") { diff --git a/core/common/src/QPoint.ts b/core/common/src/QPoint.ts index 447ad18fed7..2a0504ce583 100644 --- a/core/common/src/QPoint.ts +++ b/core/common/src/QPoint.ts @@ -68,15 +68,28 @@ export class QParams2d { return params; } + /** Return the unquantized point for the input values */ + public unquantize(x: number, y: number, out?: Point2d): Point2d { + const pt: Point2d = undefined !== out ? out : new Point2d(); + pt.x = Quantization.unquantize(x, this.origin.x, this.scale.x); + pt.y = Quantization.unquantize(y, this.origin.y, this.scale.y); + return pt; + } + /** Creates parameters supporting quantization of values within the range [-1.0, 1.0]. */ public static fromNormalizedRange(rangeScale = Quantization.rangeScale16) { return QParams2d.fromRange(Range2d.createArray([Point2d.create(-1, -1), Point2d.create(1, 1)]), undefined, rangeScale); } /** Creates parameters supporting quantization of values within the range [0.0, 1.0]. */ public static fromZeroToOne(rangeScale = Quantization.rangeScale16) { return QParams2d.fromRange(Range2d.createArray([Point2d.create(0, 0), Point2d.create(1, 1)]), undefined, rangeScale); } + /** Create parameters from origin and scale components */ + public static fromOriginAndScale(ox: number, oy: number, sx: number, sy: number) { return new QParams2d(ox, oy, sx, sy); } + // Return the range diagonal. public get rangeDiagonal(): Vector2d { return Vector2d.createFrom({ x: 0 === this.scale.x ? 0 : Quantization.rangeScale16 / this.scale.x, y: 0 === this.scale.y ? 0 : Quantization.rangeScale16 / this.scale.y }); } + /** Return true if the point point is quantizable */ + public isQuantizable(point: Point2d ) { return Quantization.isQuantizable(point.x, this.origin.x, this.scale.x) && Quantization.isQuantizable(point.y, this.origin.y, this.scale.y); } } /** Represents a quantized 2d point as an (x, y) pair in the integer range [0, 0xffff]. @@ -261,6 +274,15 @@ export class QParams3d { this.scale.x = this.scale.y = this.scale.z = 0; } } + /** Return the unquantized point for the input values */ + public unquantize(x: number, y: number, z: number, out?: Point3d): Point3d { + const pt: Point3d = undefined !== out ? out : new Point3d(); + pt.x = Quantization.unquantize(x, this.origin.x, this.scale.x); + pt.y = Quantization.unquantize(y, this.origin.y, this.scale.y); + pt.z = Quantization.unquantize(z, this.origin.z, this.scale.z); + return pt; + } + /** Creates parameters to support quantization of values within the specified range. */ public static fromRange(range: Range3d, out?: QParams3d, rangeScale = Quantization.rangeScale16) { const params = undefined !== out ? out : new QParams3d(); @@ -283,6 +305,9 @@ export class QParams3d { // Return the range diagonal. public get rangeDiagonal(): Vector3d { return Vector3d.createFrom({ x: this.scale.x === 0 ? 0 : Quantization.rangeScale16 / this.scale.x, y: this.scale.y === 0 ? 0 : Quantization.rangeScale16 / this.scale.y, z: this.scale.z === 0 ? 0 : Quantization.rangeScale16 / this.scale.z }); } + + /** Return true if the point point is quantizable */ + public isQuantizable(point: Point3d ) { return Quantization.isQuantizable(point.x, this.origin.x, this.scale.x) && Quantization.isQuantizable(point.y, this.origin.y, this.scale.y) && Quantization.isQuantizable(point.z, this.origin.z, this.scale.z); } } /** Represents a quantized 3d point as an (x, y, z) triplet in the integer range [0, 0xffff]. diff --git a/core/common/src/ThematicDisplay.ts b/core/common/src/ThematicDisplay.ts index 35f815c1eb5..a21d3fb99f2 100644 --- a/core/common/src/ThematicDisplay.ts +++ b/core/common/src/ThematicDisplay.ts @@ -10,7 +10,10 @@ import { Point3d, Range1d, Range1dProps, Vector3d, XYZProps } from "@bentley/geo import { ColorDef, ColorDefProps } from "./ColorDef"; import { Gradient } from "./Gradient"; -/** @beta */ +/** A thematic gradient mode used to generate and apply a thematic effect to a scene. + * @see [[ThematicGradientSettings.mode]] + * @public + */ export enum ThematicGradientMode { /** Apply a smooth color gradient to the scene. */ Smooth = 0, @@ -22,17 +25,30 @@ export enum ThematicGradientMode { IsoLines = 3, } -/** @beta */ +/** A color scheme used to generate the colors of a thematic gradient within an applied range. + * @see [[ThematicGradientSettings.colorScheme]] + * @see [[ThematicDisplay.range]] + * @public */ export enum ThematicGradientColorScheme { + /** A color gradient scheme that represents a blue-to-red gradation. */ BlueRed = 0, + /** A color gradient scheme that represents a red-to-blue gradation. */ RedBlue = 1, + /** A color gradient scheme that represents a monochrome (black-to-white) gradation. */ Monochrome = 2, + /** A color gradient scheme that suits a topographic gradation. */ Topographic = 3, + /** A color gradient scheme that suits a sea-mountain gradation. */ SeaMountain = 4, + /** A custom color gradient scheme configured by the user. + * @see [[ThematicGradientSettings.customKeys]] + */ Custom = 5, } -/** @beta */ +/** JSON representation of a [[ThematicGradientSettings]]. + * @public + **/ export interface ThematicGradientSettingsProps { /** The thematic image mode used to generate and apply the thematic gradient. Defaults to [[ThematicGradientMode.Smooth]]. */ mode?: ThematicGradientMode; @@ -40,7 +56,7 @@ export interface ThematicGradientSettingsProps { stepCount?: number; /** The margin color used at the extremes of the gradient, when outside the applied range. Defaults to a black color using [[ColorDef.fromJSON]] with no arguments. */ marginColor?: ColorDefProps; - /** The color scheme used to generate the colors of the gradient color used at the extremes of the gradient, when outside the applied range. Defaults to [[ThematicGradientColorScheme.BlueRed]]. */ + /** The color scheme used to generate the colors of the gradient within the applied range. Defaults to [[ThematicGradientColorScheme.BlueRed]]. */ colorScheme?: ThematicGradientColorScheme; /** The key color values that must be provided when using a custom thematic color scheme. * Defaults to empty, unless using a custom thematic color scheme. In that case, this defaults to two key colors going from white to black. @@ -53,7 +69,7 @@ export interface ThematicGradientSettingsProps { } /** Thematic settings specific to creating a color gradient used by [[ThematicDisplay]]. - * @beta + * @public */ export class ThematicGradientSettings { /** The thematic image mode used to generate the gradient. Defaults to [[ThematicGradientMode.Smooth]]. */ @@ -62,7 +78,7 @@ export class ThematicGradientSettings { public readonly stepCount: number; /** The margin color used at the extremes of the gradient, when outside the applied range. Defaults to a black color using [[ColorDef.fromJSON]] with no arguments. */ public readonly marginColor: ColorDef; - /** The color scheme used to generate the colors of the gradient color used at the extremes of the gradient, when outside the applied range. Defaults to [[ThematicGradientColorScheme.BlueRed]]. */ + /** The color scheme used to generate the colors of the gradient color within the applied range. Defaults to [[ThematicGradientColorScheme.BlueRed]]. */ public readonly colorScheme: ThematicGradientColorScheme; /** The key color values that must be provided when using a custom thematic color scheme. * Defaults to empty, unless using a custom thematic color scheme. In that case, this defaults to two key colors going from white to black. @@ -180,7 +196,7 @@ export class ThematicGradientSettings { } /** JSON representation of a [[ThematicDisplaySensor]]. - * @alpha + * @public */ export interface ThematicDisplaySensorProps { /** The world position of the sensor in X, Y, and Z. Defaults to {0,0,0}. */ @@ -190,7 +206,7 @@ export interface ThematicDisplaySensorProps { } /** A sensor in world space, used for [[ThematicDisplayMode.InverseDistanceWeightedSensors]]. - * @alpha + * @public */ export class ThematicDisplaySensor { /** The world position of the sensor in X, Y, and Z. Defaults to {0,0,0}. */ @@ -229,7 +245,7 @@ export class ThematicDisplaySensor { } /** JSON representation of a [[ThematicDisplaySensorSettings]] for [[ThematicDisplayMode.InverseDistanceWeightedSensors]]. - * @alpha + * @public */ export interface ThematicDisplaySensorSettingsProps { /** This is the list of sensors. Defaults to an empty array. */ @@ -243,7 +259,7 @@ export interface ThematicDisplaySensorSettingsProps { } /** Settings for sensor-based thematic display for [[ThematicDisplayMode.InverseDistanceWeightedSensors]]. - * @alpha + * @public */ export class ThematicDisplaySensorSettings { /** This is the list of sensors. Defaults to an empty array. */ @@ -302,14 +318,14 @@ export class ThematicDisplaySensorSettings { } /** The thematic display mode. This determines how to apply the thematic color gradient to the geometry. - * @beta + * @public */ export enum ThematicDisplayMode { /** The color gradient will be mapped to surface geometry and point clouds based on world height in meters. */ Height = 0, /** The color gradient will be mapped to surface geometry and point clouds using inverse distance weighting based on world positions and corresponding values from a list of sensors. - * @note In its alpha state, this mode has been measured to run at 60 frames per second with 64 sensors, and 30 frames per second with 150 sensors. Performance continues to decrease as more sensors are added. These measurements were recorded using a developer laptop running on a 1080p monitor. - * @alpha + * @note Performance will decrease as more sensors are added. + * @public */ InverseDistanceWeightedSensors = 1, /** The color gradient will be mapped to surface geometry based on the slope of the surface. The slope value is calculated based on the angle between the surface and the axis specified in the associated [[ThematicDisplay]] object. @@ -323,7 +339,7 @@ export enum ThematicDisplayMode { } /** JSON representation of the thematic display setup of a [[DisplayStyle3d]]. - * @beta + * @public */ export interface ThematicDisplayProps { /** The thematic display mode. This determines how to apply the thematic color gradient to the geometry. Defaults to [[ThematicDisplayMode.Height]]. */ @@ -341,13 +357,15 @@ export interface ThematicDisplayProps { /** For [[ThematicDisplayMode.HillShade]], this is the direction of the sun in world space. Defaults to {0,0,0}. */ sunDirection?: XYZProps; /** For [[ThematicDisplayMode.InverseDistanceWeightedSensors]], these are the settings that control the sensors. Defaults to an instantiation using [[ThematicDisplaySensorSettings.fromJSON]] with no arguments. - * @alpha + * @public */ sensorSettings?: ThematicDisplaySensorSettingsProps; } /** The thematic display setup of a [[DisplayStyle3d]]. - * @beta + * Thematic display allows a user to colorize a scene using a color gradient in a way that provides a visual cue about certain attributes of the rendered geometry. This scene colorization will be done based on certain geometric attributes like height, surface slope, position of surfaces relative to a sun position, or geometric position relative to a list of sensors. + * The documentation for [[ThematicDisplayMode]] describes how each mode colorizes the scene. + * @public */ export class ThematicDisplay { /** The thematic display mode. This determines how to apply the thematic color gradient to the geometry. Defaults to [[ThematicDisplayMode.Height]]. */ @@ -365,7 +383,7 @@ export class ThematicDisplay { /** For [[ThematicDisplayMode.HillShade]], this is the direction of the sun in world space. Defaults to {0,0,0}. */ public readonly sunDirection: Vector3d; /** For [[ThematicDisplayMode.InverseDistanceWeightedSensors]], these are the settings that control the sensors. Defaults to an instantiation using [[ThematicDisplaySensorSettings.fromJSON]] with no arguments. - * @alpha + * @public */ public readonly sensorSettings: ThematicDisplaySensorSettings; diff --git a/core/common/src/Tween.ts b/core/common/src/Tween.ts index 1777c25ce7c..eed799971d5 100644 --- a/core/common/src/Tween.ts +++ b/core/common/src/Tween.ts @@ -27,7 +27,7 @@ * like the global object `TWEEN` in tween.js. You must create an instance of this class, and then create [[Tween]]s by * calling [[Tweens.create]] or by calling `new Tween()` and pass your Group as its first argument. * @see The [tween.js users guide](https://github.com/tweenjs/tween.js/blob/master/docs/user_guide.md) - * @beta + * @public */ export class Tweens { private _tweens: any = {}; @@ -113,18 +113,18 @@ export class Tweens { } } -/** @beta */ +/** @public */ export type TweenCallback = (obj: any) => void; -/** @beta */ +/** @public */ export type UpdateCallback = (obj: any, t: number) => void; -/** @beta */ +/** @public */ export type EasingFunction = (k: number) => number; -/** @beta */ +/** @public */ export type InterpolationFunction = (v: any, k: number) => number; /** A Tween for interpolating values of an object. Instances of this class are owned by a `Tweens` group. * @see The [tween.js users guide](https://github.com/tweenjs/tween.js/blob/master/docs/user_guide.md) - * @beta + * @public */ export class Tween { private _isPaused = false; @@ -426,7 +426,7 @@ export class Tween { } /** Easing functions from tween.js - * @beta + * @public */ export const Easing = { Linear: { @@ -642,7 +642,7 @@ export const Easing = { }; /** Interpolation functions from tween.js - * @beta + * @public */ export const Interpolation = { diff --git a/core/common/src/ipc/IpcSocket.ts b/core/common/src/ipc/IpcSocket.ts index be95a313b5b..1de119de38b 100644 --- a/core/common/src/ipc/IpcSocket.ts +++ b/core/common/src/ipc/IpcSocket.ts @@ -20,7 +20,7 @@ export type IpcListener = (evt: Event, ...args: any[]) => void; /** * Function returned when establishing an Ipc `receive` listener or `invoke` handler. Call this method to remove the listener/handler. - * @beta + * @public */ export type RemoveFunction = () => void; diff --git a/core/common/src/rpc/core/RpcInvocation.ts b/core/common/src/rpc/core/RpcInvocation.ts index e39550ca1ca..25b0e514e4d 100644 --- a/core/common/src/rpc/core/RpcInvocation.ts +++ b/core/common/src/rpc/core/RpcInvocation.ts @@ -227,6 +227,13 @@ export class RpcInvocation { interfaceName: (typeof (this.operation) === "undefined") ? "" : this.operation.interfaceDefinition.interfaceName, }; + try { + const impl = RpcRegistry.instance.getImplForInterface(this.operation.interfaceDefinition) as any; + if (impl[CURRENT_INVOCATION] === this) { + impl[CURRENT_INVOCATION] = undefined; + } + } catch (_err) { } + return fulfillment; } diff --git a/core/ecschema-locaters/package.json b/core/ecschema-locaters/package.json index 99c319ab453..1de85bcf1af 100644 --- a/core/ecschema-locaters/package.json +++ b/core/ecschema-locaters/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/ecschema-locaters", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "EC Schema file locaters", "license": "MIT", "main": "lib/ecschema-locaters.js", @@ -31,16 +31,16 @@ "url": "http://www.bentley.com" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/ecschema-metadata": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/ecschema-metadata": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", "@types/glob": "^5.0.35", "@types/mocha": "^5.2.5", "@types/node": "10.14.1", "@types/sinon": "^9.0.0", - "@types/xmldom": "^0.1.29", + "@types/xmldom": "^0.1.30", "chai": "^4.1.2", "chai-as-promised": "^7", "eslint": "^6.8.0", @@ -48,13 +48,13 @@ "nyc": "^14.0.0", "rimraf": "^3.0.2", "typescript": "~4.1.0", - "xmldom": "^0.1.27" + "xmldom": "^0.5.0" }, "dependencies": { "glob": "^7.1.2" }, "peerDependencies": { - "@bentley/ecschema-metadata": "^2.15.0-dev.6" + "@bentley/ecschema-metadata": "^2.15.0-dev.13" }, "nyc": { "nycrc-path": "./node_modules/@bentley/build-tools/.nycrc", diff --git a/core/ecschema-metadata/package.json b/core/ecschema-metadata/package.json index 42a2114ac46..85eb132e470 100644 --- a/core/ecschema-metadata/package.json +++ b/core/ecschema-metadata/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/ecschema-metadata", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "ECObjects core concepts in typescript", "license": "MIT", "main": "lib/ecschema-metadata.js", @@ -12,8 +12,9 @@ }, "scripts": { "compile": "npm run build", - "build": "tsc 1>&2 && npm run createLocalization", + "build": "tsc 1>&2 && npm run createLocalization && npm run copy:test-assets", "clean": "rimraf lib .rush/temp/package-deps*.json", + "copy:test-assets": "cpx \"./src/test/assets/**/*\" ./lib/test/assets", "extract-api": "betools extract-api --entry=ecschema-metadata", "lint": "eslint -f visualstudio \"./src/**/*.ts\" 1>&2", "test": "betools test", @@ -33,10 +34,10 @@ "url": "http://www.bentley.com" }, "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", "@bentley/units-schema": "^1.0.5", "@types/almost-equal": "1.1.0", "@types/benchmark": "^2.1.0", @@ -46,10 +47,11 @@ "@types/mocha": "^5.2.5", "@types/node": "10.14.1", "@types/sinon": "^9.0.0", - "@types/xmldom": "^0.1.29", + "@types/xmldom": "^0.1.30", "benchmark": "^2.1.4", "chai": "^4.1.2", "chai-as-promised": "^7", + "cpx": "^1.5.0", "eslint": "^6.8.0", "i18next-node-fs-backend": "^2.1.3", "mocha": "^5.2.0", @@ -57,12 +59,12 @@ "rimraf": "^3.0.2", "sinon": "^9.0.2", "typescript": "~4.1.0", - "xmldom": "^0.1.27", + "xmldom": "^0.5.0", "xmlhttprequest": "^1.8.0" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/imodeljs-i18n": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/imodeljs-i18n": "^2.15.0-dev.13" }, "dependencies": { "almost-equal": "^1.1.0" diff --git a/core/ecschema-metadata/src/UnitConversion/UnitConverter.ts b/core/ecschema-metadata/src/UnitConversion/UnitConverter.ts index 53166749279..580287ead84 100644 --- a/core/ecschema-metadata/src/UnitConversion/UnitConverter.ts +++ b/core/ecschema-metadata/src/UnitConversion/UnitConverter.ts @@ -26,10 +26,11 @@ export class UnitConverter { * Find conversion between from and to units, formatted {schemaName}.{schemaItemName} or {schemaName}:{schemaItemName} * @param fromUnit SchemaItem full name of source unit * @param toUnit SchemaItem full name of target unit - * @returns [[UnitConversion]] converting fromFullName -> toFullName with a factor and an offset + * @returns [[UnitConversion]] converting fromUnit -> toUnit with a factor and an offset * @throws Error if from and to Units' SchemaItem is not found in Schema or Schema prefix is not found in SchemaContext * @throws Error if from and to Units do not belong to the same phenomenon * @throws Error if definitions' SchemaItems cannot be found in its own or referenced Schemas + * @throws Error if base units of source and target unit do not match */ public async calculateConversion(fromUnit: string, toUnit: string): Promise { const [fromSchemaName, fromSchemaItemName] = SchemaItem.parseFullName(fromUnit); @@ -70,10 +71,17 @@ export class UnitConverter { await this._uGraph.addUnit(from); await this._uGraph.addUnit(to); + const fromBaseUnits = new Map(); + const toBaseUnits = new Map(); // Calculate map of UnitConversions to get between from -> base - const fromMapStore = this._uGraph.reduce(from, new Set()); + const fromMapStore = this._uGraph.reduce(from, fromBaseUnits); // Calculate map of UnitConversions to get between base -> to - const toMapStore = this._uGraph.reduce(to, new Set()); + const toMapStore = this._uGraph.reduce(to, toBaseUnits); + + if (!this.checkBaseUnitsMatch(fromBaseUnits, toBaseUnits)) + throw new BentleyError(BentleyStatus.ERROR, `Source and target units do not have matching base units`, () => { + return { from, to }; + }); // Final calculations to get singular UnitConversion between from -> to const fromMap = fromMapStore.get(from.key.fullName) || UnitConversion.identity; @@ -81,4 +89,39 @@ export class UnitConverter { const fromInverse = fromMap.inverse(); return fromInverse.compose(toMap); } + + /** + * Check if fromBaseUnits's base units and exponents matches toBaseUnits's + * @param fromBaseUnits Map of base units for source unit + * @param toBaseUnits Map of base units for target unit + * @internal + */ + private checkBaseUnitsMatch(fromBaseUnits: Map, toBaseUnits: Map): boolean { + // Trim maps of "One" and value that equal zero as they do not affect the base units and calculations + for (const [key, value] of fromBaseUnits.entries()) { + const [, schemaItemName] = SchemaItem.parseFullName(key); + if (schemaItemName === "ONE" || value === 0) { + fromBaseUnits.delete(key); + } + } + + for (const [key, value] of toBaseUnits.entries()) { + const [, schemaItemName] = SchemaItem.parseFullName(key); + if (schemaItemName === "ONE" || value === 0) { + toBaseUnits.delete(key); + } + } + + if (fromBaseUnits.size !== toBaseUnits.size) + return false; + + for (const key of fromBaseUnits.keys()) { + if (!toBaseUnits.has(key) || fromBaseUnits.get(key) !== toBaseUnits.get(key)) { + // Mismatching key or value + return false; + } + } + + return true; + } } diff --git a/core/ecschema-metadata/src/UnitConversion/UnitTree.ts b/core/ecschema-metadata/src/UnitConversion/UnitTree.ts index 1cc3a4a9fad..baffaed1967 100644 --- a/core/ecschema-metadata/src/UnitConversion/UnitTree.ts +++ b/core/ecschema-metadata/src/UnitConversion/UnitTree.ts @@ -19,14 +19,25 @@ export class GraphUtils { * @param op Reducing function * @param initial Initial label */ - public static dfsReduce(_graph: Graph, key: string, op: (previous: T, current: string) => T, visitChildren: (key: string) => boolean, initial: T): T { + public static dfsReduce(_graph: Graph, key: string, op: (previous: T, current: string) => T, initial: T, baseUnitsMap: Map, accumulatedExponent: number): T { const outEdges = _graph.outEdges(key); let t = initial; - if (outEdges && visitChildren(key)) { + if (outEdges.length > 0) { t = outEdges.reduce( - (p, edge) => GraphUtils.dfsReduce(_graph, edge.w, op, visitChildren, p), + (p, edge) => { + const {v, w} = edge; + const edgeExponent = _graph.edge(v, w).exponent; + return GraphUtils.dfsReduce(_graph, edge.w, op, p, baseUnitsMap, accumulatedExponent * edgeExponent); + }, t ); + } else { + if (baseUnitsMap.has(key)) { + const oldExponent = baseUnitsMap.get(key)!; + baseUnitsMap.set(key, oldExponent + accumulatedExponent); + } else { + baseUnitsMap.set(key, accumulatedExponent); + } } return op(t, key); @@ -107,7 +118,8 @@ export class UnitGraph { if (this._graph.hasNode(unit.key.fullName)) return; this._graph.setNode(unit.key.fullName, unit); - if (this.isIdentity(unit)) return; + if (this.isIdentity(unit)) + return; const umap = parseDefinition(unit.definition); @@ -140,19 +152,14 @@ export class UnitGraph { * @param unit Unit to be processed * @param stopNodes The tree exploration should stop here */ - public reduce(unit: Unit | Constant, stopNodes: Set): Map { + public reduce(unit: Unit | Constant, baseUnitsMap: Map): Map { const unitFullName = unit.key.fullName; const innerMapStore = new Map(); - const outerMapStore = GraphUtils.dfsReduce(this._graph, unitFullName, (p, c) => this.reducingFunction(stopNodes, p, c), (c) => !stopNodes.has(c), innerMapStore); + const outerMapStore = GraphUtils.dfsReduce(this._graph, unitFullName, (p, c) => this.reducingFunction(p, c), innerMapStore, baseUnitsMap, 1); return outerMapStore; } - private reducingFunction(stopNodes: Set, innermapStore: Map, unitFullName: string) { - if (stopNodes.has(unitFullName)) { - innermapStore.set(unitFullName, UnitConversion.identity); - return innermapStore; - } - + private reducingFunction(innermapStore: Map, unitFullName: string) { const outEdges = this._graph.outEdges(unitFullName); if (outEdges) { const cmap = outEdges.reduce((pm, e) => { diff --git a/core/ecschema-metadata/src/test/UnitConversion/Convert.test.ts b/core/ecschema-metadata/src/test/UnitConversion/Convert.test.ts new file mode 100644 index 00000000000..dbee4ec73b6 --- /dev/null +++ b/core/ecschema-metadata/src/test/UnitConversion/Convert.test.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Bentley Systems, Incorporated. All rights reserved. +* See LICENSE.md in the project root for license terms and full copyright notice. +*--------------------------------------------------------------------------------------------*/ +import * as almostEqual from "almost-equal"; +import * as fs from "fs"; +import * as path from "path"; +import { expect } from "chai"; +import { SchemaContext } from "../../Context"; +import { deserializeXmlSync } from "../TestUtils/DeserializationHelpers"; +import { UnitConverter } from "../../UnitConversion/UnitConverter"; + +interface TestData { + from: string; + input: number; + to: string; + expect: number; +} + +describe("Unit Conversion tests", () => { + const context = new SchemaContext(); + + const testData: TestData[] = JSON.parse( + fs.readFileSync(path.join(__dirname, "..", "assets", "./UnitTests.json"), "utf-8") + ); + + before(() => { + const schemaFile = path.join(__dirname, "..", "..", "..", "node_modules", "@bentley", "units-schema", "Units.ecschema.xml"); + const schemaXml = fs.readFileSync(schemaFile, "utf-8"); + deserializeXmlSync(schemaXml, context); + }); + + testData.forEach((test: TestData) => { + it(`should convert ${test.from} to ${test.to}`, async () => { + const converter = new UnitConverter(context); + const fromFullName = `Units:${test.from}`; + const toFullName = `Units:${test.to}`; + const map = await converter.calculateConversion(fromFullName, toFullName); + const actual = map.evaluate(test.input); + expect( + almostEqual(test.expect, actual, almostEqual.FLT_EPSILON, almostEqual.FLT_EPSILON), + `${test.input} ${test.from} in ${test.to} should be ${test.expect} + and not ${actual} error = ${Math.abs(test.expect - actual)} > ${almostEqual.FLT_EPSILON}` + ).to.be.true; + }); + }); +}); diff --git a/core/ecschema-metadata/src/test/UnitConversion/CrossSchema.test.ts b/core/ecschema-metadata/src/test/UnitConversion/CrossSchema.test.ts new file mode 100644 index 00000000000..be3665b84d6 --- /dev/null +++ b/core/ecschema-metadata/src/test/UnitConversion/CrossSchema.test.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Bentley Systems, Incorporated. All rights reserved. +* See LICENSE.md in the project root for license terms and full copyright notice. +*--------------------------------------------------------------------------------------------*/ +import * as almostEqual from "almost-equal"; +import * as fs from "fs"; +import * as path from "path"; +import { expect } from "chai"; +import { SchemaContext } from "../../Context"; +import { deserializeXmlSync } from "../TestUtils/DeserializationHelpers"; +import { UnitConverter } from "../../UnitConversion/UnitConverter"; + +interface TestData { + fromSchema: string; + from: string; + toSchema: string; + to: string; + input: number; + expect: number; +} + +describe("Cross Schema unit definitions tests", () => { + const context = new SchemaContext(); + + const testData: TestData[] = JSON.parse( + fs.readFileSync(path.join(__dirname, "..", "assets", "./CrossSchemaTests.json"), "utf-8") + ); + + before(() => { + const siSchemaFile = path.join(__dirname, "..", "assets", "SIUnits.ecschema.xml"); + const siSchemaXml = fs.readFileSync(siSchemaFile, "utf-8"); + deserializeXmlSync(siSchemaXml, context); + + const metricSchemaFile = path.join(__dirname, "..", "assets", "MetricUnits.ecschema.xml"); + const metricSchemaXml = fs.readFileSync(metricSchemaFile, "utf-8"); + deserializeXmlSync(metricSchemaXml, context); + + const usSchemaFile = path.join(__dirname, "..", "assets", "USUnits.ecschema.xml"); + const usSchemaXml = fs.readFileSync(usSchemaFile, "utf-8"); + deserializeXmlSync(usSchemaXml, context); + }); + + testData.forEach((test: TestData) => { + it(`should convert ${test.fromSchema}:${test.from} to ${test.toSchema}:${test.to}`, async () => { + const converter = new UnitConverter(context); + const fromFullName = `${test.fromSchema}.${test.from}`; + const toFullName = `${test.toSchema}.${test.to}`; + const map = await converter.calculateConversion(fromFullName, toFullName); + const actual = map.evaluate(test.input); + expect( + almostEqual(test.expect, actual, almostEqual.FLT_EPSILON, almostEqual.FLT_EPSILON), + `${test.input} ${test.from} in ${test.to} should be ${test.expect} + and not ${actual} error = ${Math.abs(test.expect - actual)} > ${almostEqual.FLT_EPSILON}` + ).to.be.true; + }); + }); +}); diff --git a/core/ecschema-metadata/src/test/UnitConversion/Parser.test.ts b/core/ecschema-metadata/src/test/UnitConversion/Parser.test.ts new file mode 100644 index 00000000000..c443e6e2b57 --- /dev/null +++ b/core/ecschema-metadata/src/test/UnitConversion/Parser.test.ts @@ -0,0 +1,160 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Bentley Systems, Incorporated. All rights reserved. +* See LICENSE.md in the project root for license terms and full copyright notice. +*--------------------------------------------------------------------------------------------*/ +import * as fs from "fs"; +import * as path from "path"; +import { expect } from "chai"; +import { DefinitionFragment, parseDefinition } from "../../UnitConversion/Parser"; + +describe("DefinitionParser tests", () => { + const definitionsToTest: string[] = [ + "NUMBER", + "NUMBER(2)", + "NUMBER(-1)", + "[NUMBER]", + "FORCE*LENGTH", + "WORK*TIME(-1)", + "LENGTH*LENGTH(-1)*TEMPERATURE_CHANGE(-1)", + "[PI]*RAD", + "BTU*IN*FT(-2)*HR(-1)*DELTA_FAHRENHEIT(-1)", + "M(3)*M(-3)", + "[PI](2)*[PI](-2)*[PI](2)*[PI](-2)*[PI](2)*[PI](-2)", + "BTU(-1)*[PI](2)*HR*[ONE]*TEMPERATURE_CHANGE(1)", + "alias:NUMBER", + "alias:NUMBER(2)", + "alias:NUMBER(-1)", + "[alias:NUMBER]", + "alias:FORCE*alias:LENGTH", + "alias:WORK*alias:TIME(-1)", + "alias:LENGTH*alias:LENGTH(-1)*alias:TEMPERATURE_CHANGE(-1)", + "[alias:PI]*alias:RAD", + "alias:BTU*alias:IN*alias:FT(-2)*alias:HR(-1)*alias:DELTA_FAHRENHEIT(-1)", + "alias:M(3)*alias:M(-3)", + "[alias:PI](2)*[alias:PI](-2)*[alias:PI](2)*[alias:PI](-2)*[alias:PI](2)*[alias:PI](-2)", + "alias:BTU(-1)*[alias:PI](2)*alias:HR*[alias:ONE]*alias:TEMPERATURE_CHANGE(1)", + ]; + + type KeyValuePair = [string, DefinitionFragment]; + + const expectedData: { [key: string]: KeyValuePair[] } = JSON.parse( + fs.readFileSync(path.join(__dirname, "..", "assets", "./ParserTests.json"), "utf-8") + ); + + describe("parsing individual tokens", () => { + it("all capture groups provided", () => { + const definition = "[TEST](-3)"; + const data = [ + [ + "TEST", + { + name: "TEST", + exponent: -3, + constant: true, + }, + ], + ]; + + expect([...parseDefinition(definition)]).to.deep.equal(data); + }); + + it("with namespace, all capture groups provided", () => { + const definition = "[alias:TEST](-3)"; + const data = [ + [ + "alias:TEST", + { + name: "alias:TEST", + exponent: -3, + constant: true, + }, + ], + ]; + + expect([...parseDefinition(definition)]).to.deep.equal(data); + }); + + it("no brackets, exponent provided", () => { + const definition = "TEST(-3)"; + const data = [ + [ + "TEST", + { + name: "TEST", + exponent: -3, + constant: false, + }, + ], + ]; + + expect([...parseDefinition(definition)]).to.deep.equal(data); + }); + + it("singular constant/unit/phenomenon provided", () => { + // Unit and Phenomenon test, units and phenomena are not wrapped with brackets + let definition = "TEST"; + let data = [ + [ + "TEST", + { + name: "TEST", + exponent: 1, + constant: false, + }, + ], + ]; + + expect([...parseDefinition(definition)]).to.deep.equal(data); + + // Constant test, constants are wrapped with brackets + definition = "[TEST]"; + data = [ + [ + "TEST", + { + name: "TEST", + exponent: 1, + constant: true, + }, + ], + ]; + + expect([...parseDefinition(definition)]).to.deep.equal(data); + }); + }); + + function testTokenizations(definition: string) { + it(`tokenization of ${definition} matches expected data`, async () => { + expect([...parseDefinition(definition)]).to.have.deep.members( + expectedData[definition] + ); + }); + } + + function testInvalidToken(definition: string) { + it(`invalid definition ${definition} throws`, async () => { + expect(() => [...parseDefinition(definition)]).to.throw(); + }); + } + + describe("parsing correctly-formed definitions", () => { + for (const definition of definitionsToTest) { + testTokenizations(definition); + } + }); + + describe("parsing malformed definitions", () => { + testInvalidToken(""); + testInvalidToken("TEST\t"); + testInvalidToken("TEST**TEST"); + testInvalidToken("[](-1)"); + testInvalidToken("TEST()"); + testInvalidToken("TEST(--1)"); + testInvalidToken("[TEST(-1)"); + testInvalidToken("[TEST](1"); + testInvalidToken("TEST*[TEST](1"); + testInvalidToken("TEST*[TEST](1)*[TEST]-1"); + testInvalidToken("[Test](1)*"); + testInvalidToken("TEST(1)[TEST]"); + }); +}); diff --git a/core/ecschema-metadata/src/test/UnitConversion/Validation.test.ts b/core/ecschema-metadata/src/test/UnitConversion/Validation.test.ts new file mode 100644 index 00000000000..f8dad141465 --- /dev/null +++ b/core/ecschema-metadata/src/test/UnitConversion/Validation.test.ts @@ -0,0 +1,149 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Bentley Systems, Incorporated. All rights reserved. +* See LICENSE.md in the project root for license terms and full copyright notice. +*--------------------------------------------------------------------------------------------*/ +import { SchemaContext } from "../../Context"; +import { expect } from "chai"; +import * as fs from "fs"; +import * as path from "path"; +import { deserializeXmlSync } from "../TestUtils/DeserializationHelpers"; +import { UnitConverter } from "../../UnitConversion/UnitConverter"; + +describe("Testing when unit conversion should throw", () => { + const context = new SchemaContext(); + + before(() => { + const siSchemaFile = path.join(__dirname, "..", "assets", "SIUnits.ecschema.xml"); + const siSchemaXml = fs.readFileSync(siSchemaFile, "utf-8"); + deserializeXmlSync(siSchemaXml, context); + + const metricSchemaFile = path.join(__dirname, "..", "assets", "MetricUnits.ecschema.xml"); + const metricSchemaXml = fs.readFileSync(metricSchemaFile, "utf-8"); + deserializeXmlSync(metricSchemaXml, context); + + const usSchemaFile = path.join(__dirname, "..", "assets", "USUnits.ecschema.xml"); + const usSchemaXml = fs.readFileSync(usSchemaFile, "utf-8"); + deserializeXmlSync(usSchemaXml, context); + + const auSchemaFile = path.join(__dirname, "..", "assets", "ValidationUnits.ecschema.xml"); + const auSchemaXml = fs.readFileSync(auSchemaFile, "utf-8"); + deserializeXmlSync(auSchemaXml, context); + }); + + it("should throw when schema name is not in context", async () => { + const converter = new UnitConverter(context); + try { + await converter.calculateConversion("MockSchema:CM", "SIUnits:M"); + } catch (err) { + expect(err).to.be.an("error"); + expect(err.message).to.equal("Cannot find from's and/or to's schema"); + } + try { + await converter.calculateConversion("SIUnits:M", "MockSchema:CM"); + } catch (err) { + expect(err).to.be.an("error"); + expect(err.message).to.equal("Cannot find from's and/or to's schema"); + } + }); + + it("should throw when schema item is not in schema ", async () => { + const converter = new UnitConverter(context); + try { + await converter.calculateConversion("SIUnits:MockUnit", "MetricUnits:CM"); + } catch (err) { + expect(err).to.be.an("error"); + expect(err.message).to.equal("Cannot find schema item"); + } + try { + await converter.calculateConversion("MetricUnits:CM", "SIUnits:MockUnit"); + } catch (err) { + expect(err).to.be.an("error"); + expect(err.message).to.equal("Cannot find schema item"); + } + }); + + it("should throw when source and target units are not the same phenomenon", async () => { + const converter = new UnitConverter(context); + try { + await converter.calculateConversion("USUnits:SQ_FT", "SIUnits:M"); + } catch (err) { + expect(err).to.be.an("error"); + expect(err.message).to.equal("Source and target units do not belong to same phenomenon"); + } + try { + await converter.calculateConversion("SIUnits:M", "USUnits:SQ_FT"); + } catch (err) { + expect(err).to.be.an("error"); + expect(err.message).to.equal("Source and target units do not belong to same phenomenon"); + } + }); + + it("should throw when unit in definition is not found in schema (deci is not in ValidationUnits schema)", async () => { + const converter = new UnitConverter(context); + try { + await converter.calculateConversion("ValidationUnits:DM", "ValidationUnits:KM"); + await converter.calculateConversion("ValidationUnits:KM", "ValidationUnits:DM"); + } catch (err) { + expect(err).to.be.an("error"); + expect(err.message).to.equal("Cannot find schema item"); + } + }); + + it("should throw when source and target units do not have the same base units", async () => { + const converter = new UnitConverter(context); + try { + await converter.calculateConversion("ValidationUnits:FT", "ValidationUnits:KM"); + } catch (err) { + expect(err).to.be.an("error"); + expect(err.message).to.equal("Source and target units do not have matching base units"); + } + try { + await converter.calculateConversion("ValidationUnits:KM", "ValidationUnits:FT"); + } catch (err) { + expect(err).to.be.an("error"); + expect(err.message).to.equal("Source and target units do not have matching base units"); + } + + try { + await converter.calculateConversion("ValidationUnits:YRD", "ValidationUnits:M"); + } catch (err) { + expect(err).to.be.an("error"); + expect(err.message).to.equal("Source and target units do not have matching base units"); + } + try { + await converter.calculateConversion("ValidationUnits:M", "ValidationUnits:YRD"); + } catch (err) { + expect(err).to.be.an("error"); + expect(err.message).to.equal("Source and target units do not have matching base units"); + } + }); + + it("should throw when source and target units do not have the same base units", async () => { + const converter = new UnitConverter(context); + try { + await converter.calculateConversion("ValidationUnits:MM_PER_SEC", "ValidationUnits:FT_PER_DAY"); + } catch (err) { + expect(err).to.be.an("error"); + expect(err.message).to.equal("Source and target units do not have matching base units"); + } + try { + await converter.calculateConversion("ValidationUnits:FT_PER_DAY", "ValidationUnits:MM_PER_SEC"); + } catch (err) { + expect(err).to.be.an("error"); + expect(err.message).to.equal("Source and target units do not have matching base units"); + } + + try { + await converter.calculateConversion("ValidationUnits:MM_PER_HR", "ValidationUnits:FT_PER_SEC"); + } catch (err) { + expect(err).to.be.an("error"); + expect(err.message).to.equal("Source and target units do not have matching base units"); + } + try { + await converter.calculateConversion("ValidationUnits:FT_PER_SEC", "ValidationUnits:MM_PER_HR"); + } catch (err) { + expect(err).to.be.an("error"); + expect(err.message).to.equal("Source and target units do not have matching base units"); + } + }); +}); diff --git a/core/ecschema-metadata/src/test/assets/CrossSchemaTests.json b/core/ecschema-metadata/src/test/assets/CrossSchemaTests.json new file mode 100644 index 00000000000..91dc119a318 --- /dev/null +++ b/core/ecschema-metadata/src/test/assets/CrossSchemaTests.json @@ -0,0 +1,322 @@ +[ + { + "fromSchema": "USUnits", + "from": "YRD", + "toSchema": "USUnits", + "to": "FT", + "input": 42.42, + "expect": 127.26 + }, + { + "fromSchema": "USUnits", + "from": "FT", + "toSchema": "USUnits", + "to": "YRD", + "input": 42.42, + "expect": 14.14 + }, + { + "fromSchema": "MetricUnits", + "from": "KM", + "toSchema": "MetricUnits", + "to": "CM", + "input": 42.42, + "expect": 4242000 + }, + { + "fromSchema": "MetricUnits", + "from": "CM", + "toSchema": "MetricUnits", + "to": "KM", + "input": 42.42, + "expect": 0.0004242 + }, + { + "fromSchema": "USUnits", + "from": "MILE", + "toSchema": "MetricUnits", + "to": "DM", + "input": 42.42, + "expect": 682683.7248000001 + }, + { + "fromSchema": "MetricUnits", + "from": "DM", + "toSchema": "USUnits", + "to": "MILE", + "input": 42.42, + "expect": 0.0026358565974707706 + }, + { + "fromSchema": "USUnits", + "from": "US_SURVEY_MILE", + "toSchema": "SIUnits", + "to": "M", + "input": 42.42, + "expect": 68268.50901701803 + }, + { + "fromSchema": "SIUnits", + "from": "M", + "toSchema": "USUnits", + "to": "US_SURVEY_MILE", + "input": 42.42, + "expect": 0.026358513257575752 + }, + { + "fromSchema": "USUnits", + "from": "SQ_MILE", + "toSchema": "USUnits", + "to": "SQ_FT", + "input": 42.42, + "expect": 1182601728.0000005 + }, + { + "fromSchema": "USUnits", + "from": "SQ_FT", + "toSchema": "USUnits", + "to": "SQ_MILE", + "input": 42.42, + "expect": 0.0000015216081267217627 + }, + { + "fromSchema": "MetricUnits", + "from": "SQ_UM", + "toSchema": "MetricUnits", + "to": "SQ_MM", + "input": 42.42, + "expect": 4.242e-5 + }, + { + "fromSchema": "MetricUnits", + "from": "SQ_MM", + "toSchema": "MetricUnits", + "to": "SQ_UM", + "input": 42.42, + "expect": 42420000 + }, + { + "fromSchema": "USUnits", + "from": "SQ_US_SURVEY_YRD", + "toSchema": "SIUnits", + "to": "SQ_M", + "input": 42.42, + "expect": 35.46866448571609 + }, + { + "fromSchema": "SIUnits", + "from": "SQ_M", + "toSchema": "USUnits", + "to": "SQ_US_SURVEY_YRD", + "input": 42.42, + "expect": 50.733694828703676 + }, + { + "fromSchema": "USUnits", + "from": "SQ_MILE", + "toSchema": "MetricUnits", + "to": "SQ_KM", + "input": 42.42, + "expect": 109.86729564045314 + }, + { + "fromSchema": "MetricUnits", + "from": "SQ_KM", + "toSchema": "USUnits", + "to": "SQ_MILE", + "input": 42.42, + "expect": 16.378453565370553 + }, + { + "fromSchema": "USUnits", + "from": "FT_PER_THOUSAND_FOOT", + "toSchema": "USUnits", + "to": "FT_PER_FT", + "input": 42.42, + "expect": 0.04242 + }, + { + "fromSchema": "USUnits", + "from": "FT_PER_FT", + "toSchema": "USUnits", + "to": "FT_PER_THOUSAND_FOOT", + "input": 42.42, + "expect": 42420 + }, + { + "fromSchema": "USUnits", + "from": "FT_PER_FT", + "toSchema": "USUnits", + "to": "IN_PER_FT", + "input": 42.42, + "expect": 509.04 + }, + { + "fromSchema": "USUnits", + "from": "IN_PER_FT", + "toSchema": "USUnits", + "to": "FT_PER_FT", + "input": 42.42, + "expect": 3.535 + }, + { + "fromSchema": "USUnits", + "from": "FT_PER_MILE", + "toSchema": "USUnits", + "to": "IN_PER_FT", + "input": 42.42, + "expect": 0.09640909090909093 + }, + { + "fromSchema": "USUnits", + "from": "IN_PER_FT", + "toSchema": "USUnits", + "to": "FT_PER_MILE", + "input": 42.42, + "expect": 18664.8 + }, + { + "fromSchema": "MetricUnits", + "from": "CM_PER_M", + "toSchema": "MetricUnits", + "to": "M_PER_KM", + "input": 42.42, + "expect": 424.2 + }, + { + "fromSchema": "MetricUnits", + "from": "M_PER_KM", + "toSchema": "MetricUnits", + "to": "CM_PER_M", + "input": 42.42, + "expect": 4.242 + }, + { + "fromSchema": "USUnits", + "from": "FT_PER_IN", + "toSchema": "MetricUnits", + "to": "M_PER_KM", + "input": 42.42, + "expect": 509040 + }, + { + "fromSchema": "MetricUnits", + "from": "M_PER_KM", + "toSchema": "USUnits", + "to": "FT_PER_IN", + "input": 42.42, + "expect": 0.003535 + }, + { + "fromSchema": "USUnits", + "from": "FT_PER_IN", + "toSchema": "SIUnits", + "to": "M_PER_M", + "input": 42.42, + "expect": 509.04 + }, + { + "fromSchema": "SIUnits", + "from": "M_PER_M", + "toSchema": "USUnits", + "to": "FT_PER_IN", + "input": 42.42, + "expect": 3.535 + }, + { + "fromSchema": "MetricUnits", + "from": "MM_PER_M", + "toSchema": "SIUnits", + "to": "M_PER_M", + "input": 42.42, + "expect": 0.04242 + }, + { + "fromSchema": "SIUnits", + "from": "M_PER_M", + "toSchema": "MetricUnits", + "to": "MM_PER_M", + "input": 42.42, + "expect": 42420 + }, + { + "fromSchema": "USUnits", + "from": "FAHRENHEIT", + "toSchema": "USUnits", + "to": "RANKINE", + "input": 42.42, + "expect": 502.09 + }, + { + "fromSchema": "USUnits", + "from": "RANKINE", + "toSchema": "USUnits", + "to": "FAHRENHEIT", + "input": 42.42, + "expect": -417.24999999999994 + }, + { + "fromSchema": "USUnits", + "from": "FAHRENHEIT", + "toSchema": "MetricUnits", + "to": "CELSIUS", + "input": 42.42, + "expect": 5.7888888888888985 + }, + { + "fromSchema": "MetricUnits", + "from": "CELSIUS", + "toSchema": "USUnits", + "to": "FAHRENHEIT", + "input": 42.42, + "expect": 108.35600000000001 + }, + { + "fromSchema": "USUnits", + "from": "FAHRENHEIT", + "toSchema": "SIUnits", + "to": "K", + "input": 42.42, + "expect": 278.93888888888887 + }, + { + "fromSchema": "SIUnits", + "from": "K", + "toSchema": "USUnits", + "to": "FAHRENHEIT", + "input": 42.42, + "expect": -383.31399999999996 + }, + { + "fromSchema": "SIUnits", + "from": "TWO_PI", + "toSchema": "SIUnits", + "to": "QUARTER_PI", + "input": 42.42, + "expect": 339.36 + }, + { + "fromSchema": "SIUnits", + "from": "QUARTER_PI", + "toSchema": "SIUnits", + "to": "TWO_PI", + "input": 42.42, + "expect": 5.3025 + }, + { + "fromSchema": "SIUnits", + "from": "HALF_PI", + "toSchema": "SIUnits", + "to": "PI", + "input": 42.42, + "expect": 21.21 + }, + { + "fromSchema": "SIUnits", + "from": "PI", + "toSchema": "SIUnits", + "to": "HALF_PI", + "input": 42.42, + "expect": 84.84 + } +] \ No newline at end of file diff --git a/core/ecschema-metadata/test/UnitConversion/assets/MetricUnits.ecschema.xml b/core/ecschema-metadata/src/test/assets/MetricUnits.ecschema.xml similarity index 100% rename from core/ecschema-metadata/test/UnitConversion/assets/MetricUnits.ecschema.xml rename to core/ecschema-metadata/src/test/assets/MetricUnits.ecschema.xml diff --git a/core/ecschema-metadata/src/test/assets/ParserTests.json b/core/ecschema-metadata/src/test/assets/ParserTests.json new file mode 100644 index 00000000000..4787501b1f7 --- /dev/null +++ b/core/ecschema-metadata/src/test/assets/ParserTests.json @@ -0,0 +1,434 @@ +{ + "NUMBER": [ + [ + "NUMBER", + { + "name": "NUMBER", + "exponent": 1, + "constant": false + } + ] + ], + "NUMBER(2)": [ + [ + "NUMBER", + { + "name": "NUMBER", + "exponent": 2, + "constant": false + } + ] + ], + "NUMBER(-1)": [ + [ + "NUMBER", + { + "name": "NUMBER", + "exponent": -1, + "constant": false + } + ] + ], + "[NUMBER]": [ + [ + "NUMBER", + { + "name": "NUMBER", + "exponent": 1, + "constant": true + } + ] + ], + "FORCE*LENGTH": [ + [ + "FORCE", + { + "name": "FORCE", + "exponent": 1, + "constant": false + } + ], + [ + "LENGTH", + { + "name": "LENGTH", + "exponent": 1, + "constant": false + } + ] + ], + "WORK*TIME(-1)": [ + [ + "WORK", + { + "name": "WORK", + "exponent": 1, + "constant": false + } + ], + [ + "TIME", + { + "name": "TIME", + "exponent": -1, + "constant": false + } + ] + ], + "LENGTH*LENGTH(-1)*TEMPERATURE_CHANGE(-1)": [ + [ + "LENGTH", + { + "name": "LENGTH", + "exponent": 0, + "constant": false + } + ], + [ + "TEMPERATURE_CHANGE", + { + "name": "TEMPERATURE_CHANGE", + "exponent": -1, + "constant": false + } + ] + ], + "[PI]*RAD": [ + [ + "PI", + { + "name": "PI", + "exponent": 1, + "constant": true + } + ], + [ + "RAD", + { + "name": "RAD", + "exponent": 1, + "constant": false + } + ] + ], + "BTU*IN*FT(-2)*HR(-1)*DELTA_FAHRENHEIT(-1)": [ + [ + "BTU", + { + "name": "BTU", + "exponent": 1, + "constant": false + } + ], + [ + "IN", + { + "name": "IN", + "exponent": 1, + "constant": false + } + ], + [ + "FT", + { + "name": "FT", + "exponent": -2, + "constant": false + } + ], + [ + "HR", + { + "name": "HR", + "exponent": -1, + "constant": false + } + ], + [ + "DELTA_FAHRENHEIT", + { + "name": "DELTA_FAHRENHEIT", + "exponent": -1, + "constant": false + } + ] + ], + "M(3)*M(-3)": [ + [ + "M", + { + "name": "M", + "exponent": 0, + "constant": false + } + ] + ], + "[PI](2)*[PI](-2)*[PI](2)*[PI](-2)*[PI](2)*[PI](-2)": [ + [ + "PI", + { + "name": "PI", + "exponent": 0, + "constant": true + } + ] + ], + "BTU(-1)*[PI](2)*HR*[ONE]*TEMPERATURE_CHANGE(1)": [ + [ + "BTU", + { + "name": "BTU", + "exponent": -1, + "constant": false + } + ], + [ + "PI", + { + "name": "PI", + "exponent": 2, + "constant": true + } + ], + [ + "HR", + { + "name": "HR", + "exponent": 1, + "constant": false + } + ], + [ + "ONE", + { + "name": "ONE", + "exponent": 1, + "constant": true + } + ], + [ + "TEMPERATURE_CHANGE", + { + "name": "TEMPERATURE_CHANGE", + "exponent": 1, + "constant": false + } + ] + ], + "alias:NUMBER": [ + [ + "alias:NUMBER", + { + "name": "alias:NUMBER", + "exponent": 1, + "constant": false + } + ] + ], + "alias:NUMBER(2)": [ + [ + "alias:NUMBER", + { + "name": "alias:NUMBER", + "exponent": 2, + "constant": false + } + ] + ], + "alias:NUMBER(-1)": [ + [ + "alias:NUMBER", + { + "name": "alias:NUMBER", + "exponent": -1, + "constant": false + } + ] + ], + "[alias:NUMBER]": [ + [ + "alias:NUMBER", + { + "name": "alias:NUMBER", + "exponent": 1, + "constant": true + } + ] + ], + "alias:FORCE*alias:LENGTH": [ + [ + "alias:FORCE", + { + "name": "alias:FORCE", + "exponent": 1, + "constant": false + } + ], + [ + "alias:LENGTH", + { + "name": "alias:LENGTH", + "exponent": 1, + "constant": false + } + ] + ], + "alias:WORK*alias:TIME(-1)": [ + [ + "alias:WORK", + { + "name": "alias:WORK", + "exponent": 1, + "constant": false + } + ], + [ + "alias:TIME", + { + "name": "alias:TIME", + "exponent": -1, + "constant": false + } + ] + ], + "alias:LENGTH*alias:LENGTH(-1)*alias:TEMPERATURE_CHANGE(-1)": [ + [ + "alias:LENGTH", + { + "name": "alias:LENGTH", + "exponent": 0, + "constant": false + } + ], + [ + "alias:TEMPERATURE_CHANGE", + { + "name": "alias:TEMPERATURE_CHANGE", + "exponent": -1, + "constant": false + } + ] + ], + "[alias:PI]*alias:RAD": [ + [ + "alias:PI", + { + "name": "alias:PI", + "exponent": 1, + "constant": true + } + ], + [ + "alias:RAD", + { + "name": "alias:RAD", + "exponent": 1, + "constant": false + } + ] + ], + "alias:BTU*alias:IN*alias:FT(-2)*alias:HR(-1)*alias:DELTA_FAHRENHEIT(-1)": [ + [ + "alias:BTU", + { + "name": "alias:BTU", + "exponent": 1, + "constant": false + } + ], + [ + "alias:IN", + { + "name": "alias:IN", + "exponent": 1, + "constant": false + } + ], + [ + "alias:FT", + { + "name": "alias:FT", + "exponent": -2, + "constant": false + } + ], + [ + "alias:HR", + { + "name": "alias:HR", + "exponent": -1, + "constant": false + } + ], + [ + "alias:DELTA_FAHRENHEIT", + { + "name": "alias:DELTA_FAHRENHEIT", + "exponent": -1, + "constant": false + } + ] + ], + "alias:M(3)*alias:M(-3)": [ + [ + "alias:M", + { + "name": "alias:M", + "exponent": 0, + "constant": false + } + ] + ], + "[alias:PI](2)*[alias:PI](-2)*[alias:PI](2)*[alias:PI](-2)*[alias:PI](2)*[alias:PI](-2)": [ + [ + "alias:PI", + { + "name": "alias:PI", + "exponent": 0, + "constant": true + } + ] + ], + "alias:BTU(-1)*[alias:PI](2)*alias:HR*[alias:ONE]*alias:TEMPERATURE_CHANGE(1)": [ + [ + "alias:BTU", + { + "name": "alias:BTU", + "exponent": -1, + "constant": false + } + ], + [ + "alias:PI", + { + "name": "alias:PI", + "exponent": 2, + "constant": true + } + ], + [ + "alias:HR", + { + "name": "alias:HR", + "exponent": 1, + "constant": false + } + ], + [ + "alias:ONE", + { + "name": "alias:ONE", + "exponent": 1, + "constant": true + } + ], + [ + "alias:TEMPERATURE_CHANGE", + { + "name": "alias:TEMPERATURE_CHANGE", + "exponent": 1, + "constant": false + } + ] + ] +} \ No newline at end of file diff --git a/core/ecschema-metadata/test/UnitConversion/assets/SIUnits.ecschema.xml b/core/ecschema-metadata/src/test/assets/SIUnits.ecschema.xml similarity index 100% rename from core/ecschema-metadata/test/UnitConversion/assets/SIUnits.ecschema.xml rename to core/ecschema-metadata/src/test/assets/SIUnits.ecschema.xml diff --git a/core/ecschema-metadata/test/UnitConversion/assets/USUnits.ecschema.xml b/core/ecschema-metadata/src/test/assets/USUnits.ecschema.xml similarity index 100% rename from core/ecschema-metadata/test/UnitConversion/assets/USUnits.ecschema.xml rename to core/ecschema-metadata/src/test/assets/USUnits.ecschema.xml diff --git a/core/ecschema-metadata/src/test/assets/UnitTests.json b/core/ecschema-metadata/src/test/assets/UnitTests.json new file mode 100644 index 00000000000..eceba9d75b7 --- /dev/null +++ b/core/ecschema-metadata/src/test/assets/UnitTests.json @@ -0,0 +1,9038 @@ +[ + { + "from": "SQ_FT", + "input": 42.42, + "to": "SQ_M", + "expect": 3.9409469568 + }, + { + "from": "SQ_FT", + "input": 42.42, + "to": "SQ_MM", + "expect": 3940946.9568 + }, + { + "from": "SQ_FT", + "input": 42.42, + "to": "SQ_UM", + "expect": 3940946956800 + }, + { + "from": "SQ_FT", + "input": 42.42, + "to": "SQ_CM", + "expect": 39409.46956799999 + }, + { + "from": "SQ_FT", + "input": 42.42, + "to": "SQ_DM", + "expect": 394.0946956799999 + }, + { + "from": "SQ_FT", + "input": 42.42, + "to": "SQ_KM", + "expect": 0.0000039409469568 + }, + { + "from": "SQ_FT", + "input": 42.42, + "to": "ARE", + "expect": 0.039409469568 + }, + { + "from": "SQ_FT", + "input": 42.42, + "to": "HECTARE", + "expect": 0.00039409469568 + }, + { + "from": "SQ_FT", + "input": 42.42, + "to": "SQ_IN", + "expect": 6108.4800000000005 + }, + { + "from": "SQ_FT", + "input": 42.42, + "to": "SQ_FT", + "expect": 42.42 + }, + { + "from": "SQ_FT", + "input": 42.42, + "to": "THOUSAND_SQ_FT", + "expect": 0.04242 + }, + { + "from": "SQ_FT", + "input": 42.42, + "to": "SQ_YRD", + "expect": 4.713333333333333 + }, + { + "from": "SQ_FT", + "input": 42.42, + "to": "SQ_MILE", + "expect": 0.0000015216081267217631 + }, + { + "from": "SQ_FT", + "input": 42.42, + "to": "SQ_CHAIN", + "expect": 0.009738292011019284 + }, + { + "from": "SQ_FT", + "input": 42.42, + "to": "ACRE", + "expect": 0.0009738292011019284 + }, + { + "from": "SQ_FT", + "input": 42.42, + "to": "SQ_US_SURVEY_IN", + "expect": 6108.455566104434 + }, + { + "from": "SQ_FT", + "input": 42.42, + "to": "SQ_US_SURVEY_FT", + "expect": 42.41983032016968 + }, + { + "from": "SQ_FT", + "input": 42.42, + "to": "SQ_US_SURVEY_YRD", + "expect": 4.713314480018854 + }, + { + "from": "SQ_FT", + "input": 42.42, + "to": "SQ_US_SURVEY_MILE", + "expect": 0.0000015216020402953428 + }, + { + "from": "SQ_FT", + "input": 42.42, + "to": "SQ_US_SURVEY_CHAIN", + "expect": 0.009738253057890193 + }, + { + "from": "SQ_FT", + "input": 42.42, + "to": "US_SURVEY_ACRE", + "expect": 0.0009738253057890192 + }, + { + "from": "THOUSAND_SQ_FT", + "input": 42.42, + "to": "SQ_M", + "expect": 3940.9469568 + }, + { + "from": "THOUSAND_SQ_FT", + "input": 42.42, + "to": "SQ_UM", + "expect": 3940946956800000 + }, + { + "from": "THOUSAND_SQ_FT", + "input": 42.42, + "to": "SQ_MM", + "expect": 3940946956.8 + }, + { + "from": "THOUSAND_SQ_FT", + "input": 42.42, + "to": "SQ_CM", + "expect": 39409469.568 + }, + { + "from": "THOUSAND_SQ_FT", + "input": 42.42, + "to": "SQ_DM", + "expect": 394094.69568 + }, + { + "from": "THOUSAND_SQ_FT", + "input": 42.42, + "to": "SQ_KM", + "expect": 0.003940946956799999 + }, + { + "from": "THOUSAND_SQ_FT", + "input": 42.42, + "to": "ARE", + "expect": 39.409469568 + }, + { + "from": "THOUSAND_SQ_FT", + "input": 42.42, + "to": "HECTARE", + "expect": 0.39409469568 + }, + { + "from": "THOUSAND_SQ_FT", + "input": 42.42, + "to": "SQ_IN", + "expect": 6108480 + }, + { + "from": "THOUSAND_SQ_FT", + "input": 42.42, + "to": "SQ_FT", + "expect": 42420 + }, + { + "from": "THOUSAND_SQ_FT", + "input": 42.42, + "to": "THOUSAND_SQ_FT", + "expect": 42.42 + }, + { + "from": "THOUSAND_SQ_FT", + "input": 42.42, + "to": "SQ_YRD", + "expect": 4713.333333333334 + }, + { + "from": "THOUSAND_SQ_FT", + "input": 42.42, + "to": "SQ_MILE", + "expect": 0.001521608126721763 + }, + { + "from": "THOUSAND_SQ_FT", + "input": 42.42, + "to": "SQ_CHAIN", + "expect": 9.738292011019285 + }, + { + "from": "THOUSAND_SQ_FT", + "input": 42.42, + "to": "ACRE", + "expect": 0.9738292011019284 + }, + { + "from": "THOUSAND_SQ_FT", + "input": 42.42, + "to": "SQ_US_SURVEY_IN", + "expect": 6108455.5661044335 + }, + { + "from": "THOUSAND_SQ_FT", + "input": 42.42, + "to": "SQ_US_SURVEY_FT", + "expect": 42419.830320169676 + }, + { + "from": "THOUSAND_SQ_FT", + "input": 42.42, + "to": "SQ_US_SURVEY_YRD", + "expect": 4713.314480018853 + }, + { + "from": "THOUSAND_SQ_FT", + "input": 42.42, + "to": "SQ_US_SURVEY_MILE", + "expect": 0.0015216020402953425 + }, + { + "from": "THOUSAND_SQ_FT", + "input": 42.42, + "to": "SQ_US_SURVEY_CHAIN", + "expect": 9.738253057890192 + }, + { + "from": "THOUSAND_SQ_FT", + "input": 42.42, + "to": "US_SURVEY_ACRE", + "expect": 0.9738253057890192 + }, + { + "from": "SQ_YRD", + "input": 42.42, + "to": "SQ_M", + "expect": 35.468522611199994 + }, + { + "from": "SQ_YRD", + "input": 42.42, + "to": "SQ_UM", + "expect": 35468522611200 + }, + { + "from": "SQ_YRD", + "input": 42.42, + "to": "SQ_MM", + "expect": 35468522.6112 + }, + { + "from": "SQ_YRD", + "input": 42.42, + "to": "SQ_CM", + "expect": 354685.22611199995 + }, + { + "from": "SQ_YRD", + "input": 42.42, + "to": "SQ_DM", + "expect": 3546.8522611199987 + }, + { + "from": "SQ_YRD", + "input": 42.42, + "to": "SQ_KM", + "expect": 0.000035468522611199996 + }, + { + "from": "SQ_YRD", + "input": 42.42, + "to": "ARE", + "expect": 0.35468522611199993 + }, + { + "from": "SQ_YRD", + "input": 42.42, + "to": "HECTARE", + "expect": 0.0035468522611199994 + }, + { + "from": "SQ_YRD", + "input": 42.42, + "to": "SQ_IN", + "expect": 54976.32 + }, + { + "from": "SQ_YRD", + "input": 42.42, + "to": "SQ_FT", + "expect": 381.78000000000003 + }, + { + "from": "SQ_YRD", + "input": 42.42, + "to": "THOUSAND_SQ_FT", + "expect": 0.38178 + }, + { + "from": "SQ_YRD", + "input": 42.42, + "to": "SQ_YRD", + "expect": 42.42 + }, + { + "from": "SQ_YRD", + "input": 42.42, + "to": "SQ_MILE", + "expect": 0.00001369447314049587 + }, + { + "from": "SQ_YRD", + "input": 42.42, + "to": "SQ_CHAIN", + "expect": 0.08764462809917356 + }, + { + "from": "SQ_YRD", + "input": 42.42, + "to": "ACRE", + "expect": 0.008764462809917356 + }, + { + "from": "SQ_YRD", + "input": 42.42, + "to": "SQ_US_SURVEY_IN", + "expect": 54976.1000949399 + }, + { + "from": "SQ_YRD", + "input": 42.42, + "to": "SQ_US_SURVEY_FT", + "expect": 381.77847288152714 + }, + { + "from": "SQ_YRD", + "input": 42.42, + "to": "SQ_US_SURVEY_YRD", + "expect": 42.41983032016968 + }, + { + "from": "SQ_YRD", + "input": 42.42, + "to": "SQ_US_SURVEY_MILE", + "expect": 0.000013694418362658081 + }, + { + "from": "SQ_YRD", + "input": 42.42, + "to": "SQ_US_SURVEY_CHAIN", + "expect": 0.08764427752101174 + }, + { + "from": "SQ_YRD", + "input": 42.42, + "to": "US_SURVEY_ACRE", + "expect": 0.008764427752101173 + }, + { + "from": "SQ_MILE", + "input": 42.42, + "to": "SQ_M", + "expect": 109867295.64045312 + }, + { + "from": "SQ_MILE", + "input": 42.42, + "to": "SQ_UM", + "expect": 109867295640453100000 + }, + { + "from": "SQ_MILE", + "input": 42.42, + "to": "SQ_MM", + "expect": 109867295640453.12 + }, + { + "from": "SQ_MILE", + "input": 42.42, + "to": "SQ_CM", + "expect": 1098672956404.5311 + }, + { + "from": "SQ_MILE", + "input": 42.42, + "to": "SQ_DM", + "expect": 10986729564.04531 + }, + { + "from": "SQ_MILE", + "input": 42.42, + "to": "SQ_KM", + "expect": 109.86729564045312 + }, + { + "from": "SQ_MILE", + "input": 42.42, + "to": "ARE", + "expect": 1098672.9564045311 + }, + { + "from": "SQ_MILE", + "input": 42.42, + "to": "HECTARE", + "expect": 10986.72956404531 + }, + { + "from": "SQ_MILE", + "input": 42.42, + "to": "SQ_IN", + "expect": 170294648832 + }, + { + "from": "SQ_MILE", + "input": 42.42, + "to": "SQ_FT", + "expect": 1182601728 + }, + { + "from": "SQ_MILE", + "input": 42.42, + "to": "THOUSAND_SQ_FT", + "expect": 1182601.7280000001 + }, + { + "from": "SQ_MILE", + "input": 42.42, + "to": "SQ_YRD", + "expect": 131400192 + }, + { + "from": "SQ_MILE", + "input": 42.42, + "to": "SQ_MILE", + "expect": 42.42 + }, + { + "from": "SQ_MILE", + "input": 42.42, + "to": "SQ_CHAIN", + "expect": 271488 + }, + { + "from": "SQ_MILE", + "input": 42.42, + "to": "ACRE", + "expect": 27148.800000000003 + }, + { + "from": "SQ_MILE", + "input": 42.42, + "to": "SQ_US_SURVEY_IN", + "expect": 170293967654.08585 + }, + { + "from": "SQ_MILE", + "input": 42.42, + "to": "SQ_US_SURVEY_FT", + "expect": 1182596997.5978184 + }, + { + "from": "SQ_MILE", + "input": 42.42, + "to": "SQ_US_SURVEY_YRD", + "expect": 131399666.39975758 + }, + { + "from": "SQ_MILE", + "input": 42.42, + "to": "SQ_US_SURVEY_MILE", + "expect": 42.41983032016968 + }, + { + "from": "SQ_MILE", + "input": 42.42, + "to": "SQ_US_SURVEY_CHAIN", + "expect": 271486.9140490859 + }, + { + "from": "SQ_MILE", + "input": 42.42, + "to": "US_SURVEY_ACRE", + "expect": 27148.691404908597 + }, + { + "from": "SQ_CHAIN", + "input": 42.42, + "to": "SQ_M", + "expect": 17166.7649438208 + }, + { + "from": "SQ_CHAIN", + "input": 42.42, + "to": "SQ_UM", + "expect": 17166764943820800 + }, + { + "from": "SQ_CHAIN", + "input": 42.42, + "to": "SQ_MM", + "expect": 17166764943.820799 + }, + { + "from": "SQ_CHAIN", + "input": 42.42, + "to": "SQ_CM", + "expect": 171667649.43820798 + }, + { + "from": "SQ_CHAIN", + "input": 42.42, + "to": "SQ_DM", + "expect": 1716676.49438208 + }, + { + "from": "SQ_CHAIN", + "input": 42.42, + "to": "SQ_KM", + "expect": 0.0171667649438208 + }, + { + "from": "SQ_CHAIN", + "input": 42.42, + "to": "ARE", + "expect": 171.66764943820803 + }, + { + "from": "SQ_CHAIN", + "input": 42.42, + "to": "HECTARE", + "expect": 1.71667649438208 + }, + { + "from": "SQ_CHAIN", + "input": 42.42, + "to": "SQ_IN", + "expect": 26608538.880000003 + }, + { + "from": "SQ_CHAIN", + "input": 42.42, + "to": "SQ_FT", + "expect": 184781.52000000002 + }, + { + "from": "SQ_CHAIN", + "input": 42.42, + "to": "THOUSAND_SQ_FT", + "expect": 184.78152 + }, + { + "from": "SQ_CHAIN", + "input": 42.42, + "to": "SQ_YRD", + "expect": 20531.280000000002 + }, + { + "from": "SQ_CHAIN", + "input": 42.42, + "to": "SQ_MILE", + "expect": 0.006628125 + }, + { + "from": "SQ_CHAIN", + "input": 42.42, + "to": "SQ_CHAIN", + "expect": 42.42 + }, + { + "from": "SQ_CHAIN", + "input": 42.42, + "to": "ACRE", + "expect": 4.242 + }, + { + "from": "SQ_CHAIN", + "input": 42.42, + "to": "SQ_US_SURVEY_IN", + "expect": 26608432.445950914 + }, + { + "from": "SQ_CHAIN", + "input": 42.42, + "to": "SQ_US_SURVEY_FT", + "expect": 184780.7808746591 + }, + { + "from": "SQ_CHAIN", + "input": 42.42, + "to": "SQ_US_SURVEY_YRD", + "expect": 20531.197874962123 + }, + { + "from": "SQ_CHAIN", + "input": 42.42, + "to": "SQ_US_SURVEY_MILE", + "expect": 0.006628098487526513 + }, + { + "from": "SQ_CHAIN", + "input": 42.42, + "to": "SQ_US_SURVEY_CHAIN", + "expect": 42.41983032016968 + }, + { + "from": "SQ_CHAIN", + "input": 42.42, + "to": "US_SURVEY_ACRE", + "expect": 4.241983032016969 + }, + { + "from": "ACRE", + "input": 42.42, + "to": "SQ_M", + "expect": 171667.649438208 + }, + { + "from": "ACRE", + "input": 42.42, + "to": "SQ_UM", + "expect": 171667649438208000 + }, + { + "from": "ACRE", + "input": 42.42, + "to": "SQ_MM", + "expect": 171667649438.20798 + }, + { + "from": "ACRE", + "input": 42.42, + "to": "SQ_CM", + "expect": 1716676494.3820798 + }, + { + "from": "ACRE", + "input": 42.42, + "to": "SQ_DM", + "expect": 17166764.943820797 + }, + { + "from": "ACRE", + "input": 42.42, + "to": "SQ_KM", + "expect": 0.171667649438208 + }, + { + "from": "ACRE", + "input": 42.42, + "to": "ARE", + "expect": 1716.67649438208 + }, + { + "from": "ACRE", + "input": 42.42, + "to": "HECTARE", + "expect": 17.166764943820798 + }, + { + "from": "ACRE", + "input": 42.42, + "to": "SQ_IN", + "expect": 266085388.8 + }, + { + "from": "ACRE", + "input": 42.42, + "to": "SQ_FT", + "expect": 1847815.2000000002 + }, + { + "from": "ACRE", + "input": 42.42, + "to": "THOUSAND_SQ_FT", + "expect": 1847.8152000000002 + }, + { + "from": "ACRE", + "input": 42.42, + "to": "SQ_YRD", + "expect": 205312.80000000002 + }, + { + "from": "ACRE", + "input": 42.42, + "to": "SQ_MILE", + "expect": 0.06628125 + }, + { + "from": "ACRE", + "input": 42.42, + "to": "SQ_CHAIN", + "expect": 424.20000000000005 + }, + { + "from": "ACRE", + "input": 42.42, + "to": "ACRE", + "expect": 42.42 + }, + { + "from": "ACRE", + "input": 42.42, + "to": "SQ_US_SURVEY_IN", + "expect": 266084324.45950913 + }, + { + "from": "ACRE", + "input": 42.42, + "to": "SQ_US_SURVEY_FT", + "expect": 1847807.8087465912 + }, + { + "from": "ACRE", + "input": 42.42, + "to": "SQ_US_SURVEY_YRD", + "expect": 205311.97874962122 + }, + { + "from": "ACRE", + "input": 42.42, + "to": "SQ_US_SURVEY_MILE", + "expect": 0.06628098487526513 + }, + { + "from": "ACRE", + "input": 42.42, + "to": "SQ_US_SURVEY_CHAIN", + "expect": 424.1983032016967 + }, + { + "from": "ACRE", + "input": 42.42, + "to": "US_SURVEY_ACRE", + "expect": 42.41983032016967 + }, + { + "from": "SQ_US_SURVEY_IN", + "input": 42.42, + "to": "SQ_M", + "expect": 0.027367796671077214 + }, + { + "from": "SQ_US_SURVEY_IN", + "input": 42.42, + "to": "SQ_UM", + "expect": 27367796671.077213 + }, + { + "from": "SQ_US_SURVEY_IN", + "input": 42.42, + "to": "SQ_MM", + "expect": 27367.796671077216 + }, + { + "from": "SQ_US_SURVEY_IN", + "input": 42.42, + "to": "SQ_CM", + "expect": 273.6779667107721 + }, + { + "from": "SQ_US_SURVEY_IN", + "input": 42.42, + "to": "SQ_DM", + "expect": 2.736779667107721 + }, + { + "from": "SQ_US_SURVEY_IN", + "input": 42.42, + "to": "SQ_KM", + "expect": 2.7367796671077217e-8 + }, + { + "from": "SQ_US_SURVEY_IN", + "input": 42.42, + "to": "ARE", + "expect": 0.00027367796671077214 + }, + { + "from": "SQ_US_SURVEY_IN", + "input": 42.42, + "to": "HECTARE", + "expect": 0.0000027367796671077212 + }, + { + "from": "SQ_US_SURVEY_IN", + "input": 42.42, + "to": "SQ_IN", + "expect": 42.42016968050905 + }, + { + "from": "SQ_US_SURVEY_IN", + "input": 42.42, + "to": "SQ_FT", + "expect": 0.2945845116702017 + }, + { + "from": "SQ_US_SURVEY_IN", + "input": 42.42, + "to": "THOUSAND_SQ_FT", + "expect": 0.0002945845116702017 + }, + { + "from": "SQ_US_SURVEY_IN", + "input": 42.42, + "to": "SQ_YRD", + "expect": 0.03273161240780019 + }, + { + "from": "SQ_US_SURVEY_IN", + "input": 42.42, + "to": "SQ_MILE", + "expect": 1.0566765369253676e-8 + }, + { + "from": "SQ_US_SURVEY_IN", + "input": 42.42, + "to": "SQ_CHAIN", + "expect": 0.00006762729836322353 + }, + { + "from": "SQ_US_SURVEY_IN", + "input": 42.42, + "to": "ACRE", + "expect": 0.000006762729836322352 + }, + { + "from": "SQ_US_SURVEY_IN", + "input": 42.42, + "to": "SQ_US_SURVEY_IN", + "expect": 42.42 + }, + { + "from": "SQ_US_SURVEY_IN", + "input": 42.42, + "to": "SQ_US_SURVEY_FT", + "expect": 0.2945833333333333 + }, + { + "from": "SQ_US_SURVEY_IN", + "input": 42.42, + "to": "SQ_US_SURVEY_YRD", + "expect": 0.03273148148148148 + }, + { + "from": "SQ_US_SURVEY_IN", + "input": 42.42, + "to": "SQ_US_SURVEY_MILE", + "expect": 1.0566723102234465e-8 + }, + { + "from": "SQ_US_SURVEY_IN", + "input": 42.42, + "to": "SQ_US_SURVEY_CHAIN", + "expect": 0.00006762702785430058 + }, + { + "from": "SQ_US_SURVEY_IN", + "input": 42.42, + "to": "US_SURVEY_ACRE", + "expect": 0.000006762702785430058 + }, + { + "from": "SQ_US_SURVEY_FT", + "input": 42.42, + "to": "SQ_M", + "expect": 3.940962720635119 + }, + { + "from": "SQ_US_SURVEY_FT", + "input": 42.42, + "to": "SQ_UM", + "expect": 3940962720635.119 + }, + { + "from": "SQ_US_SURVEY_FT", + "input": 42.42, + "to": "SQ_MM", + "expect": 3940962.720635119 + }, + { + "from": "SQ_US_SURVEY_FT", + "input": 42.42, + "to": "SQ_CM", + "expect": 39409.627206351186 + }, + { + "from": "SQ_US_SURVEY_FT", + "input": 42.42, + "to": "SQ_DM", + "expect": 394.09627206351183 + }, + { + "from": "SQ_US_SURVEY_FT", + "input": 42.42, + "to": "SQ_KM", + "expect": 0.000003940962720635118 + }, + { + "from": "SQ_US_SURVEY_FT", + "input": 42.42, + "to": "ARE", + "expect": 0.03940962720635119 + }, + { + "from": "SQ_US_SURVEY_FT", + "input": 42.42, + "to": "HECTARE", + "expect": 0.0003940962720635119 + }, + { + "from": "SQ_US_SURVEY_FT", + "input": 42.42, + "to": "SQ_IN", + "expect": 6108.504433993302 + }, + { + "from": "SQ_US_SURVEY_FT", + "input": 42.42, + "to": "SQ_FT", + "expect": 42.42016968050905 + }, + { + "from": "SQ_US_SURVEY_FT", + "input": 42.42, + "to": "THOUSAND_SQ_FT", + "expect": 0.042420169680509046 + }, + { + "from": "SQ_US_SURVEY_FT", + "input": 42.42, + "to": "SQ_YRD", + "expect": 4.7133521867232275 + }, + { + "from": "SQ_US_SURVEY_FT", + "input": 42.42, + "to": "SQ_MILE", + "expect": 0.0000015216142131725291 + }, + { + "from": "SQ_US_SURVEY_FT", + "input": 42.42, + "to": "SQ_CHAIN", + "expect": 0.009738330964304188 + }, + { + "from": "SQ_US_SURVEY_FT", + "input": 42.42, + "to": "ACRE", + "expect": 0.0009738330964304188 + }, + { + "from": "SQ_US_SURVEY_FT", + "input": 42.42, + "to": "SQ_US_SURVEY_IN", + "expect": 6108.4800000000005 + }, + { + "from": "SQ_US_SURVEY_FT", + "input": 42.42, + "to": "SQ_US_SURVEY_FT", + "expect": 42.42 + }, + { + "from": "SQ_US_SURVEY_FT", + "input": 42.42, + "to": "SQ_US_SURVEY_YRD", + "expect": 4.713333333333333 + }, + { + "from": "SQ_US_SURVEY_FT", + "input": 42.42, + "to": "SQ_US_SURVEY_MILE", + "expect": 0.0000015216081267217631 + }, + { + "from": "SQ_US_SURVEY_FT", + "input": 42.42, + "to": "SQ_US_SURVEY_CHAIN", + "expect": 0.009738292011019284 + }, + { + "from": "SQ_US_SURVEY_FT", + "input": 42.42, + "to": "US_SURVEY_ACRE", + "expect": 0.0009738292011019284 + }, + { + "from": "SQ_US_SURVEY_YRD", + "input": 42.42, + "to": "SQ_M", + "expect": 35.46866448571607 + }, + { + "from": "SQ_US_SURVEY_YRD", + "input": 42.42, + "to": "SQ_UM", + "expect": 35468664485716.07 + }, + { + "from": "SQ_US_SURVEY_YRD", + "input": 42.42, + "to": "SQ_MM", + "expect": 35468664.485716075 + }, + { + "from": "SQ_US_SURVEY_YRD", + "input": 42.42, + "to": "SQ_CM", + "expect": 354686.6448571607 + }, + { + "from": "SQ_US_SURVEY_YRD", + "input": 42.42, + "to": "SQ_DM", + "expect": 3546.8664485716063 + }, + { + "from": "SQ_US_SURVEY_YRD", + "input": 42.42, + "to": "SQ_KM", + "expect": 0.00003546866448571607 + }, + { + "from": "SQ_US_SURVEY_YRD", + "input": 42.42, + "to": "ARE", + "expect": 0.35468664485716067 + }, + { + "from": "SQ_US_SURVEY_YRD", + "input": 42.42, + "to": "HECTARE", + "expect": 0.0035468664485716075 + }, + { + "from": "SQ_US_SURVEY_YRD", + "input": 42.42, + "to": "SQ_IN", + "expect": 54976.53990593973 + }, + { + "from": "SQ_US_SURVEY_YRD", + "input": 42.42, + "to": "SQ_FT", + "expect": 381.7815271245815 + }, + { + "from": "SQ_US_SURVEY_YRD", + "input": 42.42, + "to": "THOUSAND_SQ_FT", + "expect": 0.38178152712458147 + }, + { + "from": "SQ_US_SURVEY_YRD", + "input": 42.42, + "to": "SQ_YRD", + "expect": 42.42016968050905 + }, + { + "from": "SQ_US_SURVEY_YRD", + "input": 42.42, + "to": "SQ_MILE", + "expect": 0.000013694527918552765 + }, + { + "from": "SQ_US_SURVEY_YRD", + "input": 42.42, + "to": "SQ_CHAIN", + "expect": 0.08764497867873769 + }, + { + "from": "SQ_US_SURVEY_YRD", + "input": 42.42, + "to": "ACRE", + "expect": 0.008764497867873769 + }, + { + "from": "SQ_US_SURVEY_YRD", + "input": 42.42, + "to": "SQ_US_SURVEY_IN", + "expect": 54976.32 + }, + { + "from": "SQ_US_SURVEY_YRD", + "input": 42.42, + "to": "SQ_US_SURVEY_FT", + "expect": 381.78000000000003 + }, + { + "from": "SQ_US_SURVEY_YRD", + "input": 42.42, + "to": "SQ_US_SURVEY_YRD", + "expect": 42.42 + }, + { + "from": "SQ_US_SURVEY_YRD", + "input": 42.42, + "to": "SQ_US_SURVEY_MILE", + "expect": 0.00001369447314049587 + }, + { + "from": "SQ_US_SURVEY_YRD", + "input": 42.42, + "to": "SQ_US_SURVEY_CHAIN", + "expect": 0.08764462809917356 + }, + { + "from": "SQ_US_SURVEY_YRD", + "input": 42.42, + "to": "US_SURVEY_ACRE", + "expect": 0.008764462809917356 + }, + { + "from": "SQ_US_SURVEY_MILE", + "input": 42.42, + "to": "SQ_M", + "expect": 109867735.11095409 + }, + { + "from": "SQ_US_SURVEY_MILE", + "input": 42.42, + "to": "SQ_UM", + "expect": 109867735110954090000 + }, + { + "from": "SQ_US_SURVEY_MILE", + "input": 42.42, + "to": "SQ_MM", + "expect": 109867735110954.11 + }, + { + "from": "SQ_US_SURVEY_MILE", + "input": 42.42, + "to": "SQ_CM", + "expect": 1098677351109.5408 + }, + { + "from": "SQ_US_SURVEY_MILE", + "input": 42.42, + "to": "SQ_DM", + "expect": 10986773511.095407 + }, + { + "from": "SQ_US_SURVEY_MILE", + "input": 42.42, + "to": "SQ_KM", + "expect": 109.86773511095409 + }, + { + "from": "SQ_US_SURVEY_MILE", + "input": 42.42, + "to": "ARE", + "expect": 1098677.351109541 + }, + { + "from": "SQ_US_SURVEY_MILE", + "input": 42.42, + "to": "HECTARE", + "expect": 10986.773511095409 + }, + { + "from": "SQ_US_SURVEY_MILE", + "input": 42.42, + "to": "SQ_IN", + "expect": 170295330012.6389 + }, + { + "from": "SQ_US_SURVEY_MILE", + "input": 42.42, + "to": "SQ_FT", + "expect": 1182606458.4211035 + }, + { + "from": "SQ_US_SURVEY_MILE", + "input": 42.42, + "to": "THOUSAND_SQ_FT", + "expect": 1182606.4584211034 + }, + { + "from": "SQ_US_SURVEY_MILE", + "input": 42.42, + "to": "SQ_YRD", + "expect": 131400717.60234481 + }, + { + "from": "SQ_US_SURVEY_MILE", + "input": 42.42, + "to": "SQ_MILE", + "expect": 42.42016968050905 + }, + { + "from": "SQ_US_SURVEY_MILE", + "input": 42.42, + "to": "SQ_CHAIN", + "expect": 271489.0859552579 + }, + { + "from": "SQ_US_SURVEY_MILE", + "input": 42.42, + "to": "ACRE", + "expect": 27148.908595525787 + }, + { + "from": "SQ_US_SURVEY_MILE", + "input": 42.42, + "to": "SQ_US_SURVEY_IN", + "expect": 170294648832 + }, + { + "from": "SQ_US_SURVEY_MILE", + "input": 42.42, + "to": "SQ_US_SURVEY_FT", + "expect": 1182601728 + }, + { + "from": "SQ_US_SURVEY_MILE", + "input": 42.42, + "to": "SQ_US_SURVEY_YRD", + "expect": 131400192 + }, + { + "from": "SQ_US_SURVEY_MILE", + "input": 42.42, + "to": "SQ_US_SURVEY_MILE", + "expect": 42.42 + }, + { + "from": "SQ_US_SURVEY_MILE", + "input": 42.42, + "to": "SQ_US_SURVEY_CHAIN", + "expect": 271488 + }, + { + "from": "SQ_US_SURVEY_MILE", + "input": 42.42, + "to": "US_SURVEY_ACRE", + "expect": 27148.800000000003 + }, + { + "from": "SQ_US_SURVEY_CHAIN", + "input": 42.42, + "to": "SQ_M", + "expect": 17166.83361108658 + }, + { + "from": "SQ_US_SURVEY_CHAIN", + "input": 42.42, + "to": "SQ_UM", + "expect": 17166833611086578 + }, + { + "from": "SQ_US_SURVEY_CHAIN", + "input": 42.42, + "to": "SQ_MM", + "expect": 17166833611.086578 + }, + { + "from": "SQ_US_SURVEY_CHAIN", + "input": 42.42, + "to": "SQ_CM", + "expect": 171668336.11086577 + }, + { + "from": "SQ_US_SURVEY_CHAIN", + "input": 42.42, + "to": "SQ_DM", + "expect": 1716683.3611086574 + }, + { + "from": "SQ_US_SURVEY_CHAIN", + "input": 42.42, + "to": "SQ_KM", + "expect": 0.017166833611086577 + }, + { + "from": "SQ_US_SURVEY_CHAIN", + "input": 42.42, + "to": "ARE", + "expect": 171.66833611086577 + }, + { + "from": "SQ_US_SURVEY_CHAIN", + "input": 42.42, + "to": "HECTARE", + "expect": 1.7166833611086576 + }, + { + "from": "SQ_US_SURVEY_CHAIN", + "input": 42.42, + "to": "SQ_IN", + "expect": 26608645.314474825 + }, + { + "from": "SQ_US_SURVEY_CHAIN", + "input": 42.42, + "to": "SQ_FT", + "expect": 184782.25912829742 + }, + { + "from": "SQ_US_SURVEY_CHAIN", + "input": 42.42, + "to": "THOUSAND_SQ_FT", + "expect": 184.78225912829743 + }, + { + "from": "SQ_US_SURVEY_CHAIN", + "input": 42.42, + "to": "SQ_YRD", + "expect": 20531.36212536638 + }, + { + "from": "SQ_US_SURVEY_CHAIN", + "input": 42.42, + "to": "SQ_MILE", + "expect": 0.006628151512579538 + }, + { + "from": "SQ_US_SURVEY_CHAIN", + "input": 42.42, + "to": "SQ_CHAIN", + "expect": 42.42016968050905 + }, + { + "from": "SQ_US_SURVEY_CHAIN", + "input": 42.42, + "to": "ACRE", + "expect": 4.242016968050904 + }, + { + "from": "SQ_US_SURVEY_CHAIN", + "input": 42.42, + "to": "SQ_US_SURVEY_IN", + "expect": 26608538.880000003 + }, + { + "from": "SQ_US_SURVEY_CHAIN", + "input": 42.42, + "to": "SQ_US_SURVEY_FT", + "expect": 184781.52000000002 + }, + { + "from": "SQ_US_SURVEY_CHAIN", + "input": 42.42, + "to": "SQ_US_SURVEY_YRD", + "expect": 20531.280000000002 + }, + { + "from": "SQ_US_SURVEY_CHAIN", + "input": 42.42, + "to": "SQ_US_SURVEY_MILE", + "expect": 0.006628125 + }, + { + "from": "SQ_US_SURVEY_CHAIN", + "input": 42.42, + "to": "SQ_US_SURVEY_CHAIN", + "expect": 42.42 + }, + { + "from": "SQ_US_SURVEY_CHAIN", + "input": 42.42, + "to": "US_SURVEY_ACRE", + "expect": 4.242 + }, + { + "from": "US_SURVEY_ACRE", + "input": 42.42, + "to": "SQ_M", + "expect": 171668.33611086576 + }, + { + "from": "US_SURVEY_ACRE", + "input": 42.42, + "to": "SQ_UM", + "expect": 171668336110865800 + }, + { + "from": "US_SURVEY_ACRE", + "input": 42.42, + "to": "SQ_MM", + "expect": 171668336110.86578 + }, + { + "from": "US_SURVEY_ACRE", + "input": 42.42, + "to": "SQ_CM", + "expect": 1716683361.1086576 + }, + { + "from": "US_SURVEY_ACRE", + "input": 42.42, + "to": "SQ_DM", + "expect": 17166833.611086573 + }, + { + "from": "US_SURVEY_ACRE", + "input": 42.42, + "to": "SQ_KM", + "expect": 0.17166833611086577 + }, + { + "from": "US_SURVEY_ACRE", + "input": 42.42, + "to": "ARE", + "expect": 1716.6833611086574 + }, + { + "from": "US_SURVEY_ACRE", + "input": 42.42, + "to": "HECTARE", + "expect": 17.166833611086577 + }, + { + "from": "US_SURVEY_ACRE", + "input": 42.42, + "to": "SQ_IN", + "expect": 266086453.14474824 + }, + { + "from": "US_SURVEY_ACRE", + "input": 42.42, + "to": "SQ_FT", + "expect": 1847822.5912829742 + }, + { + "from": "US_SURVEY_ACRE", + "input": 42.42, + "to": "THOUSAND_SQ_FT", + "expect": 1847.8225912829741 + }, + { + "from": "US_SURVEY_ACRE", + "input": 42.42, + "to": "SQ_YRD", + "expect": 205313.62125366376 + }, + { + "from": "US_SURVEY_ACRE", + "input": 42.42, + "to": "SQ_MILE", + "expect": 0.06628151512579537 + }, + { + "from": "US_SURVEY_ACRE", + "input": 42.42, + "to": "SQ_CHAIN", + "expect": 424.2016968050905 + }, + { + "from": "US_SURVEY_ACRE", + "input": 42.42, + "to": "ACRE", + "expect": 42.420169680509055 + }, + { + "from": "US_SURVEY_ACRE", + "input": 42.42, + "to": "SQ_US_SURVEY_IN", + "expect": 266085388.8 + }, + { + "from": "US_SURVEY_ACRE", + "input": 42.42, + "to": "SQ_US_SURVEY_FT", + "expect": 1847815.2000000002 + }, + { + "from": "US_SURVEY_ACRE", + "input": 42.42, + "to": "SQ_US_SURVEY_YRD", + "expect": 205312.80000000002 + }, + { + "from": "US_SURVEY_ACRE", + "input": 42.42, + "to": "SQ_US_SURVEY_MILE", + "expect": 0.06628125 + }, + { + "from": "US_SURVEY_ACRE", + "input": 42.42, + "to": "SQ_US_SURVEY_CHAIN", + "expect": 424.20000000000005 + }, + { + "from": "US_SURVEY_ACRE", + "input": 42.42, + "to": "US_SURVEY_ACRE", + "expect": 42.42 + }, + { + "from": "MM_TO_THE_FOURTH", + "input": 42.42, + "to": "MM_TO_THE_FOURTH", + "expect": 42.42 + }, + { + "from": "MM_TO_THE_FOURTH", + "input": 42.42, + "to": "M_TO_THE_FOURTH", + "expect": 4.242e-11 + }, + { + "from": "MM_TO_THE_FOURTH", + "input": 42.42, + "to": "CM_TO_THE_FOURTH", + "expect": 0.004242 + }, + { + "from": "MM_TO_THE_FOURTH", + "input": 42.42, + "to": "IN_TO_THE_FOURTH", + "expect": 0.00010191445765742299 + }, + { + "from": "MM_TO_THE_FOURTH", + "input": 42.42, + "to": "FT_TO_THE_FOURTH", + "expect": 4.914856175608747e-9 + }, + { + "from": "M_TO_THE_FOURTH", + "input": 42.42, + "to": "MM_TO_THE_FOURTH", + "expect": 42420000000000 + }, + { + "from": "M_TO_THE_FOURTH", + "input": 42.42, + "to": "M_TO_THE_FOURTH", + "expect": 42.42 + }, + { + "from": "M_TO_THE_FOURTH", + "input": 42.42, + "to": "CM_TO_THE_FOURTH", + "expect": 4242000000 + }, + { + "from": "M_TO_THE_FOURTH", + "input": 42.42, + "to": "IN_TO_THE_FOURTH", + "expect": 101914457.65742299 + }, + { + "from": "M_TO_THE_FOURTH", + "input": 42.42, + "to": "FT_TO_THE_FOURTH", + "expect": 4914.856175608747 + }, + { + "from": "CM_TO_THE_FOURTH", + "input": 42.42, + "to": "MM_TO_THE_FOURTH", + "expect": 424200 + }, + { + "from": "CM_TO_THE_FOURTH", + "input": 42.42, + "to": "M_TO_THE_FOURTH", + "expect": 4.242e-7 + }, + { + "from": "CM_TO_THE_FOURTH", + "input": 42.42, + "to": "CM_TO_THE_FOURTH", + "expect": 42.42 + }, + { + "from": "CM_TO_THE_FOURTH", + "input": 42.42, + "to": "IN_TO_THE_FOURTH", + "expect": 1.0191445765742297 + }, + { + "from": "CM_TO_THE_FOURTH", + "input": 42.42, + "to": "FT_TO_THE_FOURTH", + "expect": 0.00004914856175608747 + }, + { + "from": "IN_TO_THE_FOURTH", + "input": 42.42, + "to": "MM_TO_THE_FOURTH", + "expect": 17656537.073952 + }, + { + "from": "IN_TO_THE_FOURTH", + "input": 42.42, + "to": "M_TO_THE_FOURTH", + "expect": 0.000017656537073951998 + }, + { + "from": "IN_TO_THE_FOURTH", + "input": 42.42, + "to": "CM_TO_THE_FOURTH", + "expect": 1765.6537073952 + }, + { + "from": "IN_TO_THE_FOURTH", + "input": 42.42, + "to": "IN_TO_THE_FOURTH", + "expect": 42.42 + }, + { + "from": "IN_TO_THE_FOURTH", + "input": 42.42, + "to": "FT_TO_THE_FOURTH", + "expect": 0.0020457175925925925 + }, + { + "from": "FT_TO_THE_FOURTH", + "input": 42.42, + "to": "MM_TO_THE_FOURTH", + "expect": 366125952765.4687 + }, + { + "from": "FT_TO_THE_FOURTH", + "input": 42.42, + "to": "M_TO_THE_FOURTH", + "expect": 0.3661259527654686 + }, + { + "from": "FT_TO_THE_FOURTH", + "input": 42.42, + "to": "CM_TO_THE_FOURTH", + "expect": 36612595.27654686 + }, + { + "from": "FT_TO_THE_FOURTH", + "input": 42.42, + "to": "IN_TO_THE_FOURTH", + "expect": 879621.12 + }, + { + "from": "FT_TO_THE_FOURTH", + "input": 42.42, + "to": "FT_TO_THE_FOURTH", + "expect": 42.42 + }, + { + "from": "PERSON", + "input": 42.42, + "to": "PERSON", + "expect": 42.42 + }, + { + "from": "PERSON", + "input": 42.42, + "to": "HUNDRED_PERSON", + "expect": 0.4242 + }, + { + "from": "PERSON", + "input": 42.42, + "to": "THOUSAND_PERSON", + "expect": 0.04242 + }, + { + "from": "HUNDRED_PERSON", + "input": 42.42, + "to": "PERSON", + "expect": 4242 + }, + { + "from": "HUNDRED_PERSON", + "input": 42.42, + "to": "HUNDRED_PERSON", + "expect": 42.42 + }, + { + "from": "HUNDRED_PERSON", + "input": 42.42, + "to": "THOUSAND_PERSON", + "expect": 4.242 + }, + { + "from": "THOUSAND_PERSON", + "input": 42.42, + "to": "PERSON", + "expect": 42420 + }, + { + "from": "THOUSAND_PERSON", + "input": 42.42, + "to": "HUNDRED_PERSON", + "expect": 424.20000000000005 + }, + { + "from": "THOUSAND_PERSON", + "input": 42.42, + "to": "THOUSAND_PERSON", + "expect": 42.42 + }, + { + "from": "US_DOLLAR", + "input": 42.42, + "to": "US_DOLLAR", + "expect": 42.42 + }, + { + "from": "A", + "input": 42.42, + "to": "A", + "expect": 42.42 + }, + { + "from": "A", + "input": 42.42, + "to": "KILOAMPERE", + "expect": 0.04242 + }, + { + "from": "A", + "input": 42.42, + "to": "MILLIAMPERE", + "expect": 42420 + }, + { + "from": "A", + "input": 42.42, + "to": "MICROAMPERE", + "expect": 42420000 + }, + { + "from": "KILOAMPERE", + "input": 42.42, + "to": "A", + "expect": 42420 + }, + { + "from": "KILOAMPERE", + "input": 42.42, + "to": "KILOAMPERE", + "expect": 42.42 + }, + { + "from": "KILOAMPERE", + "input": 42.42, + "to": "MILLIAMPERE", + "expect": 42420000 + }, + { + "from": "KILOAMPERE", + "input": 42.42, + "to": "MICROAMPERE", + "expect": 42420000000 + }, + { + "from": "MILLIAMPERE", + "input": 42.42, + "to": "A", + "expect": 0.04242 + }, + { + "from": "MILLIAMPERE", + "input": 42.42, + "to": "KILOAMPERE", + "expect": 0.00004242 + }, + { + "from": "MILLIAMPERE", + "input": 42.42, + "to": "MILLIAMPERE", + "expect": 42.42 + }, + { + "from": "MILLIAMPERE", + "input": 42.42, + "to": "MICROAMPERE", + "expect": 42420.00000000001 + }, + { + "from": "MICROAMPERE", + "input": 42.42, + "to": "A", + "expect": 0.00004242 + }, + { + "from": "MICROAMPERE", + "input": 42.42, + "to": "KILOAMPERE", + "expect": 4.242e-8 + }, + { + "from": "MICROAMPERE", + "input": 42.42, + "to": "MILLIAMPERE", + "expect": 0.04242 + }, + { + "from": "MICROAMPERE", + "input": 42.42, + "to": "MICROAMPERE", + "expect": 42.42 + }, + { + "from": "KG_PER_CUB_M", + "input": 42.42, + "to": "KG_PER_CUB_M", + "expect": 42.42 + }, + { + "from": "KG_PER_CUB_M", + "input": 42.42, + "to": "KG_PER_CUB_CM", + "expect": 0.00004242000000000001 + }, + { + "from": "KG_PER_CUB_M", + "input": 42.42, + "to": "KG_PER_LITRE", + "expect": 0.04242000000000001 + }, + { + "from": "KG_PER_CUB_M", + "input": 42.42, + "to": "G_PER_CUB_CM", + "expect": 0.04242000000000001 + }, + { + "from": "KG_PER_CUB_M", + "input": 42.42, + "to": "MKG_PER_LITRE", + "expect": 42420000.000000015 + }, + { + "from": "KG_PER_CUB_M", + "input": 42.42, + "to": "MG_PER_LITRE", + "expect": 42420.000000000015 + }, + { + "from": "KG_PER_CUB_M", + "input": 42.42, + "to": "LBM_PER_CUB_FT", + "expect": 2.648194087640054 + }, + { + "from": "KG_PER_CUB_M", + "input": 42.42, + "to": "LBM_PER_GALLON", + "expect": 0.35401205685466003 + }, + { + "from": "KG_PER_CUB_M", + "input": 42.42, + "to": "LBM_PER_GALLON_IMPERIAL", + "expect": 0.42515075330742463 + }, + { + "from": "KG_PER_CUB_M", + "input": 42.42, + "to": "LBM_PER_CUB_IN", + "expect": 0.00153251972664355 + }, + { + "from": "KG_PER_CUB_M", + "input": 42.42, + "to": "LBM_PER_MILLION_GALLON", + "expect": 354012.05685466004 + }, + { + "from": "KG_PER_CUB_M", + "input": 42.42, + "to": "SLUG_PER_CUB_FT", + "expect": 0.08230838848257951 + }, + { + "from": "KG_PER_CUB_M", + "input": 42.42, + "to": "KIP_PER_CUB_FT", + "expect": 0.002648194087640054 + }, + { + "from": "KG_PER_CUB_M", + "input": 42.42, + "to": "SHORT_TON_PER_CUB_FT", + "expect": 0.001324097043820027 + }, + { + "from": "KG_PER_CUB_CM", + "input": 42.42, + "to": "KG_PER_CUB_M", + "expect": 42420000 + }, + { + "from": "KG_PER_CUB_CM", + "input": 42.42, + "to": "KG_PER_CUB_CM", + "expect": 42.42 + }, + { + "from": "KG_PER_CUB_CM", + "input": 42.42, + "to": "KG_PER_LITRE", + "expect": 42420.00000000001 + }, + { + "from": "KG_PER_CUB_CM", + "input": 42.42, + "to": "G_PER_CUB_CM", + "expect": 42420 + }, + { + "from": "KG_PER_CUB_CM", + "input": 42.42, + "to": "MKG_PER_LITRE", + "expect": 42420000000000.01 + }, + { + "from": "KG_PER_CUB_CM", + "input": 42.42, + "to": "MG_PER_LITRE", + "expect": 42420000000.00001 + }, + { + "from": "KG_PER_CUB_CM", + "input": 42.42, + "to": "LBM_PER_CUB_FT", + "expect": 2648194.087640054 + }, + { + "from": "KG_PER_CUB_CM", + "input": 42.42, + "to": "LBM_PER_GALLON", + "expect": 354012.05685466 + }, + { + "from": "KG_PER_CUB_CM", + "input": 42.42, + "to": "LBM_PER_GALLON_IMPERIAL", + "expect": 425150.75330742454 + }, + { + "from": "KG_PER_CUB_CM", + "input": 42.42, + "to": "LBM_PER_CUB_IN", + "expect": 1532.51972664355 + }, + { + "from": "KG_PER_CUB_CM", + "input": 42.42, + "to": "LBM_PER_MILLION_GALLON", + "expect": 354012056854.66 + }, + { + "from": "KG_PER_CUB_CM", + "input": 42.42, + "to": "SLUG_PER_CUB_FT", + "expect": 82308.38848257951 + }, + { + "from": "KG_PER_CUB_CM", + "input": 42.42, + "to": "KIP_PER_CUB_FT", + "expect": 2648.1940876400536 + }, + { + "from": "KG_PER_CUB_CM", + "input": 42.42, + "to": "SHORT_TON_PER_CUB_FT", + "expect": 1324.0970438200268 + }, + { + "from": "KG_PER_LITRE", + "input": 42.42, + "to": "KG_PER_CUB_M", + "expect": 42419.99999999999 + }, + { + "from": "KG_PER_LITRE", + "input": 42.42, + "to": "KG_PER_CUB_CM", + "expect": 0.04242 + }, + { + "from": "KG_PER_LITRE", + "input": 42.42, + "to": "KG_PER_LITRE", + "expect": 42.42 + }, + { + "from": "KG_PER_LITRE", + "input": 42.42, + "to": "G_PER_CUB_CM", + "expect": 42.42 + }, + { + "from": "KG_PER_LITRE", + "input": 42.42, + "to": "MKG_PER_LITRE", + "expect": 42420000000 + }, + { + "from": "KG_PER_LITRE", + "input": 42.42, + "to": "MG_PER_LITRE", + "expect": 42420000 + }, + { + "from": "KG_PER_LITRE", + "input": 42.42, + "to": "LBM_PER_CUB_FT", + "expect": 2648.1940876400536 + }, + { + "from": "KG_PER_LITRE", + "input": 42.42, + "to": "LBM_PER_GALLON", + "expect": 354.01205685465993 + }, + { + "from": "KG_PER_LITRE", + "input": 42.42, + "to": "LBM_PER_GALLON_IMPERIAL", + "expect": 425.1507533074245 + }, + { + "from": "KG_PER_LITRE", + "input": 42.42, + "to": "LBM_PER_CUB_IN", + "expect": 1.5325197266435495 + }, + { + "from": "KG_PER_LITRE", + "input": 42.42, + "to": "LBM_PER_MILLION_GALLON", + "expect": 354012056.85466 + }, + { + "from": "KG_PER_LITRE", + "input": 42.42, + "to": "SLUG_PER_CUB_FT", + "expect": 82.3083884825795 + }, + { + "from": "KG_PER_LITRE", + "input": 42.42, + "to": "KIP_PER_CUB_FT", + "expect": 2.648194087640054 + }, + { + "from": "KG_PER_LITRE", + "input": 42.42, + "to": "SHORT_TON_PER_CUB_FT", + "expect": 1.324097043820027 + }, + { + "from": "G_PER_CUB_CM", + "input": 42.42, + "to": "KG_PER_CUB_M", + "expect": 42420 + }, + { + "from": "G_PER_CUB_CM", + "input": 42.42, + "to": "KG_PER_CUB_CM", + "expect": 0.04242 + }, + { + "from": "G_PER_CUB_CM", + "input": 42.42, + "to": "KG_PER_LITRE", + "expect": 42.42000000000001 + }, + { + "from": "G_PER_CUB_CM", + "input": 42.42, + "to": "G_PER_CUB_CM", + "expect": 42.42 + }, + { + "from": "G_PER_CUB_CM", + "input": 42.42, + "to": "MKG_PER_LITRE", + "expect": 42420000000.00001 + }, + { + "from": "G_PER_CUB_CM", + "input": 42.42, + "to": "MG_PER_LITRE", + "expect": 42420000.00000001 + }, + { + "from": "G_PER_CUB_CM", + "input": 42.42, + "to": "LBM_PER_CUB_FT", + "expect": 2648.1940876400536 + }, + { + "from": "G_PER_CUB_CM", + "input": 42.42, + "to": "LBM_PER_GALLON", + "expect": 354.01205685465993 + }, + { + "from": "G_PER_CUB_CM", + "input": 42.42, + "to": "LBM_PER_GALLON_IMPERIAL", + "expect": 425.15075330742457 + }, + { + "from": "G_PER_CUB_CM", + "input": 42.42, + "to": "LBM_PER_CUB_IN", + "expect": 1.5325197266435493 + }, + { + "from": "G_PER_CUB_CM", + "input": 42.42, + "to": "LBM_PER_MILLION_GALLON", + "expect": 354012056.85466 + }, + { + "from": "G_PER_CUB_CM", + "input": 42.42, + "to": "SLUG_PER_CUB_FT", + "expect": 82.30838848257952 + }, + { + "from": "G_PER_CUB_CM", + "input": 42.42, + "to": "KIP_PER_CUB_FT", + "expect": 2.6481940876400536 + }, + { + "from": "G_PER_CUB_CM", + "input": 42.42, + "to": "SHORT_TON_PER_CUB_FT", + "expect": 1.3240970438200268 + }, + { + "from": "MKG_PER_LITRE", + "input": 42.42, + "to": "KG_PER_CUB_M", + "expect": 0.00004241999999999999 + }, + { + "from": "MKG_PER_LITRE", + "input": 42.42, + "to": "KG_PER_CUB_CM", + "expect": 4.242e-11 + }, + { + "from": "MKG_PER_LITRE", + "input": 42.42, + "to": "KG_PER_LITRE", + "expect": 4.2420000000000005e-8 + }, + { + "from": "MKG_PER_LITRE", + "input": 42.42, + "to": "G_PER_CUB_CM", + "expect": 4.2420000000000005e-8 + }, + { + "from": "MKG_PER_LITRE", + "input": 42.42, + "to": "MKG_PER_LITRE", + "expect": 42.42 + }, + { + "from": "MKG_PER_LITRE", + "input": 42.42, + "to": "MG_PER_LITRE", + "expect": 0.04242 + }, + { + "from": "MKG_PER_LITRE", + "input": 42.42, + "to": "LBM_PER_CUB_FT", + "expect": 0.000002648194087640053 + }, + { + "from": "MKG_PER_LITRE", + "input": 42.42, + "to": "LBM_PER_GALLON", + "expect": 3.540120568546599e-7 + }, + { + "from": "MKG_PER_LITRE", + "input": 42.42, + "to": "LBM_PER_GALLON_IMPERIAL", + "expect": 4.251507533074245e-7 + }, + { + "from": "MKG_PER_LITRE", + "input": 42.42, + "to": "LBM_PER_CUB_IN", + "expect": 1.5325197266435495e-9 + }, + { + "from": "MKG_PER_LITRE", + "input": 42.42, + "to": "LBM_PER_MILLION_GALLON", + "expect": 0.35401205685466 + }, + { + "from": "MKG_PER_LITRE", + "input": 42.42, + "to": "SLUG_PER_CUB_FT", + "expect": 8.230838848257954e-8 + }, + { + "from": "MKG_PER_LITRE", + "input": 42.42, + "to": "KIP_PER_CUB_FT", + "expect": 2.6481940876400533e-9 + }, + { + "from": "MKG_PER_LITRE", + "input": 42.42, + "to": "SHORT_TON_PER_CUB_FT", + "expect": 1.3240970438200266e-9 + }, + { + "from": "MG_PER_LITRE", + "input": 42.42, + "to": "KG_PER_CUB_M", + "expect": 0.04241999999999999 + }, + { + "from": "MG_PER_LITRE", + "input": 42.42, + "to": "KG_PER_CUB_CM", + "expect": 4.2420000000000005e-8 + }, + { + "from": "MG_PER_LITRE", + "input": 42.42, + "to": "KG_PER_LITRE", + "expect": 0.00004242 + }, + { + "from": "MG_PER_LITRE", + "input": 42.42, + "to": "G_PER_CUB_CM", + "expect": 0.00004242 + }, + { + "from": "MG_PER_LITRE", + "input": 42.42, + "to": "MKG_PER_LITRE", + "expect": 42420.00000000001 + }, + { + "from": "MG_PER_LITRE", + "input": 42.42, + "to": "MG_PER_LITRE", + "expect": 42.42 + }, + { + "from": "MG_PER_LITRE", + "input": 42.42, + "to": "LBM_PER_CUB_FT", + "expect": 0.0026481940876400536 + }, + { + "from": "MG_PER_LITRE", + "input": 42.42, + "to": "LBM_PER_GALLON", + "expect": 0.00035401205685466 + }, + { + "from": "MG_PER_LITRE", + "input": 42.42, + "to": "LBM_PER_GALLON_IMPERIAL", + "expect": 0.0004251507533074245 + }, + { + "from": "MG_PER_LITRE", + "input": 42.42, + "to": "LBM_PER_CUB_IN", + "expect": 0.0000015325197266435495 + }, + { + "from": "MG_PER_LITRE", + "input": 42.42, + "to": "LBM_PER_MILLION_GALLON", + "expect": 354.01205685465993 + }, + { + "from": "MG_PER_LITRE", + "input": 42.42, + "to": "SLUG_PER_CUB_FT", + "expect": 0.0000823083884825795 + }, + { + "from": "MG_PER_LITRE", + "input": 42.42, + "to": "KIP_PER_CUB_FT", + "expect": 0.000002648194087640054 + }, + { + "from": "MG_PER_LITRE", + "input": 42.42, + "to": "SHORT_TON_PER_CUB_FT", + "expect": 0.000001324097043820027 + }, + { + "from": "LBM_PER_CUB_FT", + "input": 42.42, + "to": "KG_PER_CUB_M", + "expect": 679.5032163233892 + }, + { + "from": "LBM_PER_CUB_FT", + "input": 42.42, + "to": "KG_PER_CUB_CM", + "expect": 0.0006795032163233894 + }, + { + "from": "LBM_PER_CUB_FT", + "input": 42.42, + "to": "KG_PER_LITRE", + "expect": 0.6795032163233894 + }, + { + "from": "LBM_PER_CUB_FT", + "input": 42.42, + "to": "G_PER_CUB_CM", + "expect": 0.6795032163233894 + }, + { + "from": "LBM_PER_CUB_FT", + "input": 42.42, + "to": "MKG_PER_LITRE", + "expect": 679503216.3233894 + }, + { + "from": "LBM_PER_CUB_FT", + "input": 42.42, + "to": "MG_PER_LITRE", + "expect": 679503.2163233893 + }, + { + "from": "LBM_PER_CUB_FT", + "input": 42.42, + "to": "LBM_PER_CUB_FT", + "expect": 42.42 + }, + { + "from": "LBM_PER_CUB_FT", + "input": 42.42, + "to": "LBM_PER_GALLON", + "expect": 5.670729166666667 + }, + { + "from": "LBM_PER_CUB_FT", + "input": 42.42, + "to": "LBM_PER_GALLON_IMPERIAL", + "expect": 6.810261770266544 + }, + { + "from": "LBM_PER_CUB_FT", + "input": 42.42, + "to": "LBM_PER_CUB_IN", + "expect": 0.02454861111111111 + }, + { + "from": "LBM_PER_CUB_FT", + "input": 42.42, + "to": "LBM_PER_MILLION_GALLON", + "expect": 5670729.166666667 + }, + { + "from": "LBM_PER_CUB_FT", + "input": 42.42, + "to": "SLUG_PER_CUB_FT", + "expect": 1.3184539062778828 + }, + { + "from": "LBM_PER_CUB_FT", + "input": 42.42, + "to": "KIP_PER_CUB_FT", + "expect": 0.04242 + }, + { + "from": "LBM_PER_CUB_FT", + "input": 42.42, + "to": "SHORT_TON_PER_CUB_FT", + "expect": 0.02121 + }, + { + "from": "LBM_PER_GALLON", + "input": 42.42, + "to": "KG_PER_CUB_M", + "expect": 5083.037046782756 + }, + { + "from": "LBM_PER_GALLON", + "input": 42.42, + "to": "KG_PER_CUB_CM", + "expect": 0.005083037046782757 + }, + { + "from": "LBM_PER_GALLON", + "input": 42.42, + "to": "KG_PER_LITRE", + "expect": 5.083037046782757 + }, + { + "from": "LBM_PER_GALLON", + "input": 42.42, + "to": "G_PER_CUB_CM", + "expect": 5.083037046782756 + }, + { + "from": "LBM_PER_GALLON", + "input": 42.42, + "to": "MKG_PER_LITRE", + "expect": 5083037046.782757 + }, + { + "from": "LBM_PER_GALLON", + "input": 42.42, + "to": "MG_PER_LITRE", + "expect": 5083037.046782757 + }, + { + "from": "LBM_PER_GALLON", + "input": 42.42, + "to": "LBM_PER_CUB_FT", + "expect": 317.32363636363635 + }, + { + "from": "LBM_PER_GALLON", + "input": 42.42, + "to": "LBM_PER_GALLON", + "expect": 42.42 + }, + { + "from": "LBM_PER_GALLON", + "input": 42.42, + "to": "LBM_PER_GALLON_IMPERIAL", + "expect": 50.94429583991597 + }, + { + "from": "LBM_PER_GALLON", + "input": 42.42, + "to": "LBM_PER_CUB_IN", + "expect": 0.18363636363636365 + }, + { + "from": "LBM_PER_GALLON", + "input": 42.42, + "to": "LBM_PER_MILLION_GALLON", + "expect": 42420000 + }, + { + "from": "LBM_PER_GALLON", + "input": 42.42, + "to": "SLUG_PER_CUB_FT", + "expect": 9.862720130078708 + }, + { + "from": "LBM_PER_GALLON", + "input": 42.42, + "to": "KIP_PER_CUB_FT", + "expect": 0.31732363636363636 + }, + { + "from": "LBM_PER_GALLON", + "input": 42.42, + "to": "SHORT_TON_PER_CUB_FT", + "expect": 0.15866181818181818 + }, + { + "from": "LBM_PER_GALLON_IMPERIAL", + "input": 42.42, + "to": "KG_PER_CUB_M", + "expect": 4232.513728368773 + }, + { + "from": "LBM_PER_GALLON_IMPERIAL", + "input": 42.42, + "to": "KG_PER_CUB_CM", + "expect": 0.0042325137283687735 + }, + { + "from": "LBM_PER_GALLON_IMPERIAL", + "input": 42.42, + "to": "KG_PER_LITRE", + "expect": 4.232513728368774 + }, + { + "from": "LBM_PER_GALLON_IMPERIAL", + "input": 42.42, + "to": "G_PER_CUB_CM", + "expect": 4.232513728368774 + }, + { + "from": "LBM_PER_GALLON_IMPERIAL", + "input": 42.42, + "to": "MKG_PER_LITRE", + "expect": 4232513728.3687744 + }, + { + "from": "LBM_PER_GALLON_IMPERIAL", + "input": 42.42, + "to": "MG_PER_LITRE", + "expect": 4232513.728368774 + }, + { + "from": "LBM_PER_GALLON_IMPERIAL", + "input": 42.42, + "to": "LBM_PER_CUB_FT", + "expect": 264.2272001725966 + }, + { + "from": "LBM_PER_GALLON_IMPERIAL", + "input": 42.42, + "to": "LBM_PER_GALLON", + "expect": 35.3220389119617 + }, + { + "from": "LBM_PER_GALLON_IMPERIAL", + "input": 42.42, + "to": "LBM_PER_GALLON_IMPERIAL", + "expect": 42.42 + }, + { + "from": "LBM_PER_GALLON_IMPERIAL", + "input": 42.42, + "to": "LBM_PER_CUB_IN", + "expect": 0.15290925935914154 + }, + { + "from": "LBM_PER_GALLON_IMPERIAL", + "input": 42.42, + "to": "LBM_PER_MILLION_GALLON", + "expect": 35322038.9119617 + }, + { + "from": "LBM_PER_GALLON_IMPERIAL", + "input": 42.42, + "to": "SLUG_PER_CUB_FT", + "expect": 8.212432442537201 + }, + { + "from": "LBM_PER_GALLON_IMPERIAL", + "input": 42.42, + "to": "KIP_PER_CUB_FT", + "expect": 0.2642272001725966 + }, + { + "from": "LBM_PER_GALLON_IMPERIAL", + "input": 42.42, + "to": "SHORT_TON_PER_CUB_FT", + "expect": 0.1321136000862983 + }, + { + "from": "LBM_PER_CUB_IN", + "input": 42.42, + "to": "KG_PER_CUB_M", + "expect": 1174181.5578068167 + }, + { + "from": "LBM_PER_CUB_IN", + "input": 42.42, + "to": "KG_PER_CUB_CM", + "expect": 1.1741815578068169 + }, + { + "from": "LBM_PER_CUB_IN", + "input": 42.42, + "to": "KG_PER_LITRE", + "expect": 1174.181557806817 + }, + { + "from": "LBM_PER_CUB_IN", + "input": 42.42, + "to": "G_PER_CUB_CM", + "expect": 1174.181557806817 + }, + { + "from": "LBM_PER_CUB_IN", + "input": 42.42, + "to": "MKG_PER_LITRE", + "expect": 1174181557806.817 + }, + { + "from": "LBM_PER_CUB_IN", + "input": 42.42, + "to": "MG_PER_LITRE", + "expect": 1174181557.8068168 + }, + { + "from": "LBM_PER_CUB_IN", + "input": 42.42, + "to": "LBM_PER_CUB_FT", + "expect": 73301.76000000001 + }, + { + "from": "LBM_PER_CUB_IN", + "input": 42.42, + "to": "LBM_PER_GALLON", + "expect": 9799.02 + }, + { + "from": "LBM_PER_CUB_IN", + "input": 42.42, + "to": "LBM_PER_GALLON_IMPERIAL", + "expect": 11768.132339020587 + }, + { + "from": "LBM_PER_CUB_IN", + "input": 42.42, + "to": "LBM_PER_CUB_IN", + "expect": 42.42 + }, + { + "from": "LBM_PER_CUB_IN", + "input": 42.42, + "to": "LBM_PER_MILLION_GALLON", + "expect": 9799020000 + }, + { + "from": "LBM_PER_CUB_IN", + "input": 42.42, + "to": "SLUG_PER_CUB_FT", + "expect": 2278.2883500481817 + }, + { + "from": "LBM_PER_CUB_IN", + "input": 42.42, + "to": "KIP_PER_CUB_FT", + "expect": 73.30176 + }, + { + "from": "LBM_PER_CUB_IN", + "input": 42.42, + "to": "SHORT_TON_PER_CUB_FT", + "expect": 36.65088 + }, + { + "from": "LBM_PER_MILLION_GALLON", + "input": 42.42, + "to": "KG_PER_CUB_M", + "expect": 0.005083037046782756 + }, + { + "from": "LBM_PER_MILLION_GALLON", + "input": 42.42, + "to": "KG_PER_CUB_CM", + "expect": 5.0830370467827564e-9 + }, + { + "from": "LBM_PER_MILLION_GALLON", + "input": 42.42, + "to": "KG_PER_LITRE", + "expect": 0.000005083037046782757 + }, + { + "from": "LBM_PER_MILLION_GALLON", + "input": 42.42, + "to": "G_PER_CUB_CM", + "expect": 0.000005083037046782757 + }, + { + "from": "LBM_PER_MILLION_GALLON", + "input": 42.42, + "to": "MKG_PER_LITRE", + "expect": 5083.037046782758 + }, + { + "from": "LBM_PER_MILLION_GALLON", + "input": 42.42, + "to": "MG_PER_LITRE", + "expect": 5.083037046782756 + }, + { + "from": "LBM_PER_MILLION_GALLON", + "input": 42.42, + "to": "LBM_PER_CUB_FT", + "expect": 0.00031732363636363637 + }, + { + "from": "LBM_PER_MILLION_GALLON", + "input": 42.42, + "to": "LBM_PER_GALLON", + "expect": 0.00004242 + }, + { + "from": "LBM_PER_MILLION_GALLON", + "input": 42.42, + "to": "LBM_PER_GALLON_IMPERIAL", + "expect": 0.00005094429583991596 + }, + { + "from": "LBM_PER_MILLION_GALLON", + "input": 42.42, + "to": "LBM_PER_CUB_IN", + "expect": 1.8363636363636363e-7 + }, + { + "from": "LBM_PER_MILLION_GALLON", + "input": 42.42, + "to": "LBM_PER_MILLION_GALLON", + "expect": 42.42 + }, + { + "from": "LBM_PER_MILLION_GALLON", + "input": 42.42, + "to": "SLUG_PER_CUB_FT", + "expect": 0.000009862720130078708 + }, + { + "from": "LBM_PER_MILLION_GALLON", + "input": 42.42, + "to": "KIP_PER_CUB_FT", + "expect": 3.173236363636363e-7 + }, + { + "from": "LBM_PER_MILLION_GALLON", + "input": 42.42, + "to": "SHORT_TON_PER_CUB_FT", + "expect": 1.5866181818181816e-7 + }, + { + "from": "SLUG_PER_CUB_FT", + "input": 42.42, + "to": "KG_PER_CUB_M", + "expect": 21862.369476239386 + }, + { + "from": "SLUG_PER_CUB_FT", + "input": 42.42, + "to": "KG_PER_CUB_CM", + "expect": 0.02186236947623939 + }, + { + "from": "SLUG_PER_CUB_FT", + "input": 42.42, + "to": "KG_PER_LITRE", + "expect": 21.862369476239394 + }, + { + "from": "SLUG_PER_CUB_FT", + "input": 42.42, + "to": "G_PER_CUB_CM", + "expect": 21.862369476239383 + }, + { + "from": "SLUG_PER_CUB_FT", + "input": 42.42, + "to": "MKG_PER_LITRE", + "expect": 21862369476.23939 + }, + { + "from": "SLUG_PER_CUB_FT", + "input": 42.42, + "to": "MG_PER_LITRE", + "expect": 21862369.47623939 + }, + { + "from": "SLUG_PER_CUB_FT", + "input": 42.42, + "to": "LBM_PER_CUB_FT", + "expect": 1364.8231397637794 + }, + { + "from": "SLUG_PER_CUB_FT", + "input": 42.42, + "to": "LBM_PER_GALLON", + "expect": 182.45031555869969 + }, + { + "from": "SLUG_PER_CUB_FT", + "input": 42.42, + "to": "LBM_PER_GALLON_IMPERIAL", + "expect": 219.11369287855774 + }, + { + "from": "SLUG_PER_CUB_FT", + "input": 42.42, + "to": "LBM_PER_CUB_IN", + "expect": 0.7898282058818168 + }, + { + "from": "SLUG_PER_CUB_FT", + "input": 42.42, + "to": "LBM_PER_MILLION_GALLON", + "expect": 182450315.5586997 + }, + { + "from": "SLUG_PER_CUB_FT", + "input": 42.42, + "to": "SLUG_PER_CUB_FT", + "expect": 42.42 + }, + { + "from": "SLUG_PER_CUB_FT", + "input": 42.42, + "to": "KIP_PER_CUB_FT", + "expect": 1.3648231397637793 + }, + { + "from": "SLUG_PER_CUB_FT", + "input": 42.42, + "to": "SHORT_TON_PER_CUB_FT", + "expect": 0.6824115698818897 + }, + { + "from": "KIP_PER_CUB_FT", + "input": 42.42, + "to": "KG_PER_CUB_M", + "expect": 679503.2163233891 + }, + { + "from": "KIP_PER_CUB_FT", + "input": 42.42, + "to": "KG_PER_CUB_CM", + "expect": 0.6795032163233892 + }, + { + "from": "KIP_PER_CUB_FT", + "input": 42.42, + "to": "KG_PER_LITRE", + "expect": 679.5032163233892 + }, + { + "from": "KIP_PER_CUB_FT", + "input": 42.42, + "to": "G_PER_CUB_CM", + "expect": 679.5032163233894 + }, + { + "from": "KIP_PER_CUB_FT", + "input": 42.42, + "to": "MKG_PER_LITRE", + "expect": 679503216323.3894 + }, + { + "from": "KIP_PER_CUB_FT", + "input": 42.42, + "to": "MG_PER_LITRE", + "expect": 679503216.3233894 + }, + { + "from": "KIP_PER_CUB_FT", + "input": 42.42, + "to": "LBM_PER_CUB_FT", + "expect": 42420 + }, + { + "from": "KIP_PER_CUB_FT", + "input": 42.42, + "to": "LBM_PER_GALLON", + "expect": 5670.729166666668 + }, + { + "from": "KIP_PER_CUB_FT", + "input": 42.42, + "to": "LBM_PER_GALLON_IMPERIAL", + "expect": 6810.261770266543 + }, + { + "from": "KIP_PER_CUB_FT", + "input": 42.42, + "to": "LBM_PER_CUB_IN", + "expect": 24.548611111111114 + }, + { + "from": "KIP_PER_CUB_FT", + "input": 42.42, + "to": "LBM_PER_MILLION_GALLON", + "expect": 5670729166.666667 + }, + { + "from": "KIP_PER_CUB_FT", + "input": 42.42, + "to": "SLUG_PER_CUB_FT", + "expect": 1318.453906277883 + }, + { + "from": "KIP_PER_CUB_FT", + "input": 42.42, + "to": "KIP_PER_CUB_FT", + "expect": 42.42 + }, + { + "from": "KIP_PER_CUB_FT", + "input": 42.42, + "to": "SHORT_TON_PER_CUB_FT", + "expect": 21.21 + }, + { + "from": "SHORT_TON_PER_CUB_FT", + "input": 42.42, + "to": "KG_PER_CUB_M", + "expect": 1359006.4326467782 + }, + { + "from": "SHORT_TON_PER_CUB_FT", + "input": 42.42, + "to": "KG_PER_CUB_CM", + "expect": 1.3590064326467783 + }, + { + "from": "SHORT_TON_PER_CUB_FT", + "input": 42.42, + "to": "KG_PER_LITRE", + "expect": 1359.0064326467784 + }, + { + "from": "SHORT_TON_PER_CUB_FT", + "input": 42.42, + "to": "G_PER_CUB_CM", + "expect": 1359.0064326467789 + }, + { + "from": "SHORT_TON_PER_CUB_FT", + "input": 42.42, + "to": "MKG_PER_LITRE", + "expect": 1359006432646.7788 + }, + { + "from": "SHORT_TON_PER_CUB_FT", + "input": 42.42, + "to": "MG_PER_LITRE", + "expect": 1359006432.6467788 + }, + { + "from": "SHORT_TON_PER_CUB_FT", + "input": 42.42, + "to": "LBM_PER_CUB_FT", + "expect": 84840 + }, + { + "from": "SHORT_TON_PER_CUB_FT", + "input": 42.42, + "to": "LBM_PER_GALLON", + "expect": 11341.458333333336 + }, + { + "from": "SHORT_TON_PER_CUB_FT", + "input": 42.42, + "to": "LBM_PER_GALLON_IMPERIAL", + "expect": 13620.523540533086 + }, + { + "from": "SHORT_TON_PER_CUB_FT", + "input": 42.42, + "to": "LBM_PER_CUB_IN", + "expect": 49.09722222222223 + }, + { + "from": "SHORT_TON_PER_CUB_FT", + "input": 42.42, + "to": "LBM_PER_MILLION_GALLON", + "expect": 11341458333.333334 + }, + { + "from": "SHORT_TON_PER_CUB_FT", + "input": 42.42, + "to": "SLUG_PER_CUB_FT", + "expect": 2636.907812555766 + }, + { + "from": "SHORT_TON_PER_CUB_FT", + "input": 42.42, + "to": "KIP_PER_CUB_FT", + "expect": 84.84 + }, + { + "from": "SHORT_TON_PER_CUB_FT", + "input": 42.42, + "to": "SHORT_TON_PER_CUB_FT", + "expect": 42.42 + }, + { + "from": "PA_S", + "input": 42.42, + "to": "PA_S", + "expect": 42.42 + }, + { + "from": "PA_S", + "input": 42.42, + "to": "POISE", + "expect": 424.20000000000005 + }, + { + "from": "PA_S", + "input": 42.42, + "to": "CENTIPOISE", + "expect": 42420 + }, + { + "from": "PA_S", + "input": 42.42, + "to": "LBM_PER_FT_S", + "expect": 28.50492392541788 + }, + { + "from": "POISE", + "input": 42.42, + "to": "PA_S", + "expect": 4.242 + }, + { + "from": "POISE", + "input": 42.42, + "to": "POISE", + "expect": 42.42 + }, + { + "from": "POISE", + "input": 42.42, + "to": "CENTIPOISE", + "expect": 4242 + }, + { + "from": "POISE", + "input": 42.42, + "to": "LBM_PER_FT_S", + "expect": 2.850492392541788 + }, + { + "from": "CENTIPOISE", + "input": 42.42, + "to": "PA_S", + "expect": 0.04242 + }, + { + "from": "CENTIPOISE", + "input": 42.42, + "to": "POISE", + "expect": 0.4242 + }, + { + "from": "CENTIPOISE", + "input": 42.42, + "to": "CENTIPOISE", + "expect": 42.42 + }, + { + "from": "CENTIPOISE", + "input": 42.42, + "to": "LBM_PER_FT_S", + "expect": 0.02850492392541788 + }, + { + "from": "LBM_PER_FT_S", + "input": 42.42, + "to": "PA_S", + "expect": 63.12791448622048 + }, + { + "from": "LBM_PER_FT_S", + "input": 42.42, + "to": "POISE", + "expect": 631.2791448622047 + }, + { + "from": "LBM_PER_FT_S", + "input": 42.42, + "to": "CENTIPOISE", + "expect": 63127.91448622048 + }, + { + "from": "LBM_PER_FT_S", + "input": 42.42, + "to": "LBM_PER_FT_S", + "expect": 42.42 + }, + { + "from": "COULOMB", + "input": 42.42, + "to": "COULOMB", + "expect": 42.42 + }, + { + "from": "VOLT", + "input": 42.42, + "to": "VOLT", + "expect": 42.42 + }, + { + "from": "VOLT", + "input": 42.42, + "to": "KILOVOLT", + "expect": 0.04242 + }, + { + "from": "VOLT", + "input": 42.42, + "to": "MEGAVOLT", + "expect": 0.00004242 + }, + { + "from": "KILOVOLT", + "input": 42.42, + "to": "VOLT", + "expect": 42420 + }, + { + "from": "KILOVOLT", + "input": 42.42, + "to": "KILOVOLT", + "expect": 42.42 + }, + { + "from": "KILOVOLT", + "input": 42.42, + "to": "MEGAVOLT", + "expect": 0.04242 + }, + { + "from": "MEGAVOLT", + "input": 42.42, + "to": "VOLT", + "expect": 42420000 + }, + { + "from": "MEGAVOLT", + "input": 42.42, + "to": "KILOVOLT", + "expect": 42420 + }, + { + "from": "MEGAVOLT", + "input": 42.42, + "to": "MEGAVOLT", + "expect": 42.42 + }, + { + "from": "J_PER_CUB_M", + "input": 42.42, + "to": "J_PER_CUB_M", + "expect": 42.42 + }, + { + "from": "J_PER_CUB_M", + "input": 42.42, + "to": "KJ_PER_CUB_M", + "expect": 0.04242 + }, + { + "from": "J_PER_CUB_M", + "input": 42.42, + "to": "KWH_PER_CUB_M", + "expect": 0.000011783333333333335 + }, + { + "from": "J_PER_CUB_M", + "input": 42.42, + "to": "KWH_PER_CUB_FT", + "expect": 3.336668423423999e-7 + }, + { + "from": "J_PER_CUB_M", + "input": 42.42, + "to": "KWH_PER_MILLION_GALLON", + "expect": 0.0446047688548 + }, + { + "from": "KJ_PER_CUB_M", + "input": 42.42, + "to": "J_PER_CUB_M", + "expect": 42420 + }, + { + "from": "KJ_PER_CUB_M", + "input": 42.42, + "to": "KJ_PER_CUB_M", + "expect": 42.42 + }, + { + "from": "KJ_PER_CUB_M", + "input": 42.42, + "to": "KWH_PER_CUB_M", + "expect": 0.011783333333333333 + }, + { + "from": "KJ_PER_CUB_M", + "input": 42.42, + "to": "KWH_PER_CUB_FT", + "expect": 0.0003336668423424 + }, + { + "from": "KJ_PER_CUB_M", + "input": 42.42, + "to": "KWH_PER_MILLION_GALLON", + "expect": 44.60476885480001 + }, + { + "from": "KWH_PER_CUB_M", + "input": 42.42, + "to": "J_PER_CUB_M", + "expect": 152712000 + }, + { + "from": "KWH_PER_CUB_M", + "input": 42.42, + "to": "KJ_PER_CUB_M", + "expect": 152712 + }, + { + "from": "KWH_PER_CUB_M", + "input": 42.42, + "to": "KWH_PER_CUB_M", + "expect": 42.42 + }, + { + "from": "KWH_PER_CUB_M", + "input": 42.42, + "to": "KWH_PER_CUB_FT", + "expect": 1.2012006324326399 + }, + { + "from": "KWH_PER_CUB_M", + "input": 42.42, + "to": "KWH_PER_MILLION_GALLON", + "expect": 160577.16787728 + }, + { + "from": "KWH_PER_CUB_FT", + "input": 42.42, + "to": "J_PER_CUB_M", + "expect": 5392973384.371965 + }, + { + "from": "KWH_PER_CUB_FT", + "input": 42.42, + "to": "KJ_PER_CUB_M", + "expect": 5392973.384371966 + }, + { + "from": "KWH_PER_CUB_FT", + "input": 42.42, + "to": "KWH_PER_CUB_M", + "expect": 1498.0481623255462 + }, + { + "from": "KWH_PER_CUB_FT", + "input": 42.42, + "to": "KWH_PER_CUB_FT", + "expect": 42.42 + }, + { + "from": "KWH_PER_CUB_FT", + "input": 42.42, + "to": "KWH_PER_MILLION_GALLON", + "expect": 5670729.166666667 + }, + { + "from": "KWH_PER_MILLION_GALLON", + "input": 42.42, + "to": "J_PER_CUB_M", + "expect": 40342.24245971756 + }, + { + "from": "KWH_PER_MILLION_GALLON", + "input": 42.42, + "to": "KJ_PER_CUB_M", + "expect": 40.34224245971756 + }, + { + "from": "KWH_PER_MILLION_GALLON", + "input": 42.42, + "to": "KWH_PER_CUB_M", + "expect": 0.011206178461032655 + }, + { + "from": "KWH_PER_MILLION_GALLON", + "input": 42.42, + "to": "KWH_PER_CUB_FT", + "expect": 0.00031732363636363637 + }, + { + "from": "KWH_PER_MILLION_GALLON", + "input": 42.42, + "to": "KWH_PER_MILLION_GALLON", + "expect": 42.42 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 42.42 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 2545.2000000000003 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 152712 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 3665088 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 42419.99999999999 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 2545199.9999999995 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 152711999.99999997 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 3665087999.9999995 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 2588627.2244985434 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 155317633.46991262 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 1498.0481623255462 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 89882.88973953277 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 129431361.22492717 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 2971.335198001084 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 123.80563325004512 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 2.0634272208340856 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 35656.022376013 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 1485.6675990005415 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 24.76112665000903 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 9331.0955128473 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 559865.730770838 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 806206652.3100069 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 11206.178461032658 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 672370.7076619593 + }, + { + "from": "CUB_M_PER_SEC", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 968213819.0332216 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 0.7070000000000001 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 42.42 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 2545.2000000000003 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 61084.8 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 706.9999999999998 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 42419.99999999999 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 2545199.9999999995 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 61084799.99999999 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 43143.78707497573 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 2588627.2244985434 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 24.967469372092435 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 1498.0481623255462 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 2157189.3537487867 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 49.52225330001806 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 2.0634272208340856 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 0.03439045368056809 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 594.2670396002167 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 24.76112665000903 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 0.41268544416681713 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 155.51825854745502 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 9331.0955128473 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 13436777.538500112 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 186.76964101721094 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 11206.178461032658 + }, + { + "from": "CUB_M_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 16136896.983887026 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 0.011783333333333333 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 0.7070000000000001 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 42.42 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 1018.08 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 11.783333333333331 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 706.9999999999998 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 42419.99999999999 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 1018079.9999999998 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 719.0631179162621 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 43143.78707497573 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 0.4161244895348739 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 24.967469372092435 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 35953.155895813106 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 0.8253708883336343 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 0.03439045368056809 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 0.0005731742280094683 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 9.904450660003612 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 0.41268544416681713 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 0.00687809073611362 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 2.591970975790917 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 155.51825854745502 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 223946.29230833525 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 3.112827350286849 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 186.76964101721094 + }, + { + "from": "CUB_M_PER_HR", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 268948.2830647838 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 0.0004909722222222222 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 0.029458333333333336 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 1.7675 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 42.42 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 0.4909722222222221 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 29.45833333333333 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 1767.4999999999998 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 42419.99999999999 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 29.96096324651092 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 1797.6577947906553 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 0.01733852039728641 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 1.0403112238371848 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 1498.0481623255462 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 0.03439045368056809 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 0.0014329355700236702 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 0.00002388225950039451 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 0.41268544416681713 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 0.017195226840284045 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 0.00028658711400473413 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 0.10799879065795488 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 6.479927439477292 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 9331.0955128473 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 0.12970113959528537 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 7.782068375717123 + }, + { + "from": "CUB_M_PER_DAY", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 11206.178461032658 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 0.04242000000000001 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 2.545200000000001 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 152.71200000000002 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 3665.088000000001 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 42.42 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 2545.2000000000003 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 152712 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 3665088 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 2588.6272244985444 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 155317.63346991266 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 1.4980481623255462 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 89.88288973953279 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 129431.3612249272 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 2.971335198001084 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 0.12380563325004515 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 0.002063427220834086 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 35.656022376013006 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 1.485667599000542 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 0.024761126650009034 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 9.331095512847304 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 559.8657307708381 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 806206.652310007 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 11.20617846103266 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 672.3707076619595 + }, + { + "from": "LITRE_PER_SEC", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 968213.8190332217 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 0.0007070000000000002 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 0.04242000000000001 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 2.545200000000001 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 61.084800000000016 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 0.7070000000000001 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 42.42 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 2545.2000000000003 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 61084.8 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 43.14378707497575 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 2588.6272244985444 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 0.024967469372092443 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 1.4980481623255462 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 2157.1893537487867 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 0.04952225330001807 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 0.002063427220834086 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 0.0000343904536805681 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 0.5942670396002168 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 0.024761126650009034 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 0.0004126854441668172 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 0.15551825854745507 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 9.331095512847304 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 13436.777538500117 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 0.18676964101721097 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 11.20617846103266 + }, + { + "from": "LITRE_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 16136.896983887029 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 0.000011783333333333338 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 0.0007070000000000002 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 0.04242000000000001 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 1.0180800000000003 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 0.011783333333333333 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 0.7070000000000001 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 42.42 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 1018.08 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 0.7190631179162623 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 43.14378707497575 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 0.0004161244895348741 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 0.024967469372092443 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 35.95315589581312 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 0.0008253708883336344 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 0.0000343904536805681 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 5.731742280094682e-7 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 0.009904450660003613 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 0.0004126854441668172 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 0.000006878090736113619 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 0.0025919709757909176 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 0.15551825854745507 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 223.9462923083353 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 0.0031128273502868497 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 0.18676964101721097 + }, + { + "from": "LITRE_PER_HR", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 268.9482830647838 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 4.909722222222223e-7 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 0.00002945833333333334 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 0.0017675000000000006 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 0.04242000000000001 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 0.0004909722222222222 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 0.029458333333333336 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 1.7675 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 42.42 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 0.02996096324651093 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 1.7976577947906558 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 0.00001733852039728642 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 0.001040311223837185 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 1.4980481623255462 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 0.0000343904536805681 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 0.000001432935570023671 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 2.3882259500394516e-8 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 0.0004126854441668172 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 0.00001719522684028405 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 2.8658711400473416e-7 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 0.0001079987906579549 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 0.006479927439477294 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 9.331095512847304 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 0.00012970113959528541 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 0.007782068375717125 + }, + { + "from": "LITRE_PER_DAY", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 11.20617846103266 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 0.00069513925488 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 0.0417083552928 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 2.502501317568 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 60.06003162163199 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 0.6951392548799998 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 41.70835529279999 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 2502.5013175679996 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 60060.03162163198 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 42.42 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 2545.2000000000003 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 0.02454861111111111 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 1.4729166666666669 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 2121 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 0.048691460055096426 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 0.0020288108356290176 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 0.00003381351392715029 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 0.584297520661157 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 0.024345730027548213 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 0.0004057621671258035 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 0.15290925935914154 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 9.174555561548493 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 13211.360008629828 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 0.18363636363636365 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 11.018181818181818 + }, + { + "from": "CUB_IN_PER_SEC", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 15866.18181818182 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 0.000011585654248 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 0.00069513925488 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 0.0417083552928 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 1.0010005270272 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 0.011585654247999998 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 0.6951392548799998 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 41.70835529279999 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 1001.0005270271997 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 0.7070000000000001 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 42.42 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 0.00040914351851851854 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 0.02454861111111111 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 35.35 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 0.000811524334251607 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 0.00003381351392715029 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 5.635585654525048e-7 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 0.009738292011019284 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 0.0004057621671258035 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 0.000006762702785430058 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 0.0025484876559856923 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 0.15290925935914154 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 220.1893334771638 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 0.0030606060606060605 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 0.18363636363636365 + }, + { + "from": "CUB_IN_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 264.43636363636364 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 1.2012006324326399 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 72.0720379459584 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 4324.3222767575035 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 103783.73464218009 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 1201.2006324326396 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 72072.03794595838 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 4324322.276757503 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 103783734.64218006 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 73301.76000000001 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 4398105.600000001 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 42.42 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 2545.2000000000003 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 3665088 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 84.13884297520661 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 3.5057851239669424 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 0.058429752066115705 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 1009.6661157024794 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 42.06942148760331 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 0.7011570247933885 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 264.22720017259655 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 15853.632010355794 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 22829230.094912343 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 317.32363636363635 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 19039.418181818182 + }, + { + "from": "CUB_FT_PER_SEC", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 27416762.181818184 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 0.020020010540543996 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 1.2012006324326399 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 72.0720379459584 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 1729.7289107030015 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 20.020010540543993 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 1201.2006324326396 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 72072.03794595838 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 1729728.910703001 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 1221.6960000000001 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 73301.76000000001 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 0.7070000000000001 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 42.42 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 61084.8 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 1.402314049586777 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 0.058429752066115705 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 0.0009738292011019284 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 16.827768595041324 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 0.7011570247933885 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 0.011685950413223142 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 4.403786669543276 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 264.22720017259655 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 380487.1682485391 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 5.2887272727272725 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 317.32363636363635 + }, + { + "from": "CUB_FT_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 456946.0363636364 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 0.000013902785097599997 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 0.000834167105856 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 0.050050026351359994 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 1.2012006324326399 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 0.013902785097599995 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 0.8341671058559997 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 50.05002635135998 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 1201.2006324326396 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 0.8484 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 50.904 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 0.0004909722222222222 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 0.029458333333333336 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 42.42 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 0.0009738292011019284 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 0.000040576216712580355 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 6.76270278543006e-7 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 0.011685950413223142 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 0.0004869146005509642 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 0.00000811524334251607 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 0.00305818518718283 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 0.18349111123096984 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 264.22720017259655 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 0.003672727272727273 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 0.22036363636363637 + }, + { + "from": "CUB_FT_PER_DAY", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 317.32363636363635 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 0.605605318851456 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 36.33631913108736 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 2180.1791478652417 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 52324.2995487658 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 605.6053188514559 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 36336.31913108735 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 2180179.1478652414 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 52324299.548765786 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 36956.304 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 2217378.24 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 21.38675 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 1283.2050000000002 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 1847815.2000000002 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 42.42 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 1.7675 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 0.029458333333333336 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 509.04 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 21.21 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 0.35350000000000004 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 133.21454675368412 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 7992.872805221047 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 11509736.839518307 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 159.984 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 9599.04 + }, + { + "from": "ACRE_FT_PER_DAY", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 13822617.600000001 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 14.534527652434944 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 872.0716591460966 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 52324.2995487658 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 1255783.1891703792 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 14534.527652434941 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 872071.6591460964 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 52324299.548765786 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 1255783189.170379 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 886951.296 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 53217077.760000005 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 513.282 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 30796.920000000002 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 44347564.800000004 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 1018.08 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 42.42 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 0.7070000000000001 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 12216.960000000001 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 509.04 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 8.484 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 3197.1491220884186 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 191828.9473253051 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 276233684.14843935 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 3839.616 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 230376.96000000002 + }, + { + "from": "ACRE_FT_PER_HR", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 331742822.4 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 872.0716591460966 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 52324.2995487658 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 3139457.972925948 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 75346991.35022275 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 872071.6591460964 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 52324299.548765786 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 3139457972.9259477 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 75346991350.22275 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 53217077.760000005 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 3193024665.6 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 30796.920000000002 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 1847815.2000000002 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 2660853888 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 61084.8 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 2545.2000000000003 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 42.42 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 733017.6 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 30542.4 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 509.04 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 191828.9473253051 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 11509736.839518307 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 16574021048.906363 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 230376.96000000002 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 13822617.600000001 + }, + { + "from": "ACRE_FT_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 19904569344 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 0.050467109904288 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 3.02802659425728 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 181.6815956554368 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 4360.358295730483 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 50.46710990428799 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 3028.026594257279 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 181681.59565543677 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 4360358.295730483 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 3079.6920000000005 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 184781.52000000002 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 1.7822291666666665 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 106.93375000000002 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 153984.6 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 3.535 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 0.14729166666666665 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 0.0024548611111111112 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 42.42 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 1.7675 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 0.029458333333333336 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 11.101212229473676 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 666.0727337684204 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 959144.7366265256 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 13.332000000000003 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 799.9200000000001 + }, + { + "from": "ACRE_IN_PER_DAY", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 1151884.8 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 1.211210637702912 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 72.67263826217471 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 4360.358295730483 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 104648.5990975316 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 1211.2106377029118 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 72672.6382621747 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 4360358.295730483 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 104648599.09753157 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 73912.60800000001 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 4434756.48 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 42.7735 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 2566.4100000000003 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 3695630.4000000004 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 84.84 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 3.535 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 0.05891666666666666 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 1018.08 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 42.42 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 0.7070000000000001 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 266.42909350736824 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 15985.745610442094 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 23019473.679036614 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 319.968 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 19198.08 + }, + { + "from": "ACRE_IN_PER_HR", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 27645235.200000003 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 72.67263826217471 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 4360.358295730483 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 261621.497743829 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 6278915.945851896 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 72672.6382621747 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 4360358.295730483 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 261621497.74382895 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 6278915945.851895 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 4434756.48 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 266085388.8 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 2566.4100000000003 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 153984.6 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 221737824 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 5090.400000000001 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 212.10000000000002 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 3.535 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 61084.8 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 2545.2000000000003 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 42.42 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 15985.745610442094 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 959144.7366265256 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 1381168420.742197 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 19198.08 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 1151884.8 + }, + { + "from": "ACRE_IN_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 1658714112 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 0.1928451378000001 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 11.570708268000004 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 694.2424960800004 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 16661.819905920005 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 192.84513780000003 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 11570.708268 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 694242.49608 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 16661819.905920003 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 11768.132339020589 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 706087.9403412353 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 6.810261770266544 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 408.6157062159927 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 588406.6169510294 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 13.507957230280747 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 0.5628315512616979 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 0.009380525854361632 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 162.09548676336897 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 6.753978615140376 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 0.1125663102523396 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 42.42 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 2545.2000000000003 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 3665088 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 50.94429583991597 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 3056.6577503949584 + }, + { + "from": "GALLON_IMPERIAL_PER_SEC", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 4401587.160568739 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 0.0032140856300000016 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 0.1928451378000001 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 11.570708268000004 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 277.6969984320001 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 3.2140856300000005 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 192.84513780000003 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 11570.708268 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 277696.99843200005 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 196.1355389836765 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 11768.132339020589 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 0.11350436283777575 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 6.810261770266544 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 9806.776949183823 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 0.2251326205046791 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 0.009380525854361632 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 0.00015634209757269384 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 2.70159144605615 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 0.1125663102523396 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 0.0018761051708723261 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 0.7070000000000001 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 42.42 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 61084.8 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 0.8490715973319328 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 50.94429583991597 + }, + { + "from": "GALLON_IMPERIAL_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 73359.786009479 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 0.000002232003909722223 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 0.00013392023458333338 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 0.008035214075000003 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 0.1928451378000001 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 0.0022320039097222222 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 0.13392023458333335 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 8.035214075 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 192.84513780000003 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 0.1362052354053309 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 8.172314124319852 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 0.00007882247419289982 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 0.004729348451573989 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 6.810261770266544 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 0.00015634209757269384 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 0.00000651425406552891 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 1.0857090109214851e-7 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 0.0018761051708723261 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 0.00007817104878634692 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 0.0000013028508131057817 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 0.0004909722222222222 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 0.029458333333333336 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 42.42 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 0.0005896330537027311 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 0.035377983222163865 + }, + { + "from": "GALLON_IMPERIAL_PER_DAY", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 50.94429583991597 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 0.16057716787728 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 9.6346300726368 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 578.077804358208 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 13873.867304596992 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 160.57716787727998 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 9634.630072636797 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 578077.8043582079 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 13873867.304596988 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 9799.02 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 587941.2000000001 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 5.670729166666667 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 340.24375000000003 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 489951 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 11.247727272727275 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 0.46865530303030306 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 0.007810921717171716 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 134.9727272727273 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 5.6238636363636365 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 0.09373106060606061 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 35.32203891196169 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 2119.322334717702 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 3051824.1619934905 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 42.42 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 2545.2000000000003 + }, + { + "from": "GALLON_PER_SEC", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 3665088 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 0.0026762861312879995 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 0.16057716787728 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 9.6346300726368 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 231.23112174328318 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 2.6762861312879993 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 160.57716787727998 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 9634.630072636797 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 231231.12174328315 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 163.317 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 9799.02 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 0.09451215277777779 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 5.670729166666667 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 8165.85 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 0.18746212121212122 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 0.007810921717171716 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 0.00013018202861952863 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 2.2495454545454545 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 0.09373106060606061 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 0.0015621843434343433 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 0.5887006485326948 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 35.32203891196169 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 50863.73603322484 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 0.7070000000000001 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 42.42 + }, + { + "from": "GALLON_PER_MIN", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 61084.8 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_SEC", + "expect": 0.0000018585320356166666 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_MIN", + "expect": 0.00011151192213699999 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_HR", + "expect": 0.00669071532822 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "CUB_M_PER_DAY", + "expect": 0.16057716787728 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_SEC", + "expect": 0.0018585320356166663 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_MIN", + "expect": 0.11151192213699997 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_HR", + "expect": 6.690715328219998 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "LITRE_PER_DAY", + "expect": 160.57716787727998 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "CUB_IN_PER_SEC", + "expect": 0.11341458333333333 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "CUB_IN_PER_MIN", + "expect": 6.804875000000001 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "CUB_FT_PER_SEC", + "expect": 0.00006563343942901234 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "CUB_FT_PER_MIN", + "expect": 0.003938006365740741 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "CUB_FT_PER_DAY", + "expect": 5.670729166666667 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "ACRE_FT_PER_DAY", + "expect": 0.00013018202861952863 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "ACRE_FT_PER_HR", + "expect": 0.000005424251192480359 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "ACRE_FT_PER_MIN", + "expect": 9.040418654133933e-8 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "ACRE_IN_PER_DAY", + "expect": 0.0015621843434343433 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "ACRE_IN_PER_HR", + "expect": 0.0000650910143097643 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "ACRE_IN_PER_MIN", + "expect": 0.0000010848502384960721 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_SEC", + "expect": 0.00040881989481437146 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_MIN", + "expect": 0.024529193688862288 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "GALLON_IMPERIAL_PER_DAY", + "expect": 35.32203891196169 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "GALLON_PER_SEC", + "expect": 0.0004909722222222222 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "GALLON_PER_MIN", + "expect": 0.029458333333333336 + }, + { + "from": "GALLON_PER_DAY", + "input": 42.42, + "to": "GALLON_PER_DAY", + "expect": 42.42 + }, + { + "from": "N", + "input": 42.42, + "to": "N", + "expect": 42.42 + }, + { + "from": "N", + "input": 42.42, + "to": "KN", + "expect": 0.04242 + }, + { + "from": "N", + "input": 42.42, + "to": "MN", + "expect": 42420 + }, + { + "from": "N", + "input": 42.42, + "to": "KGF", + "expect": 4.325636175452372 + }, + { + "from": "N", + "input": 42.42, + "to": "DYNE", + "expect": 4242000 + }, + { + "from": "N", + "input": 42.42, + "to": "PDL", + "expect": 306.8244475683237 + }, + { + "from": "N", + "input": 42.42, + "to": "SHORT_TON_FORCE", + "expect": 0.00476819768314486 + }, + { + "from": "N", + "input": 42.42, + "to": "LONG_TON_FORCE", + "expect": 0.004257319359950767 + }, + { + "from": "N", + "input": 42.42, + "to": "LBF", + "expect": 9.53639536628972 + }, + { + "from": "N", + "input": 42.42, + "to": "OZF", + "expect": 152.5823258606355 + }, + { + "from": "N", + "input": 42.42, + "to": "KPF", + "expect": 0.009536395366289719 + }, + { + "from": "KN", + "input": 42.42, + "to": "N", + "expect": 42420 + }, + { + "from": "KN", + "input": 42.42, + "to": "KN", + "expect": 42.42 + }, + { + "from": "KN", + "input": 42.42, + "to": "MN", + "expect": 42420000 + }, + { + "from": "KN", + "input": 42.42, + "to": "KGF", + "expect": 4325.636175452372 + }, + { + "from": "KN", + "input": 42.42, + "to": "DYNE", + "expect": 4242000000 + }, + { + "from": "KN", + "input": 42.42, + "to": "PDL", + "expect": 306824.4475683237 + }, + { + "from": "KN", + "input": 42.42, + "to": "SHORT_TON_FORCE", + "expect": 4.76819768314486 + }, + { + "from": "KN", + "input": 42.42, + "to": "LONG_TON_FORCE", + "expect": 4.257319359950768 + }, + { + "from": "KN", + "input": 42.42, + "to": "LBF", + "expect": 9536.39536628972 + }, + { + "from": "KN", + "input": 42.42, + "to": "OZF", + "expect": 152582.32586063552 + }, + { + "from": "KN", + "input": 42.42, + "to": "KPF", + "expect": 9.53639536628972 + }, + { + "from": "MN", + "input": 42.42, + "to": "N", + "expect": 0.04242 + }, + { + "from": "MN", + "input": 42.42, + "to": "KN", + "expect": 0.00004242 + }, + { + "from": "MN", + "input": 42.42, + "to": "MN", + "expect": 42.42 + }, + { + "from": "MN", + "input": 42.42, + "to": "KGF", + "expect": 0.004325636175452372 + }, + { + "from": "MN", + "input": 42.42, + "to": "DYNE", + "expect": 4242 + }, + { + "from": "MN", + "input": 42.42, + "to": "PDL", + "expect": 0.3068244475683237 + }, + { + "from": "MN", + "input": 42.42, + "to": "SHORT_TON_FORCE", + "expect": 0.000004768197683144859 + }, + { + "from": "MN", + "input": 42.42, + "to": "LONG_TON_FORCE", + "expect": 0.000004257319359950767 + }, + { + "from": "MN", + "input": 42.42, + "to": "LBF", + "expect": 0.00953639536628972 + }, + { + "from": "MN", + "input": 42.42, + "to": "OZF", + "expect": 0.15258232586063553 + }, + { + "from": "MN", + "input": 42.42, + "to": "KPF", + "expect": 0.00000953639536628972 + }, + { + "from": "KGF", + "input": 42.42, + "to": "N", + "expect": 415.998093 + }, + { + "from": "KGF", + "input": 42.42, + "to": "KN", + "expect": 0.415998093 + }, + { + "from": "KGF", + "input": 42.42, + "to": "MN", + "expect": 415998.093 + }, + { + "from": "KGF", + "input": 42.42, + "to": "KGF", + "expect": 42.42 + }, + { + "from": "KGF", + "input": 42.42, + "to": "DYNE", + "expect": 41599809.300000004 + }, + { + "from": "KGF", + "input": 42.42, + "to": "PDL", + "expect": 3008.9199687459018 + }, + { + "from": "KGF", + "input": 42.42, + "to": "SHORT_TON_FORCE", + "expect": 0.04676004580941254 + }, + { + "from": "KGF", + "input": 42.42, + "to": "LONG_TON_FORCE", + "expect": 0.04175004090126119 + }, + { + "from": "KGF", + "input": 42.42, + "to": "LBF", + "expect": 93.52009161882506 + }, + { + "from": "KGF", + "input": 42.42, + "to": "OZF", + "expect": 1496.321465901201 + }, + { + "from": "KGF", + "input": 42.42, + "to": "KPF", + "expect": 0.09352009161882507 + }, + { + "from": "DYNE", + "input": 42.42, + "to": "N", + "expect": 0.00042420000000000007 + }, + { + "from": "DYNE", + "input": 42.42, + "to": "KN", + "expect": 4.242e-7 + }, + { + "from": "DYNE", + "input": 42.42, + "to": "MN", + "expect": 0.4242 + }, + { + "from": "DYNE", + "input": 42.42, + "to": "KGF", + "expect": 0.000043256361754523724 + }, + { + "from": "DYNE", + "input": 42.42, + "to": "DYNE", + "expect": 42.42 + }, + { + "from": "DYNE", + "input": 42.42, + "to": "PDL", + "expect": 0.003068244475683238 + }, + { + "from": "DYNE", + "input": 42.42, + "to": "SHORT_TON_FORCE", + "expect": 4.76819768314486e-8 + }, + { + "from": "DYNE", + "input": 42.42, + "to": "LONG_TON_FORCE", + "expect": 4.2573193599507684e-8 + }, + { + "from": "DYNE", + "input": 42.42, + "to": "LBF", + "expect": 0.00009536395366289719 + }, + { + "from": "DYNE", + "input": 42.42, + "to": "OZF", + "expect": 0.001525823258606355 + }, + { + "from": "DYNE", + "input": 42.42, + "to": "KPF", + "expect": 9.536395366289722e-8 + }, + { + "from": "PDL", + "input": 42.42, + "to": "N", + "expect": 5.86477516462992 + }, + { + "from": "PDL", + "input": 42.42, + "to": "KN", + "expect": 0.00586477516462992 + }, + { + "from": "PDL", + "input": 42.42, + "to": "MN", + "expect": 5864.77516462992 + }, + { + "from": "PDL", + "input": 42.42, + "to": "KGF", + "expect": 0.5980406320843428 + }, + { + "from": "PDL", + "input": 42.42, + "to": "DYNE", + "expect": 586477.5164629921 + }, + { + "from": "PDL", + "input": 42.42, + "to": "PDL", + "expect": 42.42 + }, + { + "from": "PDL", + "input": 42.42, + "to": "SHORT_TON_FORCE", + "expect": 0.0006592269531389415 + }, + { + "from": "PDL", + "input": 42.42, + "to": "LONG_TON_FORCE", + "expect": 0.0005885954938740547 + }, + { + "from": "PDL", + "input": 42.42, + "to": "LBF", + "expect": 1.3184539062778828 + }, + { + "from": "PDL", + "input": 42.42, + "to": "OZF", + "expect": 21.095262500446125 + }, + { + "from": "PDL", + "input": 42.42, + "to": "KPF", + "expect": 0.0013184539062778826 + }, + { + "from": "SHORT_TON_FORCE", + "input": 42.42, + "to": "N", + "expect": 377387.1218387008 + }, + { + "from": "SHORT_TON_FORCE", + "input": 42.42, + "to": "KN", + "expect": 377.3871218387008 + }, + { + "from": "SHORT_TON_FORCE", + "input": 42.42, + "to": "MN", + "expect": 377387121.8387008 + }, + { + "from": "SHORT_TON_FORCE", + "input": 42.42, + "to": "KGF", + "expect": 38482.776670800005 + }, + { + "from": "SHORT_TON_FORCE", + "input": 42.42, + "to": "DYNE", + "expect": 37738712183.87008 + }, + { + "from": "SHORT_TON_FORCE", + "input": 42.42, + "to": "PDL", + "expect": 2729646.279527559 + }, + { + "from": "SHORT_TON_FORCE", + "input": 42.42, + "to": "SHORT_TON_FORCE", + "expect": 42.42 + }, + { + "from": "SHORT_TON_FORCE", + "input": 42.42, + "to": "LONG_TON_FORCE", + "expect": 37.875 + }, + { + "from": "SHORT_TON_FORCE", + "input": 42.42, + "to": "LBF", + "expect": 84840 + }, + { + "from": "SHORT_TON_FORCE", + "input": 42.42, + "to": "OZF", + "expect": 1357440 + }, + { + "from": "SHORT_TON_FORCE", + "input": 42.42, + "to": "KPF", + "expect": 84.84 + }, + { + "from": "LONG_TON_FORCE", + "input": 42.42, + "to": "N", + "expect": 422673.5764593449 + }, + { + "from": "LONG_TON_FORCE", + "input": 42.42, + "to": "KN", + "expect": 422.6735764593449 + }, + { + "from": "LONG_TON_FORCE", + "input": 42.42, + "to": "MN", + "expect": 422673576.45934486 + }, + { + "from": "LONG_TON_FORCE", + "input": 42.42, + "to": "KGF", + "expect": 43100.70987129601 + }, + { + "from": "LONG_TON_FORCE", + "input": 42.42, + "to": "DYNE", + "expect": 42267357645.93449 + }, + { + "from": "LONG_TON_FORCE", + "input": 42.42, + "to": "PDL", + "expect": 3057203.833070866 + }, + { + "from": "LONG_TON_FORCE", + "input": 42.42, + "to": "SHORT_TON_FORCE", + "expect": 47.510400000000004 + }, + { + "from": "LONG_TON_FORCE", + "input": 42.42, + "to": "LONG_TON_FORCE", + "expect": 42.42 + }, + { + "from": "LONG_TON_FORCE", + "input": 42.42, + "to": "LBF", + "expect": 95020.8 + }, + { + "from": "LONG_TON_FORCE", + "input": 42.42, + "to": "OZF", + "expect": 1520332.8 + }, + { + "from": "LONG_TON_FORCE", + "input": 42.42, + "to": "KPF", + "expect": 95.02080000000001 + }, + { + "from": "LBF", + "input": 42.42, + "to": "N", + "expect": 188.6935609193504 + }, + { + "from": "LBF", + "input": 42.42, + "to": "KN", + "expect": 0.18869356091935038 + }, + { + "from": "LBF", + "input": 42.42, + "to": "MN", + "expect": 188693.5609193504 + }, + { + "from": "LBF", + "input": 42.42, + "to": "KGF", + "expect": 19.241388335400003 + }, + { + "from": "LBF", + "input": 42.42, + "to": "DYNE", + "expect": 18869356.09193504 + }, + { + "from": "LBF", + "input": 42.42, + "to": "PDL", + "expect": 1364.8231397637796 + }, + { + "from": "LBF", + "input": 42.42, + "to": "SHORT_TON_FORCE", + "expect": 0.02121 + }, + { + "from": "LBF", + "input": 42.42, + "to": "LONG_TON_FORCE", + "expect": 0.0189375 + }, + { + "from": "LBF", + "input": 42.42, + "to": "LBF", + "expect": 42.42 + }, + { + "from": "LBF", + "input": 42.42, + "to": "OZF", + "expect": 678.72 + }, + { + "from": "LBF", + "input": 42.42, + "to": "KPF", + "expect": 0.04242 + }, + { + "from": "OZF", + "input": 42.42, + "to": "N", + "expect": 11.7933475574594 + }, + { + "from": "OZF", + "input": 42.42, + "to": "KN", + "expect": 0.011793347557459399 + }, + { + "from": "OZF", + "input": 42.42, + "to": "MN", + "expect": 11793.3475574594 + }, + { + "from": "OZF", + "input": 42.42, + "to": "KGF", + "expect": 1.2025867709625002 + }, + { + "from": "OZF", + "input": 42.42, + "to": "DYNE", + "expect": 1179334.75574594 + }, + { + "from": "OZF", + "input": 42.42, + "to": "PDL", + "expect": 85.30144623523623 + }, + { + "from": "OZF", + "input": 42.42, + "to": "SHORT_TON_FORCE", + "expect": 0.001325625 + }, + { + "from": "OZF", + "input": 42.42, + "to": "LONG_TON_FORCE", + "expect": 0.00118359375 + }, + { + "from": "OZF", + "input": 42.42, + "to": "LBF", + "expect": 2.65125 + }, + { + "from": "OZF", + "input": 42.42, + "to": "OZF", + "expect": 42.42 + }, + { + "from": "OZF", + "input": 42.42, + "to": "KPF", + "expect": 0.00265125 + }, + { + "from": "KPF", + "input": 42.42, + "to": "N", + "expect": 188693.5609193504 + }, + { + "from": "KPF", + "input": 42.42, + "to": "KN", + "expect": 188.6935609193504 + }, + { + "from": "KPF", + "input": 42.42, + "to": "MN", + "expect": 188693560.9193504 + }, + { + "from": "KPF", + "input": 42.42, + "to": "KGF", + "expect": 19241.388335400003 + }, + { + "from": "KPF", + "input": 42.42, + "to": "DYNE", + "expect": 18869356091.93504 + }, + { + "from": "KPF", + "input": 42.42, + "to": "PDL", + "expect": 1364823.1397637795 + }, + { + "from": "KPF", + "input": 42.42, + "to": "SHORT_TON_FORCE", + "expect": 21.21 + }, + { + "from": "KPF", + "input": 42.42, + "to": "LONG_TON_FORCE", + "expect": 18.9375 + }, + { + "from": "KPF", + "input": 42.42, + "to": "LBF", + "expect": 42420 + }, + { + "from": "KPF", + "input": 42.42, + "to": "OZF", + "expect": 678720 + }, + { + "from": "KPF", + "input": 42.42, + "to": "KPF", + "expect": 42.42 + }, + { + "from": "N_PER_CUB_M", + "input": 42.42, + "to": "N_PER_CUB_M", + "expect": 42.42 + }, + { + "from": "N_PER_CUB_M", + "input": 42.42, + "to": "KN_PER_CUB_M", + "expect": 0.04242 + }, + { + "from": "N_PER_CUB_M", + "input": 42.42, + "to": "N_PER_CUB_FT", + "expect": 1.2012006324326399 + }, + { + "from": "N_PER_CUB_M", + "input": 42.42, + "to": "KN_PER_CUB_FT", + "expect": 0.0012012006324326398 + }, + { + "from": "KN_PER_CUB_M", + "input": 42.42, + "to": "N_PER_CUB_M", + "expect": 42420 + }, + { + "from": "KN_PER_CUB_M", + "input": 42.42, + "to": "KN_PER_CUB_M", + "expect": 42.42 + }, + { + "from": "KN_PER_CUB_M", + "input": 42.42, + "to": "N_PER_CUB_FT", + "expect": 1201.20063243264 + }, + { + "from": "KN_PER_CUB_M", + "input": 42.42, + "to": "KN_PER_CUB_FT", + "expect": 1.2012006324326399 + }, + { + "from": "N_PER_CUB_FT", + "input": 42.42, + "to": "N_PER_CUB_M", + "expect": 1498.0481623255462 + }, + { + "from": "N_PER_CUB_FT", + "input": 42.42, + "to": "KN_PER_CUB_M", + "expect": 1.4980481623255462 + }, + { + "from": "N_PER_CUB_FT", + "input": 42.42, + "to": "N_PER_CUB_FT", + "expect": 42.42 + }, + { + "from": "N_PER_CUB_FT", + "input": 42.42, + "to": "KN_PER_CUB_FT", + "expect": 0.04242 + }, + { + "from": "KN_PER_CUB_FT", + "input": 42.42, + "to": "N_PER_CUB_M", + "expect": 1498048.162325546 + }, + { + "from": "KN_PER_CUB_FT", + "input": 42.42, + "to": "KN_PER_CUB_M", + "expect": 1498.0481623255462 + }, + { + "from": "KN_PER_CUB_FT", + "input": 42.42, + "to": "N_PER_CUB_FT", + "expect": 42420 + }, + { + "from": "KN_PER_CUB_FT", + "input": 42.42, + "to": "KN_PER_CUB_FT", + "expect": 42.42 + }, + { + "from": "HZ", + "input": 42.42, + "to": "HZ", + "expect": 42.42 + }, + { + "from": "HZ", + "input": 42.42, + "to": "KHZ", + "expect": 0.04242 + }, + { + "from": "HZ", + "input": 42.42, + "to": "MHZ", + "expect": 0.00004242 + }, + { + "from": "KHZ", + "input": 42.42, + "to": "HZ", + "expect": 42420 + }, + { + "from": "KHZ", + "input": 42.42, + "to": "KHZ", + "expect": 42.42 + }, + { + "from": "KHZ", + "input": 42.42, + "to": "MHZ", + "expect": 0.04242 + }, + { + "from": "MHZ", + "input": 42.42, + "to": "HZ", + "expect": 42420000 + }, + { + "from": "MHZ", + "input": 42.42, + "to": "KHZ", + "expect": 42420 + }, + { + "from": "MHZ", + "input": 42.42, + "to": "MHZ", + "expect": 42.42 + }, + { + "from": "W_PER_SQ_M", + "input": 42.42, + "to": "W_PER_SQ_M", + "expect": 42.42 + }, + { + "from": "W_PER_SQ_M_K", + "input": 42.42, + "to": "W_PER_SQ_M_K", + "expect": 42.42 + }, + { + "from": "W_PER_SQ_M_K", + "input": 42.42, + "to": "W_PER_SQ_M_CELSIUS", + "expect": 42.42 + }, + { + "from": "W_PER_SQ_M_K", + "input": 42.42, + "to": "BTU_PER_SQ_FT_HR_FAHRENHEIT", + "expect": 7.4705939918034145 + }, + { + "from": "W_PER_SQ_M_CELSIUS", + "input": 42.42, + "to": "W_PER_SQ_M_K", + "expect": 42.42 + }, + { + "from": "W_PER_SQ_M_CELSIUS", + "input": 42.42, + "to": "W_PER_SQ_M_CELSIUS", + "expect": 42.42 + }, + { + "from": "W_PER_SQ_M_CELSIUS", + "input": 42.42, + "to": "BTU_PER_SQ_FT_HR_FAHRENHEIT", + "expect": 7.4705939918034145 + }, + { + "from": "BTU_PER_SQ_FT_HR_FAHRENHEIT", + "input": 42.42, + "to": "W_PER_SQ_M_K", + "expect": 240.87193093003418 + }, + { + "from": "BTU_PER_SQ_FT_HR_FAHRENHEIT", + "input": 42.42, + "to": "W_PER_SQ_M_CELSIUS", + "expect": 240.87193093003418 + }, + { + "from": "BTU_PER_SQ_FT_HR_FAHRENHEIT", + "input": 42.42, + "to": "BTU_PER_SQ_FT_HR_FAHRENHEIT", + "expect": 42.42 + }, + { + "from": "LUX", + "input": 42.42, + "to": "LUX", + "expect": 42.42 + }, + { + "from": "LUX", + "input": 42.42, + "to": "LUMEN_PER_SQ_FT", + "expect": 3.9409469568 + }, + { + "from": "LUMEN_PER_SQ_FT", + "input": 42.42, + "to": "LUX", + "expect": 456.6050798768265 + }, + { + "from": "LUMEN_PER_SQ_FT", + "input": 42.42, + "to": "LUMEN_PER_SQ_FT", + "expect": 42.42 + }, + { + "from": "SQ_M_PER_SEC", + "input": 42.42, + "to": "SQ_M_PER_SEC", + "expect": 42.42 + }, + { + "from": "SQ_M_PER_SEC", + "input": 42.42, + "to": "SQ_FT_PER_SEC", + "expect": 456.60507987682644 + }, + { + "from": "SQ_M_PER_SEC", + "input": 42.42, + "to": "STOKE", + "expect": 424200 + }, + { + "from": "SQ_M_PER_SEC", + "input": 42.42, + "to": "CENTISTOKE", + "expect": 42420000 + }, + { + "from": "SQ_FT_PER_SEC", + "input": 42.42, + "to": "SQ_M_PER_SEC", + "expect": 3.9409469568 + }, + { + "from": "SQ_FT_PER_SEC", + "input": 42.42, + "to": "SQ_FT_PER_SEC", + "expect": 42.42 + }, + { + "from": "SQ_FT_PER_SEC", + "input": 42.42, + "to": "STOKE", + "expect": 39409.46956799999 + }, + { + "from": "SQ_FT_PER_SEC", + "input": 42.42, + "to": "CENTISTOKE", + "expect": 3940946.9568 + }, + { + "from": "STOKE", + "input": 42.42, + "to": "SQ_M_PER_SEC", + "expect": 0.004242 + }, + { + "from": "STOKE", + "input": 42.42, + "to": "SQ_FT_PER_SEC", + "expect": 0.04566050798768265 + }, + { + "from": "STOKE", + "input": 42.42, + "to": "STOKE", + "expect": 42.42 + }, + { + "from": "STOKE", + "input": 42.42, + "to": "CENTISTOKE", + "expect": 4242.000000000001 + }, + { + "from": "CENTISTOKE", + "input": 42.42, + "to": "SQ_M_PER_SEC", + "expect": 0.00004242 + }, + { + "from": "CENTISTOKE", + "input": 42.42, + "to": "SQ_FT_PER_SEC", + "expect": 0.00045660507987682646 + }, + { + "from": "CENTISTOKE", + "input": 42.42, + "to": "STOKE", + "expect": 0.42419999999999997 + }, + { + "from": "CENTISTOKE", + "input": 42.42, + "to": "CENTISTOKE", + "expect": 42.42 + }, + { + "from": "M", + "input": 42.42, + "to": "M", + "expect": 42.42 + }, + { + "from": "M", + "input": 42.42, + "to": "MM", + "expect": 42420 + }, + { + "from": "M", + "input": 42.42, + "to": "CM", + "expect": 4242 + }, + { + "from": "M", + "input": 42.42, + "to": "DM", + "expect": 424.20000000000005 + }, + { + "from": "M", + "input": 42.42, + "to": "KM", + "expect": 0.04242 + }, + { + "from": "M", + "input": 42.42, + "to": "UM", + "expect": 42420000 + }, + { + "from": "M", + "input": 42.42, + "to": "MILLIINCH", + "expect": 1670078.7401574804 + }, + { + "from": "M", + "input": 42.42, + "to": "MICROINCH", + "expect": 1670078740.1574805 + }, + { + "from": "M", + "input": 42.42, + "to": "MILLIFOOT", + "expect": 139173.22834645672 + }, + { + "from": "M", + "input": 42.42, + "to": "IN", + "expect": 1670.0787401574805 + }, + { + "from": "M", + "input": 42.42, + "to": "FT", + "expect": 139.1732283464567 + }, + { + "from": "M", + "input": 42.42, + "to": "YRD", + "expect": 46.39107611548557 + }, + { + "from": "M", + "input": 42.42, + "to": "CHAIN", + "expect": 2.1086852779766168 + }, + { + "from": "M", + "input": 42.42, + "to": "MILE", + "expect": 0.02635856597470771 + }, + { + "from": "M", + "input": 42.42, + "to": "US_SURVEY_IN", + "expect": 1670.0754 + }, + { + "from": "M", + "input": 42.42, + "to": "US_SURVEY_FT", + "expect": 139.17295000000001 + }, + { + "from": "M", + "input": 42.42, + "to": "US_SURVEY_YRD", + "expect": 46.39098333333333 + }, + { + "from": "M", + "input": 42.42, + "to": "US_SURVEY_CHAIN", + "expect": 2.108681060606061 + }, + { + "from": "M", + "input": 42.42, + "to": "US_SURVEY_MILE", + "expect": 0.026358513257575756 + }, + { + "from": "M", + "input": 42.42, + "to": "NAUT_MILE", + "expect": 0.022904967602591794 + }, + { + "from": "MM", + "input": 42.42, + "to": "M", + "expect": 0.04242 + }, + { + "from": "MM", + "input": 42.42, + "to": "MM", + "expect": 42.42 + }, + { + "from": "MM", + "input": 42.42, + "to": "CM", + "expect": 4.242 + }, + { + "from": "MM", + "input": 42.42, + "to": "DM", + "expect": 0.4242 + }, + { + "from": "MM", + "input": 42.42, + "to": "KM", + "expect": 0.00004242 + }, + { + "from": "MM", + "input": 42.42, + "to": "UM", + "expect": 42420.00000000001 + }, + { + "from": "MM", + "input": 42.42, + "to": "MILLIINCH", + "expect": 1670.0787401574805 + }, + { + "from": "MM", + "input": 42.42, + "to": "MICROINCH", + "expect": 1670078.7401574804 + }, + { + "from": "MM", + "input": 42.42, + "to": "MILLIFOOT", + "expect": 139.1732283464567 + }, + { + "from": "MM", + "input": 42.42, + "to": "IN", + "expect": 1.6700787401574804 + }, + { + "from": "MM", + "input": 42.42, + "to": "FT", + "expect": 0.1391732283464567 + }, + { + "from": "MM", + "input": 42.42, + "to": "YRD", + "expect": 0.04639107611548557 + }, + { + "from": "MM", + "input": 42.42, + "to": "CHAIN", + "expect": 0.0021086852779766165 + }, + { + "from": "MM", + "input": 42.42, + "to": "MILE", + "expect": 0.000026358565974707707 + }, + { + "from": "MM", + "input": 42.42, + "to": "US_SURVEY_IN", + "expect": 1.6700754000000002 + }, + { + "from": "MM", + "input": 42.42, + "to": "US_SURVEY_FT", + "expect": 0.13917295000000002 + }, + { + "from": "MM", + "input": 42.42, + "to": "US_SURVEY_YRD", + "expect": 0.04639098333333333 + }, + { + "from": "MM", + "input": 42.42, + "to": "US_SURVEY_CHAIN", + "expect": 0.002108681060606061 + }, + { + "from": "MM", + "input": 42.42, + "to": "US_SURVEY_MILE", + "expect": 0.000026358513257575757 + }, + { + "from": "MM", + "input": 42.42, + "to": "NAUT_MILE", + "expect": 0.000022904967602591793 + }, + { + "from": "CM", + "input": 42.42, + "to": "M", + "expect": 0.4242 + }, + { + "from": "CM", + "input": 42.42, + "to": "MM", + "expect": 424.20000000000005 + }, + { + "from": "CM", + "input": 42.42, + "to": "CM", + "expect": 42.42 + }, + { + "from": "CM", + "input": 42.42, + "to": "DM", + "expect": 4.242 + }, + { + "from": "CM", + "input": 42.42, + "to": "KM", + "expect": 0.00042420000000000007 + }, + { + "from": "CM", + "input": 42.42, + "to": "UM", + "expect": 424200 + }, + { + "from": "CM", + "input": 42.42, + "to": "MILLIINCH", + "expect": 16700.787401574806 + }, + { + "from": "CM", + "input": 42.42, + "to": "MICROINCH", + "expect": 16700787.401574805 + }, + { + "from": "CM", + "input": 42.42, + "to": "MILLIFOOT", + "expect": 1391.7322834645672 + }, + { + "from": "CM", + "input": 42.42, + "to": "IN", + "expect": 16.700787401574804 + }, + { + "from": "CM", + "input": 42.42, + "to": "FT", + "expect": 1.391732283464567 + }, + { + "from": "CM", + "input": 42.42, + "to": "YRD", + "expect": 0.4639107611548558 + }, + { + "from": "CM", + "input": 42.42, + "to": "CHAIN", + "expect": 0.021086852779766168 + }, + { + "from": "CM", + "input": 42.42, + "to": "MILE", + "expect": 0.0002635856597470771 + }, + { + "from": "CM", + "input": 42.42, + "to": "US_SURVEY_IN", + "expect": 16.700754 + }, + { + "from": "CM", + "input": 42.42, + "to": "US_SURVEY_FT", + "expect": 1.3917295 + }, + { + "from": "CM", + "input": 42.42, + "to": "US_SURVEY_YRD", + "expect": 0.46390983333333335 + }, + { + "from": "CM", + "input": 42.42, + "to": "US_SURVEY_CHAIN", + "expect": 0.021086810606060605 + }, + { + "from": "CM", + "input": 42.42, + "to": "US_SURVEY_MILE", + "expect": 0.0002635851325757576 + }, + { + "from": "CM", + "input": 42.42, + "to": "NAUT_MILE", + "expect": 0.00022904967602591794 + }, + { + "from": "DM", + "input": 42.42, + "to": "M", + "expect": 4.242 + }, + { + "from": "DM", + "input": 42.42, + "to": "MM", + "expect": 4242 + }, + { + "from": "DM", + "input": 42.42, + "to": "CM", + "expect": 424.20000000000005 + }, + { + "from": "DM", + "input": 42.42, + "to": "DM", + "expect": 42.42 + }, + { + "from": "DM", + "input": 42.42, + "to": "KM", + "expect": 0.004242 + }, + { + "from": "DM", + "input": 42.42, + "to": "UM", + "expect": 4242000.000000001 + }, + { + "from": "DM", + "input": 42.42, + "to": "MILLIINCH", + "expect": 167007.8740157481 + }, + { + "from": "DM", + "input": 42.42, + "to": "MICROINCH", + "expect": 167007874.01574808 + }, + { + "from": "DM", + "input": 42.42, + "to": "MILLIFOOT", + "expect": 13917.32283464567 + }, + { + "from": "DM", + "input": 42.42, + "to": "IN", + "expect": 167.00787401574803 + }, + { + "from": "DM", + "input": 42.42, + "to": "FT", + "expect": 13.917322834645672 + }, + { + "from": "DM", + "input": 42.42, + "to": "YRD", + "expect": 4.639107611548558 + }, + { + "from": "DM", + "input": 42.42, + "to": "CHAIN", + "expect": 0.21086852779766171 + }, + { + "from": "DM", + "input": 42.42, + "to": "MILE", + "expect": 0.002635856597470771 + }, + { + "from": "DM", + "input": 42.42, + "to": "US_SURVEY_IN", + "expect": 167.00754 + }, + { + "from": "DM", + "input": 42.42, + "to": "US_SURVEY_FT", + "expect": 13.917295000000001 + }, + { + "from": "DM", + "input": 42.42, + "to": "US_SURVEY_YRD", + "expect": 4.639098333333334 + }, + { + "from": "DM", + "input": 42.42, + "to": "US_SURVEY_CHAIN", + "expect": 0.21086810606060608 + }, + { + "from": "DM", + "input": 42.42, + "to": "US_SURVEY_MILE", + "expect": 0.0026358513257575757 + }, + { + "from": "DM", + "input": 42.42, + "to": "NAUT_MILE", + "expect": 0.0022904967602591798 + }, + { + "from": "KM", + "input": 42.42, + "to": "M", + "expect": 42420 + }, + { + "from": "KM", + "input": 42.42, + "to": "MM", + "expect": 42420000 + }, + { + "from": "KM", + "input": 42.42, + "to": "CM", + "expect": 4242000 + }, + { + "from": "KM", + "input": 42.42, + "to": "DM", + "expect": 424200 + }, + { + "from": "KM", + "input": 42.42, + "to": "KM", + "expect": 42.42 + }, + { + "from": "KM", + "input": 42.42, + "to": "UM", + "expect": 42420000000 + }, + { + "from": "KM", + "input": 42.42, + "to": "MILLIINCH", + "expect": 1670078740.1574805 + }, + { + "from": "KM", + "input": 42.42, + "to": "MICROINCH", + "expect": 1670078740157.4802 + }, + { + "from": "KM", + "input": 42.42, + "to": "MILLIFOOT", + "expect": 139173228.3464567 + }, + { + "from": "KM", + "input": 42.42, + "to": "IN", + "expect": 1670078.7401574806 + }, + { + "from": "KM", + "input": 42.42, + "to": "FT", + "expect": 139173.2283464567 + }, + { + "from": "KM", + "input": 42.42, + "to": "YRD", + "expect": 46391.07611548556 + }, + { + "from": "KM", + "input": 42.42, + "to": "CHAIN", + "expect": 2108.6852779766164 + }, + { + "from": "KM", + "input": 42.42, + "to": "MILE", + "expect": 26.3585659747077 + }, + { + "from": "KM", + "input": 42.42, + "to": "US_SURVEY_IN", + "expect": 1670075.4000000001 + }, + { + "from": "KM", + "input": 42.42, + "to": "US_SURVEY_FT", + "expect": 139172.95 + }, + { + "from": "KM", + "input": 42.42, + "to": "US_SURVEY_YRD", + "expect": 46390.98333333334 + }, + { + "from": "KM", + "input": 42.42, + "to": "US_SURVEY_CHAIN", + "expect": 2108.681060606061 + }, + { + "from": "KM", + "input": 42.42, + "to": "US_SURVEY_MILE", + "expect": 26.35851325757576 + }, + { + "from": "KM", + "input": 42.42, + "to": "NAUT_MILE", + "expect": 22.904967602591793 + }, + { + "from": "UM", + "input": 42.42, + "to": "M", + "expect": 0.00004242 + }, + { + "from": "UM", + "input": 42.42, + "to": "MM", + "expect": 0.04242 + }, + { + "from": "UM", + "input": 42.42, + "to": "CM", + "expect": 0.004242 + }, + { + "from": "UM", + "input": 42.42, + "to": "DM", + "expect": 0.00042419999999999996 + }, + { + "from": "UM", + "input": 42.42, + "to": "KM", + "expect": 4.242e-8 + }, + { + "from": "UM", + "input": 42.42, + "to": "UM", + "expect": 42.42 + }, + { + "from": "UM", + "input": 42.42, + "to": "MILLIINCH", + "expect": 1.6700787401574804 + }, + { + "from": "UM", + "input": 42.42, + "to": "MICROINCH", + "expect": 1670.0787401574805 + }, + { + "from": "UM", + "input": 42.42, + "to": "MILLIFOOT", + "expect": 0.1391732283464567 + }, + { + "from": "UM", + "input": 42.42, + "to": "IN", + "expect": 0.0016700787401574806 + }, + { + "from": "UM", + "input": 42.42, + "to": "FT", + "expect": 0.00013917322834645668 + }, + { + "from": "UM", + "input": 42.42, + "to": "YRD", + "expect": 0.00004639107611548557 + }, + { + "from": "UM", + "input": 42.42, + "to": "CHAIN", + "expect": 0.0000021086852779766164 + }, + { + "from": "UM", + "input": 42.42, + "to": "MILE", + "expect": 2.6358565974707707e-8 + }, + { + "from": "UM", + "input": 42.42, + "to": "US_SURVEY_IN", + "expect": 0.0016700754 + }, + { + "from": "GRAD", + "input": 42.42, + "to": "ARC_DEG", + "expect": 38.178 + }, + { + "from": "ARC_DEG", + "input": 42.42, + "to": "GRAD", + "expect": 47.13333333333334 + }, + { + "from": "ARC_MINUTE", + "input": 42.42, + "to": "ARC_DEG", + "expect": 0.7070000000000001 + }, + { + "from": "ARC_DEG", + "input": 42.42, + "to": "ARC_MINUTE", + "expect": 2545.2000000000003 + }, + { + "from": "GRAD", + "input": 42.42, + "to": "ARC_QUADRANT", + "expect": 0.42419999999999997 + }, + { + "from": "ARC_QUADRANT", + "input": 42.42, + "to": "GRAD", + "expect": 4242 + }, + { + "from": "ARC_MINUTE", + "input": 42.42, + "to": "ARC_QUADRANT", + "expect": 0.007855555555555555 + }, + { + "from": "ARC_QUADRANT", + "input": 42.42, + "to": "ARC_MINUTE", + "expect": 229068 + }, + { + "from": "GRAD", + "input": 42.42, + "to": "REVOLUTION", + "expect": 0.10604999999999999 + }, + { + "from": "REVOLUTION", + "input": 42.42, + "to": "GRAD", + "expect": 16968 + }, + { + "from": "ARC_MINUTE", + "input": 42.42, + "to": "REVOLUTION", + "expect": 0.0019638888888888887 + }, + { + "from": "REVOLUTION", + "input": 42.42, + "to": "ARC_MINUTE", + "expect": 916272 + }, + { + "from": "DEG_PER_SEC", + "input": 42.42, + "to": "RAD_PER_MIN", + "expect": 44.422120121759676 + }, + { + "from": "RAD_PER_MIN", + "input": 42.42, + "to": "DEG_PER_SEC", + "expect": 40.5081161157492 + }, + { + "from": "DEG_PER_HR", + "input": 42.42, + "to": "RPM", + "expect": 0.001963888888888889 + }, + { + "from": "RPM", + "input": 42.42, + "to": "DEG_PER_HR", + "expect": 916272 + }, + { + "from": "KILOPASCAL", + "input": 42.42, + "to": "PSI", + "expect": 6.152500834515474 + }, + { + "from": "BARYE", + "input": 42.42, + "to": "HECTOPASCAL", + "expect": 0.04242 + }, + { + "from": "SQ_FT_HR_FAHRENHEIT_PER_BTU", + "input": 42.42, + "to": "SQ_M_CELSIUS_PER_WATT", + "expect": 7.470593991803412 + }, + { + "from": "SQ_M_CELSIUS_PER_WATT", + "input": 42.42, + "to": "SQ_FT_HR_FAHRENHEIT_PER_BTU", + "expect": 240.8719309300342 + }, + { + "from": "FT_PER_IN", + "input": 42.42, + "to": "PERCENT_SLOPE", + "expect": 50904 + }, + { + "from": "PERCENT_SLOPE", + "input": 42.42, + "to": "FT_PER_IN", + "expect": 0.03535 + }, + { + "from": "AT", + "input": 42.42, + "to": "AT_GAUGE", + "expect": 41.38677254720012 + }, + { + "from": "AT_GAUGE", + "input": 42.42, + "to": "AT", + "expect": 43.45322745279989 + }, + { + "from": "AT_GAUGE", + "input": 42.42, + "to": "PA_GAUGE", + "expect": 4159980.93 + }, + { + "from": "PA_GAUGE", + "input": 42.42, + "to": "AT_GAUGE", + "expect": 0.0004325636175450151 + }, + { + "from": "BAR", + "input": 42.42, + "to": "BAR_GAUGE", + "expect": 41.406749999999995 + }, + { + "from": "BAR_GAUGE", + "input": 42.42, + "to": "BAR", + "expect": 43.433249999999994 + }, + { + "from": "TORR", + "input": 42.42, + "to": "AT_GAUGE", + "expect": -0.9755570468159765 + }, + { + "from": "AT_GAUGE", + "input": 42.42, + "to": "TORR", + "expect": 31962.42296373057 + }, + { + "from": "KILOPASCAL_GAUGE", + "input": 42.42, + "to": "PA_GAUGE", + "expect": 42420 + }, + { + "from": "PA_GAUGE", + "input": 42.42, + "to": "KILOPASCAL_GAUGE", + "expect": 0.04242 + }, + { + "from": "KILOPASCAL_GAUGE", + "input": 42.42, + "to": "PSIG", + "expect": 6.152500834515474 + }, + { + "from": "PSIG", + "input": 42.42, + "to": "KILOPASCAL_GAUGE", + "expect": 292.47560437620194 + }, + { + "from": "FAHRENHEIT", + "input": 42.42, + "to": "K", + "expect": 278.93888888888887 + }, + { + "from": "K", + "input": 42.42, + "to": "FAHRENHEIT", + "expect": -383.31399999999996 + }, + { + "from": "CELSIUS", + "input": 42.42, + "to": "RANKINE", + "expect": 568.026 + }, + { + "from": "RANKINE", + "input": 42.42, + "to": "CELSIUS", + "expect": -249.58333333333331 + }, + { + "from": "FAHRENHEIT", + "input": 42.42, + "to": "CELSIUS", + "expect": 5.7888888888888985 + }, + { + "from": "CELSIUS", + "input": 42.42, + "to": "FAHRENHEIT", + "expect": 108.35600000000001 + } +] \ No newline at end of file diff --git a/core/ecschema-metadata/src/test/assets/ValidationUnits.ecschema.xml b/core/ecschema-metadata/src/test/assets/ValidationUnits.ecschema.xml new file mode 100644 index 00000000000..5269682f1b1 --- /dev/null +++ b/core/ecschema-metadata/src/test/assets/ValidationUnits.ecschema.xml @@ -0,0 +1,43 @@ +AlteredUnits.ecschema.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/ecschema-metadata/test/UnitConversion/Convert.test.ts b/core/ecschema-metadata/test/UnitConversion/Convert.test.ts deleted file mode 100644 index d35a7dd22af..00000000000 --- a/core/ecschema-metadata/test/UnitConversion/Convert.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Bentley Systems, Incorporated. All rights reserved. -* See LICENSE.md in the project root for license terms and full copyright notice. -*--------------------------------------------------------------------------------------------*/ -import { SchemaContext } from "../../src/ecschema-metadata"; -import { expect } from "chai"; -import * as fs from "fs"; -import * as path from "path"; -import * as almostEqual from "almost-equal"; -import { deserializeXml } from "./DeserializeSchema"; -import { UnitConverter } from "../../src/UnitConversion/UnitConverter"; - -interface TestData { - From: string; - Input: number; - To: string; - Expect: number; -} - -describe("A unit tree creator", () => { - const context = new SchemaContext(); - - const testData: TestData[] = JSON.parse( - fs.readFileSync(path.join(__dirname, "assets", "./UnitTests.json"), "utf-8") - ); - - before(() => { - const schemaFile = path.join(__dirname, "..", "..", "node_modules", "@bentley", "units-schema", "Units.ecschema.xml"); - const schemaXml = fs.readFileSync(schemaFile, "utf-8"); - deserializeXml(context, schemaXml as string); - }); - - testData.forEach((test: TestData) => { - it(`should convert ${test.From} to ${test.To}`, async () => { - const converter = new UnitConverter(context); - const fromFullName = `Units:${test.From}`; - const toFullName = `Units:${test.To}`; - const map = await converter.calculateConversion(fromFullName, toFullName); - const actual = map.evaluate(test.Input); - expect( - almostEqual(test.Expect, actual, almostEqual.FLT_EPSILON, almostEqual.FLT_EPSILON), - `${test.Input} ${test.From} in ${test.To} should be ${test.Expect} - and not ${actual} error = ${Math.abs(test.Expect - actual)} > ${almostEqual.FLT_EPSILON}` - ).to.be.true; - }); - }); -}); diff --git a/core/ecschema-metadata/test/UnitConversion/CrossSchema.test.ts b/core/ecschema-metadata/test/UnitConversion/CrossSchema.test.ts deleted file mode 100644 index 4103f39f1c1..00000000000 --- a/core/ecschema-metadata/test/UnitConversion/CrossSchema.test.ts +++ /dev/null @@ -1,89 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Bentley Systems, Incorporated. All rights reserved. -* See LICENSE.md in the project root for license terms and full copyright notice. -*--------------------------------------------------------------------------------------------*/ -import { SchemaContext } from "../../src/ecschema-metadata"; -import { expect } from "chai"; -import * as fs from "fs"; -import * as path from "path"; -import * as almostEqual from "almost-equal"; -import { deserializeXml } from "./DeserializeSchema"; -import { UnitConverter } from "../../src/UnitConversion/UnitConverter"; - -interface TestData { - FromSchema: string; - From: string; - ToSchema: string; - To: string; - Input: number; - Expect: number; -} - -describe("Testing creating second schema", () => { - const context = new SchemaContext(); - - const testData: TestData[] = JSON.parse( - fs.readFileSync(path.join(__dirname, "assets", "./CrossSchemaTests.json"), "utf-8") - ); - - before(() => { - const siSchemaFile = path.join(__dirname, "assets", "SIUnits.ecschema.xml"); - const siSchemaXml = fs.readFileSync(siSchemaFile, "utf-8"); - deserializeXml(context, siSchemaXml); - - const metricSchemaFile = path.join(__dirname, "assets", "MetricUnits.ecschema.xml"); - const metricSchemaXml = fs.readFileSync(metricSchemaFile, "utf-8"); - deserializeXml(context, metricSchemaXml); - - const usSchemaFile = path.join(__dirname, "assets", "USUnits.ecschema.xml"); - const usSchemaXml = fs.readFileSync(usSchemaFile, "utf-8"); - deserializeXml(context, usSchemaXml); - }); - - testData.forEach((test: TestData) => { - it(`should convert ${test.FromSchema}:${test.From} to ${test.ToSchema}:${test.To}`, async () => { - const converter = new UnitConverter(context); - const fromFullName = `${test.FromSchema}.${test.From}`; - const toFullName = `${test.ToSchema}.${test.To}`; - const map = await converter.calculateConversion(fromFullName, toFullName); - const actual = map.evaluate(test.Input); - expect( - almostEqual(test.Expect, actual, almostEqual.FLT_EPSILON, almostEqual.FLT_EPSILON), - `${test.Input} ${test.From} in ${test.To} should be ${test.Expect} - and not ${actual} error = ${Math.abs(test.Expect - actual)} > ${almostEqual.FLT_EPSILON}` - ).to.be.true; - }); - }); - - it("should throw when schema name is not in context", async () => { - const converter = new UnitConverter(context); - try { - await converter.calculateConversion("MockSchema:CM", "SIUnits:M"); - } catch (err) { - expect(err).to.be.an("error"); - expect(err.message).to.equal("Cannot find from's and/or to's schema"); - } - try { - await converter.calculateConversion("SIUnits:M", "MockSchema:CM"); - } catch (err) { - expect(err).to.be.an("error"); - expect(err.message).to.equal("Cannot find from's and/or to's schema"); - } - }); - - it("should throw when schema item is not in schema ", async () => { - const converter = new UnitConverter(context); - try { - await converter.calculateConversion("SIUnits:MockUnit", "MetricUnits:CM"); - } catch (err) { - expect(err).to.be.an("error"); - expect(err.message).to.equal("Cannot find schema item"); - } - try { - await converter.calculateConversion("MetricUnits:CM", "SIUnits:MockUnit"); - } catch (err) { - expect(err).to.be.an("error"); - expect(err.message).to.equal("Cannot find schema item"); - } - }); -}); diff --git a/core/ecschema-metadata/test/UnitConversion/DeserializeSchema.ts b/core/ecschema-metadata/test/UnitConversion/DeserializeSchema.ts deleted file mode 100644 index 3c58b4ddc3d..00000000000 --- a/core/ecschema-metadata/test/UnitConversion/DeserializeSchema.ts +++ /dev/null @@ -1,18 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Bentley Systems, Incorporated. All rights reserved. -* See LICENSE.md in the project root for license terms and full copyright notice. -*--------------------------------------------------------------------------------------------*/ -import { Schema, SchemaContext, SchemaReadHelper, XmlParser } from "../../src/ecschema-metadata"; -import { DOMParser } from "xmldom"; - -export function deserializeXml( - context: SchemaContext, - schemaXml: string -): Schema { - let schema: Schema = new Schema(context); - const parser = new DOMParser(); - const document = parser.parseFromString(schemaXml, "application/xml"); - const reader = new SchemaReadHelper(XmlParser, context); - schema = reader.readSchemaSync(schema, document); - return schema; -} diff --git a/core/ecschema-metadata/test/UnitConversion/Parser.test.ts b/core/ecschema-metadata/test/UnitConversion/Parser.test.ts deleted file mode 100644 index d2be082d928..00000000000 --- a/core/ecschema-metadata/test/UnitConversion/Parser.test.ts +++ /dev/null @@ -1,589 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Bentley Systems, Incorporated. All rights reserved. -* See LICENSE.md in the project root for license terms and full copyright notice. -*--------------------------------------------------------------------------------------------*/ -import { expect } from "chai"; -import { parseDefinition, DefinitionFragment } from "../../src/UnitConversion/Parser"; - -describe("DefinitionParser tests", () => { - const definitionsToTest: string[] = [ - "NUMBER", - "NUMBER(2)", - "NUMBER(-1)", - "[NUMBER]", - "FORCE*LENGTH", - "WORK*TIME(-1)", - "LENGTH*LENGTH(-1)*TEMPERATURE_CHANGE(-1)", - "[PI]*RAD", - "BTU*IN*FT(-2)*HR(-1)*DELTA_FAHRENHEIT(-1)", - "M(3)*M(-3)", - "[PI](2)*[PI](-2)*[PI](2)*[PI](-2)*[PI](2)*[PI](-2)", - "BTU(-1)*[PI](2)*HR*[ONE]*TEMPERATURE_CHANGE(1)", - "alias:NUMBER", - "alias:NUMBER(2)", - "alias:NUMBER(-1)", - "[alias:NUMBER]", - "alias:FORCE*alias:LENGTH", - "alias:WORK*alias:TIME(-1)", - "alias:LENGTH*alias:LENGTH(-1)*alias:TEMPERATURE_CHANGE(-1)", - "[alias:PI]*alias:RAD", - "alias:BTU*alias:IN*alias:FT(-2)*alias:HR(-1)*alias:DELTA_FAHRENHEIT(-1)", - "alias:M(3)*alias:M(-3)", - "[alias:PI](2)*[alias:PI](-2)*[alias:PI](2)*[alias:PI](-2)*[alias:PI](2)*[alias:PI](-2)", - "alias:BTU(-1)*[alias:PI](2)*alias:HR*[alias:ONE]*alias:TEMPERATURE_CHANGE(1)", - ]; - - type KeyValuePair = [string, DefinitionFragment]; - - const expectedData: { [key: string]: KeyValuePair[] } = { - NUMBER: [ - [ - "NUMBER", - { - name: "NUMBER", - exponent: 1, - constant: false, - }, - ], - ], - "NUMBER(2)": [ - [ - "NUMBER", - { - name: "NUMBER", - exponent: 2, - constant: false, - }, - ], - ], - "NUMBER(-1)": [ - [ - "NUMBER", - { - name: "NUMBER", - exponent: -1, - constant: false, - }, - ], - ], - "[NUMBER]": [ - [ - "NUMBER", - { - name: "NUMBER", - exponent: 1, - constant: true, - }, - ], - ], - "FORCE*LENGTH": [ - [ - "FORCE", - { - name: "FORCE", - exponent: 1, - constant: false, - }, - ], - [ - "LENGTH", - { - name: "LENGTH", - exponent: 1, - constant: false, - }, - ], - ], - "WORK*TIME(-1)": [ - [ - "WORK", - { - name: "WORK", - exponent: 1, - constant: false, - }, - ], - [ - "TIME", - { - name: "TIME", - exponent: -1, - constant: false, - }, - ], - ], - "LENGTH*LENGTH(-1)*TEMPERATURE_CHANGE(-1)": [ - [ - "LENGTH", - { - name: "LENGTH", - exponent: 0, - constant: false, - }, - ], - [ - "TEMPERATURE_CHANGE", - { - name: "TEMPERATURE_CHANGE", - exponent: -1, - constant: false, - }, - ], - ], - "[PI]*RAD": [ - [ - "PI", - { - name: "PI", - exponent: 1, - constant: true, - }, - ], - [ - "RAD", - { - name: "RAD", - exponent: 1, - constant: false, - }, - ], - ], - "BTU*IN*FT(-2)*HR(-1)*DELTA_FAHRENHEIT(-1)": [ - [ - "BTU", - { - name: "BTU", - exponent: 1, - constant: false, - }, - ], - [ - "IN", - { - name: "IN", - exponent: 1, - constant: false, - }, - ], - [ - "FT", - { - name: "FT", - exponent: -2, - constant: false, - }, - ], - [ - "HR", - { - name: "HR", - exponent: -1, - constant: false, - }, - ], - [ - "DELTA_FAHRENHEIT", - { - name: "DELTA_FAHRENHEIT", - exponent: -1, - constant: false, - }, - ], - ], - "M(3)*M(-3)": [ - [ - "M", - { - name: "M", - exponent: 0, - constant: false, - }, - ], - ], - "[PI](2)*[PI](-2)*[PI](2)*[PI](-2)*[PI](2)*[PI](-2)": [ - [ - "PI", - { - name: "PI", - exponent: 0, - constant: true, - }, - ], - ], - "BTU(-1)*[PI](2)*HR*[ONE]*TEMPERATURE_CHANGE(1)": [ - [ - "BTU", - { - name: "BTU", - exponent: -1, - constant: false, - }, - ], - [ - "PI", - { - name: "PI", - exponent: 2, - constant: true, - }, - ], - [ - "HR", - { - name: "HR", - exponent: 1, - constant: false, - }, - ], - [ - "ONE", - { - name: "ONE", - exponent: 1, - constant: true, - }, - ], - [ - "TEMPERATURE_CHANGE", - { - name: "TEMPERATURE_CHANGE", - exponent: 1, - constant: false, - }, - ], - ], - "alias:NUMBER": [ - [ - "alias:NUMBER", - { - name: "alias:NUMBER", - exponent: 1, - constant: false, - }, - ], - ], - "alias:NUMBER(2)": [ - [ - "alias:NUMBER", - { - name: "alias:NUMBER", - exponent: 2, - constant: false, - }, - ], - ], - "alias:NUMBER(-1)": [ - [ - "alias:NUMBER", - { - name: "alias:NUMBER", - exponent: -1, - constant: false, - }, - ], - ], - "[alias:NUMBER]": [ - [ - "alias:NUMBER", - { - name: "alias:NUMBER", - exponent: 1, - constant: true, - }, - ], - ], - "alias:FORCE*alias:LENGTH": [ - [ - "alias:FORCE", - { - name: "alias:FORCE", - exponent: 1, - constant: false, - }, - ], - [ - "alias:LENGTH", - { - name: "alias:LENGTH", - exponent: 1, - constant: false, - }, - ], - ], - "alias:WORK*alias:TIME(-1)": [ - [ - "alias:WORK", - { - name: "alias:WORK", - exponent: 1, - constant: false, - }, - ], - [ - "alias:TIME", - { - name: "alias:TIME", - exponent: -1, - constant: false, - }, - ], - ], - "alias:LENGTH*alias:LENGTH(-1)*alias:TEMPERATURE_CHANGE(-1)": [ - [ - "alias:LENGTH", - { - name: "alias:LENGTH", - exponent: 0, - constant: false, - }, - ], - [ - "alias:TEMPERATURE_CHANGE", - { - name: "alias:TEMPERATURE_CHANGE", - exponent: -1, - constant: false, - }, - ], - ], - "[alias:PI]*alias:RAD": [ - [ - "alias:PI", - { - name: "alias:PI", - exponent: 1, - constant: true, - }, - ], - [ - "alias:RAD", - { - name: "alias:RAD", - exponent: 1, - constant: false, - }, - ], - ], - "alias:BTU*alias:IN*alias:FT(-2)*alias:HR(-1)*alias:DELTA_FAHRENHEIT(-1)": [ - [ - "alias:BTU", - { - name: "alias:BTU", - exponent: 1, - constant: false, - }, - ], - [ - "alias:IN", - { - name: "alias:IN", - exponent: 1, - constant: false, - }, - ], - [ - "alias:FT", - { - name: "alias:FT", - exponent: -2, - constant: false, - }, - ], - [ - "alias:HR", - { - name: "alias:HR", - exponent: -1, - constant: false, - }, - ], - [ - "alias:DELTA_FAHRENHEIT", - { - name: "alias:DELTA_FAHRENHEIT", - exponent: -1, - constant: false, - }, - ], - ], - "alias:M(3)*alias:M(-3)": [ - [ - "alias:M", - { - name: "alias:M", - exponent: 0, - constant: false, - }, - ], - ], - "[alias:PI](2)*[alias:PI](-2)*[alias:PI](2)*[alias:PI](-2)*[alias:PI](2)*[alias:PI](-2)": [ - [ - "alias:PI", - { - name: "alias:PI", - exponent: 0, - constant: true, - }, - ], - ], - "alias:BTU(-1)*[alias:PI](2)*alias:HR*[alias:ONE]*alias:TEMPERATURE_CHANGE(1)": [ - [ - "alias:BTU", - { - name: "alias:BTU", - exponent: -1, - constant: false, - }, - ], - [ - "alias:PI", - { - name: "alias:PI", - exponent: 2, - constant: true, - }, - ], - [ - "alias:HR", - { - name: "alias:HR", - exponent: 1, - constant: false, - }, - ], - [ - "alias:ONE", - { - name: "alias:ONE", - exponent: 1, - constant: true, - }, - ], - [ - "alias:TEMPERATURE_CHANGE", - { - name: "alias:TEMPERATURE_CHANGE", - exponent: 1, - constant: false, - }, - ], - ], - }; - - describe("parsing individual tokens", () => { - it("all capture groups provided", () => { - const definition = "[TEST](-3)"; - const data = [ - [ - "TEST", - { - name: "TEST", - exponent: -3, - constant: true, - }, - ], - ]; - - expect([...parseDefinition(definition)]).to.deep.equal(data); - }); - - it("with namespace, all capture groups provided", () => { - const definition = "[alias:TEST](-3)"; - const data = [ - [ - "alias:TEST", - { - name: "alias:TEST", - exponent: -3, - constant: true, - }, - ], - ]; - - expect([...parseDefinition(definition)]).to.deep.equal(data); - }); - - it("no brackets, exponent provided", () => { - const definition = "TEST(-3)"; - const data = [ - [ - "TEST", - { - name: "TEST", - exponent: -3, - constant: false, - }, - ], - ]; - - expect([...parseDefinition(definition)]).to.deep.equal(data); - }); - - it("singular constant/unit/phenomenon provided", () => { - // Unit and Phenomenon test, units and phenomena are not wrapped with brackets - let definition = "TEST"; - let data = [ - [ - "TEST", - { - name: "TEST", - exponent: 1, - constant: false, - }, - ], - ]; - - expect([...parseDefinition(definition)]).to.deep.equal(data); - - // Constant test, constants are wrapped with brackets - definition = "[TEST]"; - data = [ - [ - "TEST", - { - name: "TEST", - exponent: 1, - constant: true, - }, - ], - ]; - - expect([...parseDefinition(definition)]).to.deep.equal(data); - }); - }); - - function testTokenizations(definition: string) { - it(`tokenization of ${definition} matches expected data`, async () => { - expect([...parseDefinition(definition)]).to.have.deep.members( - expectedData[definition] - ); - }); - } - - function testInvalidToken(definition: string) { - it(`invalid definition ${definition} throws`, async () => { - expect(() => [...parseDefinition(definition)]).to.throw(); - }); - } - - describe("parsing correctly-formed definitions", () => { - for (const definition of definitionsToTest) { - testTokenizations(definition); - } - }); - - describe("parsing malformed definitions", () => { - testInvalidToken(""); - testInvalidToken("TEST\t"); - testInvalidToken("TEST**TEST"); - testInvalidToken("[](-1)"); - testInvalidToken("TEST()"); - testInvalidToken("TEST(--1)"); - testInvalidToken("[TEST(-1)"); - testInvalidToken("[TEST](1"); - testInvalidToken("TEST*[TEST](1"); - testInvalidToken("TEST*[TEST](1)*[TEST]-1"); - testInvalidToken("[Test](1)*"); - testInvalidToken("TEST(1)[TEST]"); - }); -}); diff --git a/core/ecschema-metadata/test/UnitConversion/assets/CrossSchemaTests.json b/core/ecschema-metadata/test/UnitConversion/assets/CrossSchemaTests.json deleted file mode 100644 index d6e604782c5..00000000000 --- a/core/ecschema-metadata/test/UnitConversion/assets/CrossSchemaTests.json +++ /dev/null @@ -1,322 +0,0 @@ -[ - { - "FromSchema": "USUnits", - "From": "YRD", - "ToSchema": "USUnits", - "To": "FT", - "Input": 42.42, - "Expect": 127.26 - }, - { - "FromSchema": "USUnits", - "From": "FT", - "ToSchema": "USUnits", - "To": "YRD", - "Input": 42.42, - "Expect": 14.14 - }, - { - "FromSchema": "MetricUnits", - "From": "KM", - "ToSchema": "MetricUnits", - "To": "CM", - "Input": 42.42, - "Expect": 4242000 - }, - { - "FromSchema": "MetricUnits", - "From": "CM", - "ToSchema": "MetricUnits", - "To": "KM", - "Input": 42.42, - "Expect": 0.0004242 - }, - { - "FromSchema": "USUnits", - "From": "MILE", - "ToSchema": "MetricUnits", - "To": "DM", - "Input": 42.42, - "Expect": 682683.7248000001 - }, - { - "FromSchema": "MetricUnits", - "From": "DM", - "ToSchema": "USUnits", - "To": "MILE", - "Input": 42.42, - "Expect": 0.0026358565974707706 - }, - { - "FromSchema": "USUnits", - "From": "US_SURVEY_MILE", - "ToSchema": "SIUnits", - "To": "M", - "Input": 42.42, - "Expect": 68268.50901701803 - }, - { - "FromSchema": "SIUnits", - "From": "M", - "ToSchema": "USUnits", - "To": "US_SURVEY_MILE", - "Input": 42.42, - "Expect": 0.026358513257575752 - }, - { - "FromSchema": "USUnits", - "From": "SQ_MILE", - "ToSchema": "USUnits", - "To": "SQ_FT", - "Input": 42.42, - "Expect": 1182601728.0000005 - }, - { - "FromSchema": "USUnits", - "From": "SQ_FT", - "ToSchema": "USUnits", - "To": "SQ_MILE", - "Input": 42.42, - "Expect": 0.0000015216081267217627 - }, - { - "FromSchema": "MetricUnits", - "From": "SQ_UM", - "ToSchema": "MetricUnits", - "To": "SQ_MM", - "Input": 42.42, - "Expect": 4.242e-5 - }, - { - "FromSchema": "MetricUnits", - "From": "SQ_MM", - "ToSchema": "MetricUnits", - "To": "SQ_UM", - "Input": 42.42, - "Expect": 42420000 - }, - { - "FromSchema": "USUnits", - "From": "SQ_US_SURVEY_YRD", - "ToSchema": "SIUnits", - "To": "SQ_M", - "Input": 42.42, - "Expect": 35.46866448571609 - }, - { - "FromSchema": "SIUnits", - "From": "SQ_M", - "ToSchema": "USUnits", - "To": "SQ_US_SURVEY_YRD", - "Input": 42.42, - "Expect": 50.733694828703676 - }, - { - "FromSchema": "USUnits", - "From": "SQ_MILE", - "ToSchema": "MetricUnits", - "To": "SQ_KM", - "Input": 42.42, - "Expect": 109.86729564045314 - }, - { - "FromSchema": "MetricUnits", - "From": "SQ_KM", - "ToSchema": "USUnits", - "To": "SQ_MILE", - "Input": 42.42, - "Expect": 16.378453565370553 - }, - { - "FromSchema": "USUnits", - "From": "FT_PER_THOUSAND_FOOT", - "ToSchema": "USUnits", - "To": "FT_PER_FT", - "Input": 42.42, - "Expect": 0.04242 - }, - { - "FromSchema": "USUnits", - "From": "FT_PER_FT", - "ToSchema": "USUnits", - "To": "FT_PER_THOUSAND_FOOT", - "Input": 42.42, - "Expect": 42420 - }, - { - "FromSchema": "USUnits", - "From": "FT_PER_FT", - "ToSchema": "USUnits", - "To": "IN_PER_FT", - "Input": 42.42, - "Expect": 509.04 - }, - { - "FromSchema": "USUnits", - "From": "IN_PER_FT", - "ToSchema": "USUnits", - "To": "FT_PER_FT", - "Input": 42.42, - "Expect": 3.535 - }, - { - "FromSchema": "USUnits", - "From": "FT_PER_MILE", - "ToSchema": "USUnits", - "To": "IN_PER_FT", - "Input": 42.42, - "Expect": 0.09640909090909093 - }, - { - "FromSchema": "USUnits", - "From": "IN_PER_FT", - "ToSchema": "USUnits", - "To": "FT_PER_MILE", - "Input": 42.42, - "Expect": 18664.8 - }, - { - "FromSchema": "MetricUnits", - "From": "CM_PER_M", - "ToSchema": "MetricUnits", - "To": "M_PER_KM", - "Input": 42.42, - "Expect": 424.2 - }, - { - "FromSchema": "MetricUnits", - "From": "M_PER_KM", - "ToSchema": "MetricUnits", - "To": "CM_PER_M", - "Input": 42.42, - "Expect": 4.242 - }, - { - "FromSchema": "USUnits", - "From": "FT_PER_IN", - "ToSchema": "MetricUnits", - "To": "M_PER_KM", - "Input": 42.42, - "Expect": 509040 - }, - { - "FromSchema": "MetricUnits", - "From": "M_PER_KM", - "ToSchema": "USUnits", - "To": "FT_PER_IN", - "Input": 42.42, - "Expect": 0.003535 - }, - { - "FromSchema": "USUnits", - "From": "FT_PER_IN", - "ToSchema": "SIUnits", - "To": "M_PER_M", - "Input": 42.42, - "Expect": 509.04 - }, - { - "FromSchema": "SIUnits", - "From": "M_PER_M", - "ToSchema": "USUnits", - "To": "FT_PER_IN", - "Input": 42.42, - "Expect": 3.535 - }, - { - "FromSchema": "MetricUnits", - "From": "MM_PER_M", - "ToSchema": "SIUnits", - "To": "M_PER_M", - "Input": 42.42, - "Expect": 0.04242 - }, - { - "FromSchema": "SIUnits", - "From": "M_PER_M", - "ToSchema": "MetricUnits", - "To": "MM_PER_M", - "Input": 42.42, - "Expect": 42420 - }, - { - "FromSchema": "USUnits", - "From": "FAHRENHEIT", - "ToSchema": "USUnits", - "To": "RANKINE", - "Input": 42.42, - "Expect": 502.09 - }, - { - "FromSchema": "USUnits", - "From": "RANKINE", - "ToSchema": "USUnits", - "To": "FAHRENHEIT", - "Input": 42.42, - "Expect": -417.24999999999994 - }, - { - "FromSchema": "USUnits", - "From": "FAHRENHEIT", - "ToSchema": "MetricUnits", - "To": "CELSIUS", - "Input": 42.42, - "Expect": 5.7888888888888985 - }, - { - "FromSchema": "MetricUnits", - "From": "CELSIUS", - "ToSchema": "USUnits", - "To": "FAHRENHEIT", - "Input": 42.42, - "Expect": 108.35600000000001 - }, - { - "FromSchema": "USUnits", - "From": "FAHRENHEIT", - "ToSchema": "SIUnits", - "To": "K", - "Input": 42.42, - "Expect": 278.93888888888887 - }, - { - "FromSchema": "SIUnits", - "From": "K", - "ToSchema": "USUnits", - "To": "FAHRENHEIT", - "Input": 42.42, - "Expect": -383.31399999999996 - }, - { - "FromSchema": "SIUnits", - "From": "TWO_PI", - "ToSchema": "SIUnits", - "To": "QUARTER_PI", - "Input": 42.42, - "Expect": 339.36 - }, - { - "FromSchema": "SIUnits", - "From": "QUARTER_PI", - "ToSchema": "SIUnits", - "To": "TWO_PI", - "Input": 42.42, - "Expect": 5.3025 - }, - { - "FromSchema": "SIUnits", - "From": "HALF_PI", - "ToSchema": "SIUnits", - "To": "PI", - "Input": 42.42, - "Expect": 21.21 - }, - { - "FromSchema": "SIUnits", - "From": "PI", - "ToSchema": "SIUnits", - "To": "HALF_PI", - "Input": 42.42, - "Expect": 84.84 - } -] diff --git a/core/ecschema-metadata/test/UnitConversion/assets/UnitTests.json b/core/ecschema-metadata/test/UnitConversion/assets/UnitTests.json deleted file mode 100644 index fa5db2df345..00000000000 --- a/core/ecschema-metadata/test/UnitConversion/assets/UnitTests.json +++ /dev/null @@ -1,9038 +0,0 @@ -[ - { - "From": "SQ_FT", - "Input": 42.42, - "To": "SQ_M", - "Expect": 3.9409469568 - }, - { - "From": "SQ_FT", - "Input": 42.42, - "To": "SQ_MM", - "Expect": 3940946.9568 - }, - { - "From": "SQ_FT", - "Input": 42.42, - "To": "SQ_UM", - "Expect": 3940946956800 - }, - { - "From": "SQ_FT", - "Input": 42.42, - "To": "SQ_CM", - "Expect": 39409.46956799999 - }, - { - "From": "SQ_FT", - "Input": 42.42, - "To": "SQ_DM", - "Expect": 394.0946956799999 - }, - { - "From": "SQ_FT", - "Input": 42.42, - "To": "SQ_KM", - "Expect": 0.0000039409469568 - }, - { - "From": "SQ_FT", - "Input": 42.42, - "To": "ARE", - "Expect": 0.039409469568 - }, - { - "From": "SQ_FT", - "Input": 42.42, - "To": "HECTARE", - "Expect": 0.00039409469568 - }, - { - "From": "SQ_FT", - "Input": 42.42, - "To": "SQ_IN", - "Expect": 6108.4800000000005 - }, - { - "From": "SQ_FT", - "Input": 42.42, - "To": "SQ_FT", - "Expect": 42.42 - }, - { - "From": "SQ_FT", - "Input": 42.42, - "To": "THOUSAND_SQ_FT", - "Expect": 0.04242 - }, - { - "From": "SQ_FT", - "Input": 42.42, - "To": "SQ_YRD", - "Expect": 4.713333333333333 - }, - { - "From": "SQ_FT", - "Input": 42.42, - "To": "SQ_MILE", - "Expect": 0.0000015216081267217631 - }, - { - "From": "SQ_FT", - "Input": 42.42, - "To": "SQ_CHAIN", - "Expect": 0.009738292011019284 - }, - { - "From": "SQ_FT", - "Input": 42.42, - "To": "ACRE", - "Expect": 0.0009738292011019284 - }, - { - "From": "SQ_FT", - "Input": 42.42, - "To": "SQ_US_SURVEY_IN", - "Expect": 6108.455566104434 - }, - { - "From": "SQ_FT", - "Input": 42.42, - "To": "SQ_US_SURVEY_FT", - "Expect": 42.41983032016968 - }, - { - "From": "SQ_FT", - "Input": 42.42, - "To": "SQ_US_SURVEY_YRD", - "Expect": 4.713314480018854 - }, - { - "From": "SQ_FT", - "Input": 42.42, - "To": "SQ_US_SURVEY_MILE", - "Expect": 0.0000015216020402953428 - }, - { - "From": "SQ_FT", - "Input": 42.42, - "To": "SQ_US_SURVEY_CHAIN", - "Expect": 0.009738253057890193 - }, - { - "From": "SQ_FT", - "Input": 42.42, - "To": "US_SURVEY_ACRE", - "Expect": 0.0009738253057890192 - }, - { - "From": "THOUSAND_SQ_FT", - "Input": 42.42, - "To": "SQ_M", - "Expect": 3940.9469568 - }, - { - "From": "THOUSAND_SQ_FT", - "Input": 42.42, - "To": "SQ_UM", - "Expect": 3940946956800000 - }, - { - "From": "THOUSAND_SQ_FT", - "Input": 42.42, - "To": "SQ_MM", - "Expect": 3940946956.8 - }, - { - "From": "THOUSAND_SQ_FT", - "Input": 42.42, - "To": "SQ_CM", - "Expect": 39409469.568 - }, - { - "From": "THOUSAND_SQ_FT", - "Input": 42.42, - "To": "SQ_DM", - "Expect": 394094.69568 - }, - { - "From": "THOUSAND_SQ_FT", - "Input": 42.42, - "To": "SQ_KM", - "Expect": 0.003940946956799999 - }, - { - "From": "THOUSAND_SQ_FT", - "Input": 42.42, - "To": "ARE", - "Expect": 39.409469568 - }, - { - "From": "THOUSAND_SQ_FT", - "Input": 42.42, - "To": "HECTARE", - "Expect": 0.39409469568 - }, - { - "From": "THOUSAND_SQ_FT", - "Input": 42.42, - "To": "SQ_IN", - "Expect": 6108480 - }, - { - "From": "THOUSAND_SQ_FT", - "Input": 42.42, - "To": "SQ_FT", - "Expect": 42420 - }, - { - "From": "THOUSAND_SQ_FT", - "Input": 42.42, - "To": "THOUSAND_SQ_FT", - "Expect": 42.42 - }, - { - "From": "THOUSAND_SQ_FT", - "Input": 42.42, - "To": "SQ_YRD", - "Expect": 4713.333333333334 - }, - { - "From": "THOUSAND_SQ_FT", - "Input": 42.42, - "To": "SQ_MILE", - "Expect": 0.001521608126721763 - }, - { - "From": "THOUSAND_SQ_FT", - "Input": 42.42, - "To": "SQ_CHAIN", - "Expect": 9.738292011019285 - }, - { - "From": "THOUSAND_SQ_FT", - "Input": 42.42, - "To": "ACRE", - "Expect": 0.9738292011019284 - }, - { - "From": "THOUSAND_SQ_FT", - "Input": 42.42, - "To": "SQ_US_SURVEY_IN", - "Expect": 6108455.5661044335 - }, - { - "From": "THOUSAND_SQ_FT", - "Input": 42.42, - "To": "SQ_US_SURVEY_FT", - "Expect": 42419.830320169676 - }, - { - "From": "THOUSAND_SQ_FT", - "Input": 42.42, - "To": "SQ_US_SURVEY_YRD", - "Expect": 4713.314480018853 - }, - { - "From": "THOUSAND_SQ_FT", - "Input": 42.42, - "To": "SQ_US_SURVEY_MILE", - "Expect": 0.0015216020402953425 - }, - { - "From": "THOUSAND_SQ_FT", - "Input": 42.42, - "To": "SQ_US_SURVEY_CHAIN", - "Expect": 9.738253057890192 - }, - { - "From": "THOUSAND_SQ_FT", - "Input": 42.42, - "To": "US_SURVEY_ACRE", - "Expect": 0.9738253057890192 - }, - { - "From": "SQ_YRD", - "Input": 42.42, - "To": "SQ_M", - "Expect": 35.468522611199994 - }, - { - "From": "SQ_YRD", - "Input": 42.42, - "To": "SQ_UM", - "Expect": 35468522611200 - }, - { - "From": "SQ_YRD", - "Input": 42.42, - "To": "SQ_MM", - "Expect": 35468522.6112 - }, - { - "From": "SQ_YRD", - "Input": 42.42, - "To": "SQ_CM", - "Expect": 354685.22611199995 - }, - { - "From": "SQ_YRD", - "Input": 42.42, - "To": "SQ_DM", - "Expect": 3546.8522611199987 - }, - { - "From": "SQ_YRD", - "Input": 42.42, - "To": "SQ_KM", - "Expect": 0.000035468522611199996 - }, - { - "From": "SQ_YRD", - "Input": 42.42, - "To": "ARE", - "Expect": 0.35468522611199993 - }, - { - "From": "SQ_YRD", - "Input": 42.42, - "To": "HECTARE", - "Expect": 0.0035468522611199994 - }, - { - "From": "SQ_YRD", - "Input": 42.42, - "To": "SQ_IN", - "Expect": 54976.32 - }, - { - "From": "SQ_YRD", - "Input": 42.42, - "To": "SQ_FT", - "Expect": 381.78000000000003 - }, - { - "From": "SQ_YRD", - "Input": 42.42, - "To": "THOUSAND_SQ_FT", - "Expect": 0.38178 - }, - { - "From": "SQ_YRD", - "Input": 42.42, - "To": "SQ_YRD", - "Expect": 42.42 - }, - { - "From": "SQ_YRD", - "Input": 42.42, - "To": "SQ_MILE", - "Expect": 0.00001369447314049587 - }, - { - "From": "SQ_YRD", - "Input": 42.42, - "To": "SQ_CHAIN", - "Expect": 0.08764462809917356 - }, - { - "From": "SQ_YRD", - "Input": 42.42, - "To": "ACRE", - "Expect": 0.008764462809917356 - }, - { - "From": "SQ_YRD", - "Input": 42.42, - "To": "SQ_US_SURVEY_IN", - "Expect": 54976.1000949399 - }, - { - "From": "SQ_YRD", - "Input": 42.42, - "To": "SQ_US_SURVEY_FT", - "Expect": 381.77847288152714 - }, - { - "From": "SQ_YRD", - "Input": 42.42, - "To": "SQ_US_SURVEY_YRD", - "Expect": 42.41983032016968 - }, - { - "From": "SQ_YRD", - "Input": 42.42, - "To": "SQ_US_SURVEY_MILE", - "Expect": 0.000013694418362658081 - }, - { - "From": "SQ_YRD", - "Input": 42.42, - "To": "SQ_US_SURVEY_CHAIN", - "Expect": 0.08764427752101174 - }, - { - "From": "SQ_YRD", - "Input": 42.42, - "To": "US_SURVEY_ACRE", - "Expect": 0.008764427752101173 - }, - { - "From": "SQ_MILE", - "Input": 42.42, - "To": "SQ_M", - "Expect": 109867295.64045312 - }, - { - "From": "SQ_MILE", - "Input": 42.42, - "To": "SQ_UM", - "Expect": 109867295640453100000 - }, - { - "From": "SQ_MILE", - "Input": 42.42, - "To": "SQ_MM", - "Expect": 109867295640453.12 - }, - { - "From": "SQ_MILE", - "Input": 42.42, - "To": "SQ_CM", - "Expect": 1098672956404.5311 - }, - { - "From": "SQ_MILE", - "Input": 42.42, - "To": "SQ_DM", - "Expect": 10986729564.04531 - }, - { - "From": "SQ_MILE", - "Input": 42.42, - "To": "SQ_KM", - "Expect": 109.86729564045312 - }, - { - "From": "SQ_MILE", - "Input": 42.42, - "To": "ARE", - "Expect": 1098672.9564045311 - }, - { - "From": "SQ_MILE", - "Input": 42.42, - "To": "HECTARE", - "Expect": 10986.72956404531 - }, - { - "From": "SQ_MILE", - "Input": 42.42, - "To": "SQ_IN", - "Expect": 170294648832 - }, - { - "From": "SQ_MILE", - "Input": 42.42, - "To": "SQ_FT", - "Expect": 1182601728 - }, - { - "From": "SQ_MILE", - "Input": 42.42, - "To": "THOUSAND_SQ_FT", - "Expect": 1182601.7280000001 - }, - { - "From": "SQ_MILE", - "Input": 42.42, - "To": "SQ_YRD", - "Expect": 131400192 - }, - { - "From": "SQ_MILE", - "Input": 42.42, - "To": "SQ_MILE", - "Expect": 42.42 - }, - { - "From": "SQ_MILE", - "Input": 42.42, - "To": "SQ_CHAIN", - "Expect": 271488 - }, - { - "From": "SQ_MILE", - "Input": 42.42, - "To": "ACRE", - "Expect": 27148.800000000003 - }, - { - "From": "SQ_MILE", - "Input": 42.42, - "To": "SQ_US_SURVEY_IN", - "Expect": 170293967654.08585 - }, - { - "From": "SQ_MILE", - "Input": 42.42, - "To": "SQ_US_SURVEY_FT", - "Expect": 1182596997.5978184 - }, - { - "From": "SQ_MILE", - "Input": 42.42, - "To": "SQ_US_SURVEY_YRD", - "Expect": 131399666.39975758 - }, - { - "From": "SQ_MILE", - "Input": 42.42, - "To": "SQ_US_SURVEY_MILE", - "Expect": 42.41983032016968 - }, - { - "From": "SQ_MILE", - "Input": 42.42, - "To": "SQ_US_SURVEY_CHAIN", - "Expect": 271486.9140490859 - }, - { - "From": "SQ_MILE", - "Input": 42.42, - "To": "US_SURVEY_ACRE", - "Expect": 27148.691404908597 - }, - { - "From": "SQ_CHAIN", - "Input": 42.42, - "To": "SQ_M", - "Expect": 17166.7649438208 - }, - { - "From": "SQ_CHAIN", - "Input": 42.42, - "To": "SQ_UM", - "Expect": 17166764943820800 - }, - { - "From": "SQ_CHAIN", - "Input": 42.42, - "To": "SQ_MM", - "Expect": 17166764943.820799 - }, - { - "From": "SQ_CHAIN", - "Input": 42.42, - "To": "SQ_CM", - "Expect": 171667649.43820798 - }, - { - "From": "SQ_CHAIN", - "Input": 42.42, - "To": "SQ_DM", - "Expect": 1716676.49438208 - }, - { - "From": "SQ_CHAIN", - "Input": 42.42, - "To": "SQ_KM", - "Expect": 0.0171667649438208 - }, - { - "From": "SQ_CHAIN", - "Input": 42.42, - "To": "ARE", - "Expect": 171.66764943820803 - }, - { - "From": "SQ_CHAIN", - "Input": 42.42, - "To": "HECTARE", - "Expect": 1.71667649438208 - }, - { - "From": "SQ_CHAIN", - "Input": 42.42, - "To": "SQ_IN", - "Expect": 26608538.880000003 - }, - { - "From": "SQ_CHAIN", - "Input": 42.42, - "To": "SQ_FT", - "Expect": 184781.52000000002 - }, - { - "From": "SQ_CHAIN", - "Input": 42.42, - "To": "THOUSAND_SQ_FT", - "Expect": 184.78152 - }, - { - "From": "SQ_CHAIN", - "Input": 42.42, - "To": "SQ_YRD", - "Expect": 20531.280000000002 - }, - { - "From": "SQ_CHAIN", - "Input": 42.42, - "To": "SQ_MILE", - "Expect": 0.006628125 - }, - { - "From": "SQ_CHAIN", - "Input": 42.42, - "To": "SQ_CHAIN", - "Expect": 42.42 - }, - { - "From": "SQ_CHAIN", - "Input": 42.42, - "To": "ACRE", - "Expect": 4.242 - }, - { - "From": "SQ_CHAIN", - "Input": 42.42, - "To": "SQ_US_SURVEY_IN", - "Expect": 26608432.445950914 - }, - { - "From": "SQ_CHAIN", - "Input": 42.42, - "To": "SQ_US_SURVEY_FT", - "Expect": 184780.7808746591 - }, - { - "From": "SQ_CHAIN", - "Input": 42.42, - "To": "SQ_US_SURVEY_YRD", - "Expect": 20531.197874962123 - }, - { - "From": "SQ_CHAIN", - "Input": 42.42, - "To": "SQ_US_SURVEY_MILE", - "Expect": 0.006628098487526513 - }, - { - "From": "SQ_CHAIN", - "Input": 42.42, - "To": "SQ_US_SURVEY_CHAIN", - "Expect": 42.41983032016968 - }, - { - "From": "SQ_CHAIN", - "Input": 42.42, - "To": "US_SURVEY_ACRE", - "Expect": 4.241983032016969 - }, - { - "From": "ACRE", - "Input": 42.42, - "To": "SQ_M", - "Expect": 171667.649438208 - }, - { - "From": "ACRE", - "Input": 42.42, - "To": "SQ_UM", - "Expect": 171667649438208000 - }, - { - "From": "ACRE", - "Input": 42.42, - "To": "SQ_MM", - "Expect": 171667649438.20798 - }, - { - "From": "ACRE", - "Input": 42.42, - "To": "SQ_CM", - "Expect": 1716676494.3820798 - }, - { - "From": "ACRE", - "Input": 42.42, - "To": "SQ_DM", - "Expect": 17166764.943820797 - }, - { - "From": "ACRE", - "Input": 42.42, - "To": "SQ_KM", - "Expect": 0.171667649438208 - }, - { - "From": "ACRE", - "Input": 42.42, - "To": "ARE", - "Expect": 1716.67649438208 - }, - { - "From": "ACRE", - "Input": 42.42, - "To": "HECTARE", - "Expect": 17.166764943820798 - }, - { - "From": "ACRE", - "Input": 42.42, - "To": "SQ_IN", - "Expect": 266085388.8 - }, - { - "From": "ACRE", - "Input": 42.42, - "To": "SQ_FT", - "Expect": 1847815.2000000002 - }, - { - "From": "ACRE", - "Input": 42.42, - "To": "THOUSAND_SQ_FT", - "Expect": 1847.8152000000002 - }, - { - "From": "ACRE", - "Input": 42.42, - "To": "SQ_YRD", - "Expect": 205312.80000000002 - }, - { - "From": "ACRE", - "Input": 42.42, - "To": "SQ_MILE", - "Expect": 0.06628125 - }, - { - "From": "ACRE", - "Input": 42.42, - "To": "SQ_CHAIN", - "Expect": 424.20000000000005 - }, - { - "From": "ACRE", - "Input": 42.42, - "To": "ACRE", - "Expect": 42.42 - }, - { - "From": "ACRE", - "Input": 42.42, - "To": "SQ_US_SURVEY_IN", - "Expect": 266084324.45950913 - }, - { - "From": "ACRE", - "Input": 42.42, - "To": "SQ_US_SURVEY_FT", - "Expect": 1847807.8087465912 - }, - { - "From": "ACRE", - "Input": 42.42, - "To": "SQ_US_SURVEY_YRD", - "Expect": 205311.97874962122 - }, - { - "From": "ACRE", - "Input": 42.42, - "To": "SQ_US_SURVEY_MILE", - "Expect": 0.06628098487526513 - }, - { - "From": "ACRE", - "Input": 42.42, - "To": "SQ_US_SURVEY_CHAIN", - "Expect": 424.1983032016967 - }, - { - "From": "ACRE", - "Input": 42.42, - "To": "US_SURVEY_ACRE", - "Expect": 42.41983032016967 - }, - { - "From": "SQ_US_SURVEY_IN", - "Input": 42.42, - "To": "SQ_M", - "Expect": 0.027367796671077214 - }, - { - "From": "SQ_US_SURVEY_IN", - "Input": 42.42, - "To": "SQ_UM", - "Expect": 27367796671.077213 - }, - { - "From": "SQ_US_SURVEY_IN", - "Input": 42.42, - "To": "SQ_MM", - "Expect": 27367.796671077216 - }, - { - "From": "SQ_US_SURVEY_IN", - "Input": 42.42, - "To": "SQ_CM", - "Expect": 273.6779667107721 - }, - { - "From": "SQ_US_SURVEY_IN", - "Input": 42.42, - "To": "SQ_DM", - "Expect": 2.736779667107721 - }, - { - "From": "SQ_US_SURVEY_IN", - "Input": 42.42, - "To": "SQ_KM", - "Expect": 2.7367796671077217e-8 - }, - { - "From": "SQ_US_SURVEY_IN", - "Input": 42.42, - "To": "ARE", - "Expect": 0.00027367796671077214 - }, - { - "From": "SQ_US_SURVEY_IN", - "Input": 42.42, - "To": "HECTARE", - "Expect": 0.0000027367796671077212 - }, - { - "From": "SQ_US_SURVEY_IN", - "Input": 42.42, - "To": "SQ_IN", - "Expect": 42.42016968050905 - }, - { - "From": "SQ_US_SURVEY_IN", - "Input": 42.42, - "To": "SQ_FT", - "Expect": 0.2945845116702017 - }, - { - "From": "SQ_US_SURVEY_IN", - "Input": 42.42, - "To": "THOUSAND_SQ_FT", - "Expect": 0.0002945845116702017 - }, - { - "From": "SQ_US_SURVEY_IN", - "Input": 42.42, - "To": "SQ_YRD", - "Expect": 0.03273161240780019 - }, - { - "From": "SQ_US_SURVEY_IN", - "Input": 42.42, - "To": "SQ_MILE", - "Expect": 1.0566765369253676e-8 - }, - { - "From": "SQ_US_SURVEY_IN", - "Input": 42.42, - "To": "SQ_CHAIN", - "Expect": 0.00006762729836322353 - }, - { - "From": "SQ_US_SURVEY_IN", - "Input": 42.42, - "To": "ACRE", - "Expect": 0.000006762729836322352 - }, - { - "From": "SQ_US_SURVEY_IN", - "Input": 42.42, - "To": "SQ_US_SURVEY_IN", - "Expect": 42.42 - }, - { - "From": "SQ_US_SURVEY_IN", - "Input": 42.42, - "To": "SQ_US_SURVEY_FT", - "Expect": 0.2945833333333333 - }, - { - "From": "SQ_US_SURVEY_IN", - "Input": 42.42, - "To": "SQ_US_SURVEY_YRD", - "Expect": 0.03273148148148148 - }, - { - "From": "SQ_US_SURVEY_IN", - "Input": 42.42, - "To": "SQ_US_SURVEY_MILE", - "Expect": 1.0566723102234465e-8 - }, - { - "From": "SQ_US_SURVEY_IN", - "Input": 42.42, - "To": "SQ_US_SURVEY_CHAIN", - "Expect": 0.00006762702785430058 - }, - { - "From": "SQ_US_SURVEY_IN", - "Input": 42.42, - "To": "US_SURVEY_ACRE", - "Expect": 0.000006762702785430058 - }, - { - "From": "SQ_US_SURVEY_FT", - "Input": 42.42, - "To": "SQ_M", - "Expect": 3.940962720635119 - }, - { - "From": "SQ_US_SURVEY_FT", - "Input": 42.42, - "To": "SQ_UM", - "Expect": 3940962720635.119 - }, - { - "From": "SQ_US_SURVEY_FT", - "Input": 42.42, - "To": "SQ_MM", - "Expect": 3940962.720635119 - }, - { - "From": "SQ_US_SURVEY_FT", - "Input": 42.42, - "To": "SQ_CM", - "Expect": 39409.627206351186 - }, - { - "From": "SQ_US_SURVEY_FT", - "Input": 42.42, - "To": "SQ_DM", - "Expect": 394.09627206351183 - }, - { - "From": "SQ_US_SURVEY_FT", - "Input": 42.42, - "To": "SQ_KM", - "Expect": 0.000003940962720635118 - }, - { - "From": "SQ_US_SURVEY_FT", - "Input": 42.42, - "To": "ARE", - "Expect": 0.03940962720635119 - }, - { - "From": "SQ_US_SURVEY_FT", - "Input": 42.42, - "To": "HECTARE", - "Expect": 0.0003940962720635119 - }, - { - "From": "SQ_US_SURVEY_FT", - "Input": 42.42, - "To": "SQ_IN", - "Expect": 6108.504433993302 - }, - { - "From": "SQ_US_SURVEY_FT", - "Input": 42.42, - "To": "SQ_FT", - "Expect": 42.42016968050905 - }, - { - "From": "SQ_US_SURVEY_FT", - "Input": 42.42, - "To": "THOUSAND_SQ_FT", - "Expect": 0.042420169680509046 - }, - { - "From": "SQ_US_SURVEY_FT", - "Input": 42.42, - "To": "SQ_YRD", - "Expect": 4.7133521867232275 - }, - { - "From": "SQ_US_SURVEY_FT", - "Input": 42.42, - "To": "SQ_MILE", - "Expect": 0.0000015216142131725291 - }, - { - "From": "SQ_US_SURVEY_FT", - "Input": 42.42, - "To": "SQ_CHAIN", - "Expect": 0.009738330964304188 - }, - { - "From": "SQ_US_SURVEY_FT", - "Input": 42.42, - "To": "ACRE", - "Expect": 0.0009738330964304188 - }, - { - "From": "SQ_US_SURVEY_FT", - "Input": 42.42, - "To": "SQ_US_SURVEY_IN", - "Expect": 6108.4800000000005 - }, - { - "From": "SQ_US_SURVEY_FT", - "Input": 42.42, - "To": "SQ_US_SURVEY_FT", - "Expect": 42.42 - }, - { - "From": "SQ_US_SURVEY_FT", - "Input": 42.42, - "To": "SQ_US_SURVEY_YRD", - "Expect": 4.713333333333333 - }, - { - "From": "SQ_US_SURVEY_FT", - "Input": 42.42, - "To": "SQ_US_SURVEY_MILE", - "Expect": 0.0000015216081267217631 - }, - { - "From": "SQ_US_SURVEY_FT", - "Input": 42.42, - "To": "SQ_US_SURVEY_CHAIN", - "Expect": 0.009738292011019284 - }, - { - "From": "SQ_US_SURVEY_FT", - "Input": 42.42, - "To": "US_SURVEY_ACRE", - "Expect": 0.0009738292011019284 - }, - { - "From": "SQ_US_SURVEY_YRD", - "Input": 42.42, - "To": "SQ_M", - "Expect": 35.46866448571607 - }, - { - "From": "SQ_US_SURVEY_YRD", - "Input": 42.42, - "To": "SQ_UM", - "Expect": 35468664485716.07 - }, - { - "From": "SQ_US_SURVEY_YRD", - "Input": 42.42, - "To": "SQ_MM", - "Expect": 35468664.485716075 - }, - { - "From": "SQ_US_SURVEY_YRD", - "Input": 42.42, - "To": "SQ_CM", - "Expect": 354686.6448571607 - }, - { - "From": "SQ_US_SURVEY_YRD", - "Input": 42.42, - "To": "SQ_DM", - "Expect": 3546.8664485716063 - }, - { - "From": "SQ_US_SURVEY_YRD", - "Input": 42.42, - "To": "SQ_KM", - "Expect": 0.00003546866448571607 - }, - { - "From": "SQ_US_SURVEY_YRD", - "Input": 42.42, - "To": "ARE", - "Expect": 0.35468664485716067 - }, - { - "From": "SQ_US_SURVEY_YRD", - "Input": 42.42, - "To": "HECTARE", - "Expect": 0.0035468664485716075 - }, - { - "From": "SQ_US_SURVEY_YRD", - "Input": 42.42, - "To": "SQ_IN", - "Expect": 54976.53990593973 - }, - { - "From": "SQ_US_SURVEY_YRD", - "Input": 42.42, - "To": "SQ_FT", - "Expect": 381.7815271245815 - }, - { - "From": "SQ_US_SURVEY_YRD", - "Input": 42.42, - "To": "THOUSAND_SQ_FT", - "Expect": 0.38178152712458147 - }, - { - "From": "SQ_US_SURVEY_YRD", - "Input": 42.42, - "To": "SQ_YRD", - "Expect": 42.42016968050905 - }, - { - "From": "SQ_US_SURVEY_YRD", - "Input": 42.42, - "To": "SQ_MILE", - "Expect": 0.000013694527918552765 - }, - { - "From": "SQ_US_SURVEY_YRD", - "Input": 42.42, - "To": "SQ_CHAIN", - "Expect": 0.08764497867873769 - }, - { - "From": "SQ_US_SURVEY_YRD", - "Input": 42.42, - "To": "ACRE", - "Expect": 0.008764497867873769 - }, - { - "From": "SQ_US_SURVEY_YRD", - "Input": 42.42, - "To": "SQ_US_SURVEY_IN", - "Expect": 54976.32 - }, - { - "From": "SQ_US_SURVEY_YRD", - "Input": 42.42, - "To": "SQ_US_SURVEY_FT", - "Expect": 381.78000000000003 - }, - { - "From": "SQ_US_SURVEY_YRD", - "Input": 42.42, - "To": "SQ_US_SURVEY_YRD", - "Expect": 42.42 - }, - { - "From": "SQ_US_SURVEY_YRD", - "Input": 42.42, - "To": "SQ_US_SURVEY_MILE", - "Expect": 0.00001369447314049587 - }, - { - "From": "SQ_US_SURVEY_YRD", - "Input": 42.42, - "To": "SQ_US_SURVEY_CHAIN", - "Expect": 0.08764462809917356 - }, - { - "From": "SQ_US_SURVEY_YRD", - "Input": 42.42, - "To": "US_SURVEY_ACRE", - "Expect": 0.008764462809917356 - }, - { - "From": "SQ_US_SURVEY_MILE", - "Input": 42.42, - "To": "SQ_M", - "Expect": 109867735.11095409 - }, - { - "From": "SQ_US_SURVEY_MILE", - "Input": 42.42, - "To": "SQ_UM", - "Expect": 109867735110954090000 - }, - { - "From": "SQ_US_SURVEY_MILE", - "Input": 42.42, - "To": "SQ_MM", - "Expect": 109867735110954.11 - }, - { - "From": "SQ_US_SURVEY_MILE", - "Input": 42.42, - "To": "SQ_CM", - "Expect": 1098677351109.5408 - }, - { - "From": "SQ_US_SURVEY_MILE", - "Input": 42.42, - "To": "SQ_DM", - "Expect": 10986773511.095407 - }, - { - "From": "SQ_US_SURVEY_MILE", - "Input": 42.42, - "To": "SQ_KM", - "Expect": 109.86773511095409 - }, - { - "From": "SQ_US_SURVEY_MILE", - "Input": 42.42, - "To": "ARE", - "Expect": 1098677.351109541 - }, - { - "From": "SQ_US_SURVEY_MILE", - "Input": 42.42, - "To": "HECTARE", - "Expect": 10986.773511095409 - }, - { - "From": "SQ_US_SURVEY_MILE", - "Input": 42.42, - "To": "SQ_IN", - "Expect": 170295330012.6389 - }, - { - "From": "SQ_US_SURVEY_MILE", - "Input": 42.42, - "To": "SQ_FT", - "Expect": 1182606458.4211035 - }, - { - "From": "SQ_US_SURVEY_MILE", - "Input": 42.42, - "To": "THOUSAND_SQ_FT", - "Expect": 1182606.4584211034 - }, - { - "From": "SQ_US_SURVEY_MILE", - "Input": 42.42, - "To": "SQ_YRD", - "Expect": 131400717.60234481 - }, - { - "From": "SQ_US_SURVEY_MILE", - "Input": 42.42, - "To": "SQ_MILE", - "Expect": 42.42016968050905 - }, - { - "From": "SQ_US_SURVEY_MILE", - "Input": 42.42, - "To": "SQ_CHAIN", - "Expect": 271489.0859552579 - }, - { - "From": "SQ_US_SURVEY_MILE", - "Input": 42.42, - "To": "ACRE", - "Expect": 27148.908595525787 - }, - { - "From": "SQ_US_SURVEY_MILE", - "Input": 42.42, - "To": "SQ_US_SURVEY_IN", - "Expect": 170294648832 - }, - { - "From": "SQ_US_SURVEY_MILE", - "Input": 42.42, - "To": "SQ_US_SURVEY_FT", - "Expect": 1182601728 - }, - { - "From": "SQ_US_SURVEY_MILE", - "Input": 42.42, - "To": "SQ_US_SURVEY_YRD", - "Expect": 131400192 - }, - { - "From": "SQ_US_SURVEY_MILE", - "Input": 42.42, - "To": "SQ_US_SURVEY_MILE", - "Expect": 42.42 - }, - { - "From": "SQ_US_SURVEY_MILE", - "Input": 42.42, - "To": "SQ_US_SURVEY_CHAIN", - "Expect": 271488 - }, - { - "From": "SQ_US_SURVEY_MILE", - "Input": 42.42, - "To": "US_SURVEY_ACRE", - "Expect": 27148.800000000003 - }, - { - "From": "SQ_US_SURVEY_CHAIN", - "Input": 42.42, - "To": "SQ_M", - "Expect": 17166.83361108658 - }, - { - "From": "SQ_US_SURVEY_CHAIN", - "Input": 42.42, - "To": "SQ_UM", - "Expect": 17166833611086578 - }, - { - "From": "SQ_US_SURVEY_CHAIN", - "Input": 42.42, - "To": "SQ_MM", - "Expect": 17166833611.086578 - }, - { - "From": "SQ_US_SURVEY_CHAIN", - "Input": 42.42, - "To": "SQ_CM", - "Expect": 171668336.11086577 - }, - { - "From": "SQ_US_SURVEY_CHAIN", - "Input": 42.42, - "To": "SQ_DM", - "Expect": 1716683.3611086574 - }, - { - "From": "SQ_US_SURVEY_CHAIN", - "Input": 42.42, - "To": "SQ_KM", - "Expect": 0.017166833611086577 - }, - { - "From": "SQ_US_SURVEY_CHAIN", - "Input": 42.42, - "To": "ARE", - "Expect": 171.66833611086577 - }, - { - "From": "SQ_US_SURVEY_CHAIN", - "Input": 42.42, - "To": "HECTARE", - "Expect": 1.7166833611086576 - }, - { - "From": "SQ_US_SURVEY_CHAIN", - "Input": 42.42, - "To": "SQ_IN", - "Expect": 26608645.314474825 - }, - { - "From": "SQ_US_SURVEY_CHAIN", - "Input": 42.42, - "To": "SQ_FT", - "Expect": 184782.25912829742 - }, - { - "From": "SQ_US_SURVEY_CHAIN", - "Input": 42.42, - "To": "THOUSAND_SQ_FT", - "Expect": 184.78225912829743 - }, - { - "From": "SQ_US_SURVEY_CHAIN", - "Input": 42.42, - "To": "SQ_YRD", - "Expect": 20531.36212536638 - }, - { - "From": "SQ_US_SURVEY_CHAIN", - "Input": 42.42, - "To": "SQ_MILE", - "Expect": 0.006628151512579538 - }, - { - "From": "SQ_US_SURVEY_CHAIN", - "Input": 42.42, - "To": "SQ_CHAIN", - "Expect": 42.42016968050905 - }, - { - "From": "SQ_US_SURVEY_CHAIN", - "Input": 42.42, - "To": "ACRE", - "Expect": 4.242016968050904 - }, - { - "From": "SQ_US_SURVEY_CHAIN", - "Input": 42.42, - "To": "SQ_US_SURVEY_IN", - "Expect": 26608538.880000003 - }, - { - "From": "SQ_US_SURVEY_CHAIN", - "Input": 42.42, - "To": "SQ_US_SURVEY_FT", - "Expect": 184781.52000000002 - }, - { - "From": "SQ_US_SURVEY_CHAIN", - "Input": 42.42, - "To": "SQ_US_SURVEY_YRD", - "Expect": 20531.280000000002 - }, - { - "From": "SQ_US_SURVEY_CHAIN", - "Input": 42.42, - "To": "SQ_US_SURVEY_MILE", - "Expect": 0.006628125 - }, - { - "From": "SQ_US_SURVEY_CHAIN", - "Input": 42.42, - "To": "SQ_US_SURVEY_CHAIN", - "Expect": 42.42 - }, - { - "From": "SQ_US_SURVEY_CHAIN", - "Input": 42.42, - "To": "US_SURVEY_ACRE", - "Expect": 4.242 - }, - { - "From": "US_SURVEY_ACRE", - "Input": 42.42, - "To": "SQ_M", - "Expect": 171668.33611086576 - }, - { - "From": "US_SURVEY_ACRE", - "Input": 42.42, - "To": "SQ_UM", - "Expect": 171668336110865800 - }, - { - "From": "US_SURVEY_ACRE", - "Input": 42.42, - "To": "SQ_MM", - "Expect": 171668336110.86578 - }, - { - "From": "US_SURVEY_ACRE", - "Input": 42.42, - "To": "SQ_CM", - "Expect": 1716683361.1086576 - }, - { - "From": "US_SURVEY_ACRE", - "Input": 42.42, - "To": "SQ_DM", - "Expect": 17166833.611086573 - }, - { - "From": "US_SURVEY_ACRE", - "Input": 42.42, - "To": "SQ_KM", - "Expect": 0.17166833611086577 - }, - { - "From": "US_SURVEY_ACRE", - "Input": 42.42, - "To": "ARE", - "Expect": 1716.6833611086574 - }, - { - "From": "US_SURVEY_ACRE", - "Input": 42.42, - "To": "HECTARE", - "Expect": 17.166833611086577 - }, - { - "From": "US_SURVEY_ACRE", - "Input": 42.42, - "To": "SQ_IN", - "Expect": 266086453.14474824 - }, - { - "From": "US_SURVEY_ACRE", - "Input": 42.42, - "To": "SQ_FT", - "Expect": 1847822.5912829742 - }, - { - "From": "US_SURVEY_ACRE", - "Input": 42.42, - "To": "THOUSAND_SQ_FT", - "Expect": 1847.8225912829741 - }, - { - "From": "US_SURVEY_ACRE", - "Input": 42.42, - "To": "SQ_YRD", - "Expect": 205313.62125366376 - }, - { - "From": "US_SURVEY_ACRE", - "Input": 42.42, - "To": "SQ_MILE", - "Expect": 0.06628151512579537 - }, - { - "From": "US_SURVEY_ACRE", - "Input": 42.42, - "To": "SQ_CHAIN", - "Expect": 424.2016968050905 - }, - { - "From": "US_SURVEY_ACRE", - "Input": 42.42, - "To": "ACRE", - "Expect": 42.420169680509055 - }, - { - "From": "US_SURVEY_ACRE", - "Input": 42.42, - "To": "SQ_US_SURVEY_IN", - "Expect": 266085388.8 - }, - { - "From": "US_SURVEY_ACRE", - "Input": 42.42, - "To": "SQ_US_SURVEY_FT", - "Expect": 1847815.2000000002 - }, - { - "From": "US_SURVEY_ACRE", - "Input": 42.42, - "To": "SQ_US_SURVEY_YRD", - "Expect": 205312.80000000002 - }, - { - "From": "US_SURVEY_ACRE", - "Input": 42.42, - "To": "SQ_US_SURVEY_MILE", - "Expect": 0.06628125 - }, - { - "From": "US_SURVEY_ACRE", - "Input": 42.42, - "To": "SQ_US_SURVEY_CHAIN", - "Expect": 424.20000000000005 - }, - { - "From": "US_SURVEY_ACRE", - "Input": 42.42, - "To": "US_SURVEY_ACRE", - "Expect": 42.42 - }, - { - "From": "MM_TO_THE_FOURTH", - "Input": 42.42, - "To": "MM_TO_THE_FOURTH", - "Expect": 42.42 - }, - { - "From": "MM_TO_THE_FOURTH", - "Input": 42.42, - "To": "M_TO_THE_FOURTH", - "Expect": 4.242e-11 - }, - { - "From": "MM_TO_THE_FOURTH", - "Input": 42.42, - "To": "CM_TO_THE_FOURTH", - "Expect": 0.004242 - }, - { - "From": "MM_TO_THE_FOURTH", - "Input": 42.42, - "To": "IN_TO_THE_FOURTH", - "Expect": 0.00010191445765742299 - }, - { - "From": "MM_TO_THE_FOURTH", - "Input": 42.42, - "To": "FT_TO_THE_FOURTH", - "Expect": 4.914856175608747e-9 - }, - { - "From": "M_TO_THE_FOURTH", - "Input": 42.42, - "To": "MM_TO_THE_FOURTH", - "Expect": 42420000000000 - }, - { - "From": "M_TO_THE_FOURTH", - "Input": 42.42, - "To": "M_TO_THE_FOURTH", - "Expect": 42.42 - }, - { - "From": "M_TO_THE_FOURTH", - "Input": 42.42, - "To": "CM_TO_THE_FOURTH", - "Expect": 4242000000 - }, - { - "From": "M_TO_THE_FOURTH", - "Input": 42.42, - "To": "IN_TO_THE_FOURTH", - "Expect": 101914457.65742299 - }, - { - "From": "M_TO_THE_FOURTH", - "Input": 42.42, - "To": "FT_TO_THE_FOURTH", - "Expect": 4914.856175608747 - }, - { - "From": "CM_TO_THE_FOURTH", - "Input": 42.42, - "To": "MM_TO_THE_FOURTH", - "Expect": 424200 - }, - { - "From": "CM_TO_THE_FOURTH", - "Input": 42.42, - "To": "M_TO_THE_FOURTH", - "Expect": 4.242e-7 - }, - { - "From": "CM_TO_THE_FOURTH", - "Input": 42.42, - "To": "CM_TO_THE_FOURTH", - "Expect": 42.42 - }, - { - "From": "CM_TO_THE_FOURTH", - "Input": 42.42, - "To": "IN_TO_THE_FOURTH", - "Expect": 1.0191445765742297 - }, - { - "From": "CM_TO_THE_FOURTH", - "Input": 42.42, - "To": "FT_TO_THE_FOURTH", - "Expect": 0.00004914856175608747 - }, - { - "From": "IN_TO_THE_FOURTH", - "Input": 42.42, - "To": "MM_TO_THE_FOURTH", - "Expect": 17656537.073952 - }, - { - "From": "IN_TO_THE_FOURTH", - "Input": 42.42, - "To": "M_TO_THE_FOURTH", - "Expect": 0.000017656537073951998 - }, - { - "From": "IN_TO_THE_FOURTH", - "Input": 42.42, - "To": "CM_TO_THE_FOURTH", - "Expect": 1765.6537073952 - }, - { - "From": "IN_TO_THE_FOURTH", - "Input": 42.42, - "To": "IN_TO_THE_FOURTH", - "Expect": 42.42 - }, - { - "From": "IN_TO_THE_FOURTH", - "Input": 42.42, - "To": "FT_TO_THE_FOURTH", - "Expect": 0.0020457175925925925 - }, - { - "From": "FT_TO_THE_FOURTH", - "Input": 42.42, - "To": "MM_TO_THE_FOURTH", - "Expect": 366125952765.4687 - }, - { - "From": "FT_TO_THE_FOURTH", - "Input": 42.42, - "To": "M_TO_THE_FOURTH", - "Expect": 0.3661259527654686 - }, - { - "From": "FT_TO_THE_FOURTH", - "Input": 42.42, - "To": "CM_TO_THE_FOURTH", - "Expect": 36612595.27654686 - }, - { - "From": "FT_TO_THE_FOURTH", - "Input": 42.42, - "To": "IN_TO_THE_FOURTH", - "Expect": 879621.12 - }, - { - "From": "FT_TO_THE_FOURTH", - "Input": 42.42, - "To": "FT_TO_THE_FOURTH", - "Expect": 42.42 - }, - { - "From": "PERSON", - "Input": 42.42, - "To": "PERSON", - "Expect": 42.42 - }, - { - "From": "PERSON", - "Input": 42.42, - "To": "HUNDRED_PERSON", - "Expect": 0.4242 - }, - { - "From": "PERSON", - "Input": 42.42, - "To": "THOUSAND_PERSON", - "Expect": 0.04242 - }, - { - "From": "HUNDRED_PERSON", - "Input": 42.42, - "To": "PERSON", - "Expect": 4242 - }, - { - "From": "HUNDRED_PERSON", - "Input": 42.42, - "To": "HUNDRED_PERSON", - "Expect": 42.42 - }, - { - "From": "HUNDRED_PERSON", - "Input": 42.42, - "To": "THOUSAND_PERSON", - "Expect": 4.242 - }, - { - "From": "THOUSAND_PERSON", - "Input": 42.42, - "To": "PERSON", - "Expect": 42420 - }, - { - "From": "THOUSAND_PERSON", - "Input": 42.42, - "To": "HUNDRED_PERSON", - "Expect": 424.20000000000005 - }, - { - "From": "THOUSAND_PERSON", - "Input": 42.42, - "To": "THOUSAND_PERSON", - "Expect": 42.42 - }, - { - "From": "US_DOLLAR", - "Input": 42.42, - "To": "US_DOLLAR", - "Expect": 42.42 - }, - { - "From": "A", - "Input": 42.42, - "To": "A", - "Expect": 42.42 - }, - { - "From": "A", - "Input": 42.42, - "To": "KILOAMPERE", - "Expect": 0.04242 - }, - { - "From": "A", - "Input": 42.42, - "To": "MILLIAMPERE", - "Expect": 42420 - }, - { - "From": "A", - "Input": 42.42, - "To": "MICROAMPERE", - "Expect": 42420000 - }, - { - "From": "KILOAMPERE", - "Input": 42.42, - "To": "A", - "Expect": 42420 - }, - { - "From": "KILOAMPERE", - "Input": 42.42, - "To": "KILOAMPERE", - "Expect": 42.42 - }, - { - "From": "KILOAMPERE", - "Input": 42.42, - "To": "MILLIAMPERE", - "Expect": 42420000 - }, - { - "From": "KILOAMPERE", - "Input": 42.42, - "To": "MICROAMPERE", - "Expect": 42420000000 - }, - { - "From": "MILLIAMPERE", - "Input": 42.42, - "To": "A", - "Expect": 0.04242 - }, - { - "From": "MILLIAMPERE", - "Input": 42.42, - "To": "KILOAMPERE", - "Expect": 0.00004242 - }, - { - "From": "MILLIAMPERE", - "Input": 42.42, - "To": "MILLIAMPERE", - "Expect": 42.42 - }, - { - "From": "MILLIAMPERE", - "Input": 42.42, - "To": "MICROAMPERE", - "Expect": 42420.00000000001 - }, - { - "From": "MICROAMPERE", - "Input": 42.42, - "To": "A", - "Expect": 0.00004242 - }, - { - "From": "MICROAMPERE", - "Input": 42.42, - "To": "KILOAMPERE", - "Expect": 4.242e-8 - }, - { - "From": "MICROAMPERE", - "Input": 42.42, - "To": "MILLIAMPERE", - "Expect": 0.04242 - }, - { - "From": "MICROAMPERE", - "Input": 42.42, - "To": "MICROAMPERE", - "Expect": 42.42 - }, - { - "From": "KG_PER_CUB_M", - "Input": 42.42, - "To": "KG_PER_CUB_M", - "Expect": 42.42 - }, - { - "From": "KG_PER_CUB_M", - "Input": 42.42, - "To": "KG_PER_CUB_CM", - "Expect": 0.00004242000000000001 - }, - { - "From": "KG_PER_CUB_M", - "Input": 42.42, - "To": "KG_PER_LITRE", - "Expect": 0.04242000000000001 - }, - { - "From": "KG_PER_CUB_M", - "Input": 42.42, - "To": "G_PER_CUB_CM", - "Expect": 0.04242000000000001 - }, - { - "From": "KG_PER_CUB_M", - "Input": 42.42, - "To": "MKG_PER_LITRE", - "Expect": 42420000.000000015 - }, - { - "From": "KG_PER_CUB_M", - "Input": 42.42, - "To": "MG_PER_LITRE", - "Expect": 42420.000000000015 - }, - { - "From": "KG_PER_CUB_M", - "Input": 42.42, - "To": "LBM_PER_CUB_FT", - "Expect": 2.648194087640054 - }, - { - "From": "KG_PER_CUB_M", - "Input": 42.42, - "To": "LBM_PER_GALLON", - "Expect": 0.35401205685466003 - }, - { - "From": "KG_PER_CUB_M", - "Input": 42.42, - "To": "LBM_PER_GALLON_IMPERIAL", - "Expect": 0.42515075330742463 - }, - { - "From": "KG_PER_CUB_M", - "Input": 42.42, - "To": "LBM_PER_CUB_IN", - "Expect": 0.00153251972664355 - }, - { - "From": "KG_PER_CUB_M", - "Input": 42.42, - "To": "LBM_PER_MILLION_GALLON", - "Expect": 354012.05685466004 - }, - { - "From": "KG_PER_CUB_M", - "Input": 42.42, - "To": "SLUG_PER_CUB_FT", - "Expect": 0.08230838848257951 - }, - { - "From": "KG_PER_CUB_M", - "Input": 42.42, - "To": "KIP_PER_CUB_FT", - "Expect": 0.002648194087640054 - }, - { - "From": "KG_PER_CUB_M", - "Input": 42.42, - "To": "SHORT_TON_PER_CUB_FT", - "Expect": 0.001324097043820027 - }, - { - "From": "KG_PER_CUB_CM", - "Input": 42.42, - "To": "KG_PER_CUB_M", - "Expect": 42420000 - }, - { - "From": "KG_PER_CUB_CM", - "Input": 42.42, - "To": "KG_PER_CUB_CM", - "Expect": 42.42 - }, - { - "From": "KG_PER_CUB_CM", - "Input": 42.42, - "To": "KG_PER_LITRE", - "Expect": 42420.00000000001 - }, - { - "From": "KG_PER_CUB_CM", - "Input": 42.42, - "To": "G_PER_CUB_CM", - "Expect": 42420 - }, - { - "From": "KG_PER_CUB_CM", - "Input": 42.42, - "To": "MKG_PER_LITRE", - "Expect": 42420000000000.01 - }, - { - "From": "KG_PER_CUB_CM", - "Input": 42.42, - "To": "MG_PER_LITRE", - "Expect": 42420000000.00001 - }, - { - "From": "KG_PER_CUB_CM", - "Input": 42.42, - "To": "LBM_PER_CUB_FT", - "Expect": 2648194.087640054 - }, - { - "From": "KG_PER_CUB_CM", - "Input": 42.42, - "To": "LBM_PER_GALLON", - "Expect": 354012.05685466 - }, - { - "From": "KG_PER_CUB_CM", - "Input": 42.42, - "To": "LBM_PER_GALLON_IMPERIAL", - "Expect": 425150.75330742454 - }, - { - "From": "KG_PER_CUB_CM", - "Input": 42.42, - "To": "LBM_PER_CUB_IN", - "Expect": 1532.51972664355 - }, - { - "From": "KG_PER_CUB_CM", - "Input": 42.42, - "To": "LBM_PER_MILLION_GALLON", - "Expect": 354012056854.66 - }, - { - "From": "KG_PER_CUB_CM", - "Input": 42.42, - "To": "SLUG_PER_CUB_FT", - "Expect": 82308.38848257951 - }, - { - "From": "KG_PER_CUB_CM", - "Input": 42.42, - "To": "KIP_PER_CUB_FT", - "Expect": 2648.1940876400536 - }, - { - "From": "KG_PER_CUB_CM", - "Input": 42.42, - "To": "SHORT_TON_PER_CUB_FT", - "Expect": 1324.0970438200268 - }, - { - "From": "KG_PER_LITRE", - "Input": 42.42, - "To": "KG_PER_CUB_M", - "Expect": 42419.99999999999 - }, - { - "From": "KG_PER_LITRE", - "Input": 42.42, - "To": "KG_PER_CUB_CM", - "Expect": 0.04242 - }, - { - "From": "KG_PER_LITRE", - "Input": 42.42, - "To": "KG_PER_LITRE", - "Expect": 42.42 - }, - { - "From": "KG_PER_LITRE", - "Input": 42.42, - "To": "G_PER_CUB_CM", - "Expect": 42.42 - }, - { - "From": "KG_PER_LITRE", - "Input": 42.42, - "To": "MKG_PER_LITRE", - "Expect": 42420000000 - }, - { - "From": "KG_PER_LITRE", - "Input": 42.42, - "To": "MG_PER_LITRE", - "Expect": 42420000 - }, - { - "From": "KG_PER_LITRE", - "Input": 42.42, - "To": "LBM_PER_CUB_FT", - "Expect": 2648.1940876400536 - }, - { - "From": "KG_PER_LITRE", - "Input": 42.42, - "To": "LBM_PER_GALLON", - "Expect": 354.01205685465993 - }, - { - "From": "KG_PER_LITRE", - "Input": 42.42, - "To": "LBM_PER_GALLON_IMPERIAL", - "Expect": 425.1507533074245 - }, - { - "From": "KG_PER_LITRE", - "Input": 42.42, - "To": "LBM_PER_CUB_IN", - "Expect": 1.5325197266435495 - }, - { - "From": "KG_PER_LITRE", - "Input": 42.42, - "To": "LBM_PER_MILLION_GALLON", - "Expect": 354012056.85466 - }, - { - "From": "KG_PER_LITRE", - "Input": 42.42, - "To": "SLUG_PER_CUB_FT", - "Expect": 82.3083884825795 - }, - { - "From": "KG_PER_LITRE", - "Input": 42.42, - "To": "KIP_PER_CUB_FT", - "Expect": 2.648194087640054 - }, - { - "From": "KG_PER_LITRE", - "Input": 42.42, - "To": "SHORT_TON_PER_CUB_FT", - "Expect": 1.324097043820027 - }, - { - "From": "G_PER_CUB_CM", - "Input": 42.42, - "To": "KG_PER_CUB_M", - "Expect": 42420 - }, - { - "From": "G_PER_CUB_CM", - "Input": 42.42, - "To": "KG_PER_CUB_CM", - "Expect": 0.04242 - }, - { - "From": "G_PER_CUB_CM", - "Input": 42.42, - "To": "KG_PER_LITRE", - "Expect": 42.42000000000001 - }, - { - "From": "G_PER_CUB_CM", - "Input": 42.42, - "To": "G_PER_CUB_CM", - "Expect": 42.42 - }, - { - "From": "G_PER_CUB_CM", - "Input": 42.42, - "To": "MKG_PER_LITRE", - "Expect": 42420000000.00001 - }, - { - "From": "G_PER_CUB_CM", - "Input": 42.42, - "To": "MG_PER_LITRE", - "Expect": 42420000.00000001 - }, - { - "From": "G_PER_CUB_CM", - "Input": 42.42, - "To": "LBM_PER_CUB_FT", - "Expect": 2648.1940876400536 - }, - { - "From": "G_PER_CUB_CM", - "Input": 42.42, - "To": "LBM_PER_GALLON", - "Expect": 354.01205685465993 - }, - { - "From": "G_PER_CUB_CM", - "Input": 42.42, - "To": "LBM_PER_GALLON_IMPERIAL", - "Expect": 425.15075330742457 - }, - { - "From": "G_PER_CUB_CM", - "Input": 42.42, - "To": "LBM_PER_CUB_IN", - "Expect": 1.5325197266435493 - }, - { - "From": "G_PER_CUB_CM", - "Input": 42.42, - "To": "LBM_PER_MILLION_GALLON", - "Expect": 354012056.85466 - }, - { - "From": "G_PER_CUB_CM", - "Input": 42.42, - "To": "SLUG_PER_CUB_FT", - "Expect": 82.30838848257952 - }, - { - "From": "G_PER_CUB_CM", - "Input": 42.42, - "To": "KIP_PER_CUB_FT", - "Expect": 2.6481940876400536 - }, - { - "From": "G_PER_CUB_CM", - "Input": 42.42, - "To": "SHORT_TON_PER_CUB_FT", - "Expect": 1.3240970438200268 - }, - { - "From": "MKG_PER_LITRE", - "Input": 42.42, - "To": "KG_PER_CUB_M", - "Expect": 0.00004241999999999999 - }, - { - "From": "MKG_PER_LITRE", - "Input": 42.42, - "To": "KG_PER_CUB_CM", - "Expect": 4.242e-11 - }, - { - "From": "MKG_PER_LITRE", - "Input": 42.42, - "To": "KG_PER_LITRE", - "Expect": 4.2420000000000005e-8 - }, - { - "From": "MKG_PER_LITRE", - "Input": 42.42, - "To": "G_PER_CUB_CM", - "Expect": 4.2420000000000005e-8 - }, - { - "From": "MKG_PER_LITRE", - "Input": 42.42, - "To": "MKG_PER_LITRE", - "Expect": 42.42 - }, - { - "From": "MKG_PER_LITRE", - "Input": 42.42, - "To": "MG_PER_LITRE", - "Expect": 0.04242 - }, - { - "From": "MKG_PER_LITRE", - "Input": 42.42, - "To": "LBM_PER_CUB_FT", - "Expect": 0.000002648194087640053 - }, - { - "From": "MKG_PER_LITRE", - "Input": 42.42, - "To": "LBM_PER_GALLON", - "Expect": 3.540120568546599e-7 - }, - { - "From": "MKG_PER_LITRE", - "Input": 42.42, - "To": "LBM_PER_GALLON_IMPERIAL", - "Expect": 4.251507533074245e-7 - }, - { - "From": "MKG_PER_LITRE", - "Input": 42.42, - "To": "LBM_PER_CUB_IN", - "Expect": 1.5325197266435495e-9 - }, - { - "From": "MKG_PER_LITRE", - "Input": 42.42, - "To": "LBM_PER_MILLION_GALLON", - "Expect": 0.35401205685466 - }, - { - "From": "MKG_PER_LITRE", - "Input": 42.42, - "To": "SLUG_PER_CUB_FT", - "Expect": 8.230838848257954e-8 - }, - { - "From": "MKG_PER_LITRE", - "Input": 42.42, - "To": "KIP_PER_CUB_FT", - "Expect": 2.6481940876400533e-9 - }, - { - "From": "MKG_PER_LITRE", - "Input": 42.42, - "To": "SHORT_TON_PER_CUB_FT", - "Expect": 1.3240970438200266e-9 - }, - { - "From": "MG_PER_LITRE", - "Input": 42.42, - "To": "KG_PER_CUB_M", - "Expect": 0.04241999999999999 - }, - { - "From": "MG_PER_LITRE", - "Input": 42.42, - "To": "KG_PER_CUB_CM", - "Expect": 4.2420000000000005e-8 - }, - { - "From": "MG_PER_LITRE", - "Input": 42.42, - "To": "KG_PER_LITRE", - "Expect": 0.00004242 - }, - { - "From": "MG_PER_LITRE", - "Input": 42.42, - "To": "G_PER_CUB_CM", - "Expect": 0.00004242 - }, - { - "From": "MG_PER_LITRE", - "Input": 42.42, - "To": "MKG_PER_LITRE", - "Expect": 42420.00000000001 - }, - { - "From": "MG_PER_LITRE", - "Input": 42.42, - "To": "MG_PER_LITRE", - "Expect": 42.42 - }, - { - "From": "MG_PER_LITRE", - "Input": 42.42, - "To": "LBM_PER_CUB_FT", - "Expect": 0.0026481940876400536 - }, - { - "From": "MG_PER_LITRE", - "Input": 42.42, - "To": "LBM_PER_GALLON", - "Expect": 0.00035401205685466 - }, - { - "From": "MG_PER_LITRE", - "Input": 42.42, - "To": "LBM_PER_GALLON_IMPERIAL", - "Expect": 0.0004251507533074245 - }, - { - "From": "MG_PER_LITRE", - "Input": 42.42, - "To": "LBM_PER_CUB_IN", - "Expect": 0.0000015325197266435495 - }, - { - "From": "MG_PER_LITRE", - "Input": 42.42, - "To": "LBM_PER_MILLION_GALLON", - "Expect": 354.01205685465993 - }, - { - "From": "MG_PER_LITRE", - "Input": 42.42, - "To": "SLUG_PER_CUB_FT", - "Expect": 0.0000823083884825795 - }, - { - "From": "MG_PER_LITRE", - "Input": 42.42, - "To": "KIP_PER_CUB_FT", - "Expect": 0.000002648194087640054 - }, - { - "From": "MG_PER_LITRE", - "Input": 42.42, - "To": "SHORT_TON_PER_CUB_FT", - "Expect": 0.000001324097043820027 - }, - { - "From": "LBM_PER_CUB_FT", - "Input": 42.42, - "To": "KG_PER_CUB_M", - "Expect": 679.5032163233892 - }, - { - "From": "LBM_PER_CUB_FT", - "Input": 42.42, - "To": "KG_PER_CUB_CM", - "Expect": 0.0006795032163233894 - }, - { - "From": "LBM_PER_CUB_FT", - "Input": 42.42, - "To": "KG_PER_LITRE", - "Expect": 0.6795032163233894 - }, - { - "From": "LBM_PER_CUB_FT", - "Input": 42.42, - "To": "G_PER_CUB_CM", - "Expect": 0.6795032163233894 - }, - { - "From": "LBM_PER_CUB_FT", - "Input": 42.42, - "To": "MKG_PER_LITRE", - "Expect": 679503216.3233894 - }, - { - "From": "LBM_PER_CUB_FT", - "Input": 42.42, - "To": "MG_PER_LITRE", - "Expect": 679503.2163233893 - }, - { - "From": "LBM_PER_CUB_FT", - "Input": 42.42, - "To": "LBM_PER_CUB_FT", - "Expect": 42.42 - }, - { - "From": "LBM_PER_CUB_FT", - "Input": 42.42, - "To": "LBM_PER_GALLON", - "Expect": 5.670729166666667 - }, - { - "From": "LBM_PER_CUB_FT", - "Input": 42.42, - "To": "LBM_PER_GALLON_IMPERIAL", - "Expect": 6.810261770266544 - }, - { - "From": "LBM_PER_CUB_FT", - "Input": 42.42, - "To": "LBM_PER_CUB_IN", - "Expect": 0.02454861111111111 - }, - { - "From": "LBM_PER_CUB_FT", - "Input": 42.42, - "To": "LBM_PER_MILLION_GALLON", - "Expect": 5670729.166666667 - }, - { - "From": "LBM_PER_CUB_FT", - "Input": 42.42, - "To": "SLUG_PER_CUB_FT", - "Expect": 1.3184539062778828 - }, - { - "From": "LBM_PER_CUB_FT", - "Input": 42.42, - "To": "KIP_PER_CUB_FT", - "Expect": 0.04242 - }, - { - "From": "LBM_PER_CUB_FT", - "Input": 42.42, - "To": "SHORT_TON_PER_CUB_FT", - "Expect": 0.02121 - }, - { - "From": "LBM_PER_GALLON", - "Input": 42.42, - "To": "KG_PER_CUB_M", - "Expect": 5083.037046782756 - }, - { - "From": "LBM_PER_GALLON", - "Input": 42.42, - "To": "KG_PER_CUB_CM", - "Expect": 0.005083037046782757 - }, - { - "From": "LBM_PER_GALLON", - "Input": 42.42, - "To": "KG_PER_LITRE", - "Expect": 5.083037046782757 - }, - { - "From": "LBM_PER_GALLON", - "Input": 42.42, - "To": "G_PER_CUB_CM", - "Expect": 5.083037046782756 - }, - { - "From": "LBM_PER_GALLON", - "Input": 42.42, - "To": "MKG_PER_LITRE", - "Expect": 5083037046.782757 - }, - { - "From": "LBM_PER_GALLON", - "Input": 42.42, - "To": "MG_PER_LITRE", - "Expect": 5083037.046782757 - }, - { - "From": "LBM_PER_GALLON", - "Input": 42.42, - "To": "LBM_PER_CUB_FT", - "Expect": 317.32363636363635 - }, - { - "From": "LBM_PER_GALLON", - "Input": 42.42, - "To": "LBM_PER_GALLON", - "Expect": 42.42 - }, - { - "From": "LBM_PER_GALLON", - "Input": 42.42, - "To": "LBM_PER_GALLON_IMPERIAL", - "Expect": 50.94429583991597 - }, - { - "From": "LBM_PER_GALLON", - "Input": 42.42, - "To": "LBM_PER_CUB_IN", - "Expect": 0.18363636363636365 - }, - { - "From": "LBM_PER_GALLON", - "Input": 42.42, - "To": "LBM_PER_MILLION_GALLON", - "Expect": 42420000 - }, - { - "From": "LBM_PER_GALLON", - "Input": 42.42, - "To": "SLUG_PER_CUB_FT", - "Expect": 9.862720130078708 - }, - { - "From": "LBM_PER_GALLON", - "Input": 42.42, - "To": "KIP_PER_CUB_FT", - "Expect": 0.31732363636363636 - }, - { - "From": "LBM_PER_GALLON", - "Input": 42.42, - "To": "SHORT_TON_PER_CUB_FT", - "Expect": 0.15866181818181818 - }, - { - "From": "LBM_PER_GALLON_IMPERIAL", - "Input": 42.42, - "To": "KG_PER_CUB_M", - "Expect": 4232.513728368773 - }, - { - "From": "LBM_PER_GALLON_IMPERIAL", - "Input": 42.42, - "To": "KG_PER_CUB_CM", - "Expect": 0.0042325137283687735 - }, - { - "From": "LBM_PER_GALLON_IMPERIAL", - "Input": 42.42, - "To": "KG_PER_LITRE", - "Expect": 4.232513728368774 - }, - { - "From": "LBM_PER_GALLON_IMPERIAL", - "Input": 42.42, - "To": "G_PER_CUB_CM", - "Expect": 4.232513728368774 - }, - { - "From": "LBM_PER_GALLON_IMPERIAL", - "Input": 42.42, - "To": "MKG_PER_LITRE", - "Expect": 4232513728.3687744 - }, - { - "From": "LBM_PER_GALLON_IMPERIAL", - "Input": 42.42, - "To": "MG_PER_LITRE", - "Expect": 4232513.728368774 - }, - { - "From": "LBM_PER_GALLON_IMPERIAL", - "Input": 42.42, - "To": "LBM_PER_CUB_FT", - "Expect": 264.2272001725966 - }, - { - "From": "LBM_PER_GALLON_IMPERIAL", - "Input": 42.42, - "To": "LBM_PER_GALLON", - "Expect": 35.3220389119617 - }, - { - "From": "LBM_PER_GALLON_IMPERIAL", - "Input": 42.42, - "To": "LBM_PER_GALLON_IMPERIAL", - "Expect": 42.42 - }, - { - "From": "LBM_PER_GALLON_IMPERIAL", - "Input": 42.42, - "To": "LBM_PER_CUB_IN", - "Expect": 0.15290925935914154 - }, - { - "From": "LBM_PER_GALLON_IMPERIAL", - "Input": 42.42, - "To": "LBM_PER_MILLION_GALLON", - "Expect": 35322038.9119617 - }, - { - "From": "LBM_PER_GALLON_IMPERIAL", - "Input": 42.42, - "To": "SLUG_PER_CUB_FT", - "Expect": 8.212432442537201 - }, - { - "From": "LBM_PER_GALLON_IMPERIAL", - "Input": 42.42, - "To": "KIP_PER_CUB_FT", - "Expect": 0.2642272001725966 - }, - { - "From": "LBM_PER_GALLON_IMPERIAL", - "Input": 42.42, - "To": "SHORT_TON_PER_CUB_FT", - "Expect": 0.1321136000862983 - }, - { - "From": "LBM_PER_CUB_IN", - "Input": 42.42, - "To": "KG_PER_CUB_M", - "Expect": 1174181.5578068167 - }, - { - "From": "LBM_PER_CUB_IN", - "Input": 42.42, - "To": "KG_PER_CUB_CM", - "Expect": 1.1741815578068169 - }, - { - "From": "LBM_PER_CUB_IN", - "Input": 42.42, - "To": "KG_PER_LITRE", - "Expect": 1174.181557806817 - }, - { - "From": "LBM_PER_CUB_IN", - "Input": 42.42, - "To": "G_PER_CUB_CM", - "Expect": 1174.181557806817 - }, - { - "From": "LBM_PER_CUB_IN", - "Input": 42.42, - "To": "MKG_PER_LITRE", - "Expect": 1174181557806.817 - }, - { - "From": "LBM_PER_CUB_IN", - "Input": 42.42, - "To": "MG_PER_LITRE", - "Expect": 1174181557.8068168 - }, - { - "From": "LBM_PER_CUB_IN", - "Input": 42.42, - "To": "LBM_PER_CUB_FT", - "Expect": 73301.76000000001 - }, - { - "From": "LBM_PER_CUB_IN", - "Input": 42.42, - "To": "LBM_PER_GALLON", - "Expect": 9799.02 - }, - { - "From": "LBM_PER_CUB_IN", - "Input": 42.42, - "To": "LBM_PER_GALLON_IMPERIAL", - "Expect": 11768.132339020587 - }, - { - "From": "LBM_PER_CUB_IN", - "Input": 42.42, - "To": "LBM_PER_CUB_IN", - "Expect": 42.42 - }, - { - "From": "LBM_PER_CUB_IN", - "Input": 42.42, - "To": "LBM_PER_MILLION_GALLON", - "Expect": 9799020000 - }, - { - "From": "LBM_PER_CUB_IN", - "Input": 42.42, - "To": "SLUG_PER_CUB_FT", - "Expect": 2278.2883500481817 - }, - { - "From": "LBM_PER_CUB_IN", - "Input": 42.42, - "To": "KIP_PER_CUB_FT", - "Expect": 73.30176 - }, - { - "From": "LBM_PER_CUB_IN", - "Input": 42.42, - "To": "SHORT_TON_PER_CUB_FT", - "Expect": 36.65088 - }, - { - "From": "LBM_PER_MILLION_GALLON", - "Input": 42.42, - "To": "KG_PER_CUB_M", - "Expect": 0.005083037046782756 - }, - { - "From": "LBM_PER_MILLION_GALLON", - "Input": 42.42, - "To": "KG_PER_CUB_CM", - "Expect": 5.0830370467827564e-9 - }, - { - "From": "LBM_PER_MILLION_GALLON", - "Input": 42.42, - "To": "KG_PER_LITRE", - "Expect": 0.000005083037046782757 - }, - { - "From": "LBM_PER_MILLION_GALLON", - "Input": 42.42, - "To": "G_PER_CUB_CM", - "Expect": 0.000005083037046782757 - }, - { - "From": "LBM_PER_MILLION_GALLON", - "Input": 42.42, - "To": "MKG_PER_LITRE", - "Expect": 5083.037046782758 - }, - { - "From": "LBM_PER_MILLION_GALLON", - "Input": 42.42, - "To": "MG_PER_LITRE", - "Expect": 5.083037046782756 - }, - { - "From": "LBM_PER_MILLION_GALLON", - "Input": 42.42, - "To": "LBM_PER_CUB_FT", - "Expect": 0.00031732363636363637 - }, - { - "From": "LBM_PER_MILLION_GALLON", - "Input": 42.42, - "To": "LBM_PER_GALLON", - "Expect": 0.00004242 - }, - { - "From": "LBM_PER_MILLION_GALLON", - "Input": 42.42, - "To": "LBM_PER_GALLON_IMPERIAL", - "Expect": 0.00005094429583991596 - }, - { - "From": "LBM_PER_MILLION_GALLON", - "Input": 42.42, - "To": "LBM_PER_CUB_IN", - "Expect": 1.8363636363636363e-7 - }, - { - "From": "LBM_PER_MILLION_GALLON", - "Input": 42.42, - "To": "LBM_PER_MILLION_GALLON", - "Expect": 42.42 - }, - { - "From": "LBM_PER_MILLION_GALLON", - "Input": 42.42, - "To": "SLUG_PER_CUB_FT", - "Expect": 0.000009862720130078708 - }, - { - "From": "LBM_PER_MILLION_GALLON", - "Input": 42.42, - "To": "KIP_PER_CUB_FT", - "Expect": 3.173236363636363e-7 - }, - { - "From": "LBM_PER_MILLION_GALLON", - "Input": 42.42, - "To": "SHORT_TON_PER_CUB_FT", - "Expect": 1.5866181818181816e-7 - }, - { - "From": "SLUG_PER_CUB_FT", - "Input": 42.42, - "To": "KG_PER_CUB_M", - "Expect": 21862.369476239386 - }, - { - "From": "SLUG_PER_CUB_FT", - "Input": 42.42, - "To": "KG_PER_CUB_CM", - "Expect": 0.02186236947623939 - }, - { - "From": "SLUG_PER_CUB_FT", - "Input": 42.42, - "To": "KG_PER_LITRE", - "Expect": 21.862369476239394 - }, - { - "From": "SLUG_PER_CUB_FT", - "Input": 42.42, - "To": "G_PER_CUB_CM", - "Expect": 21.862369476239383 - }, - { - "From": "SLUG_PER_CUB_FT", - "Input": 42.42, - "To": "MKG_PER_LITRE", - "Expect": 21862369476.23939 - }, - { - "From": "SLUG_PER_CUB_FT", - "Input": 42.42, - "To": "MG_PER_LITRE", - "Expect": 21862369.47623939 - }, - { - "From": "SLUG_PER_CUB_FT", - "Input": 42.42, - "To": "LBM_PER_CUB_FT", - "Expect": 1364.8231397637794 - }, - { - "From": "SLUG_PER_CUB_FT", - "Input": 42.42, - "To": "LBM_PER_GALLON", - "Expect": 182.45031555869969 - }, - { - "From": "SLUG_PER_CUB_FT", - "Input": 42.42, - "To": "LBM_PER_GALLON_IMPERIAL", - "Expect": 219.11369287855774 - }, - { - "From": "SLUG_PER_CUB_FT", - "Input": 42.42, - "To": "LBM_PER_CUB_IN", - "Expect": 0.7898282058818168 - }, - { - "From": "SLUG_PER_CUB_FT", - "Input": 42.42, - "To": "LBM_PER_MILLION_GALLON", - "Expect": 182450315.5586997 - }, - { - "From": "SLUG_PER_CUB_FT", - "Input": 42.42, - "To": "SLUG_PER_CUB_FT", - "Expect": 42.42 - }, - { - "From": "SLUG_PER_CUB_FT", - "Input": 42.42, - "To": "KIP_PER_CUB_FT", - "Expect": 1.3648231397637793 - }, - { - "From": "SLUG_PER_CUB_FT", - "Input": 42.42, - "To": "SHORT_TON_PER_CUB_FT", - "Expect": 0.6824115698818897 - }, - { - "From": "KIP_PER_CUB_FT", - "Input": 42.42, - "To": "KG_PER_CUB_M", - "Expect": 679503.2163233891 - }, - { - "From": "KIP_PER_CUB_FT", - "Input": 42.42, - "To": "KG_PER_CUB_CM", - "Expect": 0.6795032163233892 - }, - { - "From": "KIP_PER_CUB_FT", - "Input": 42.42, - "To": "KG_PER_LITRE", - "Expect": 679.5032163233892 - }, - { - "From": "KIP_PER_CUB_FT", - "Input": 42.42, - "To": "G_PER_CUB_CM", - "Expect": 679.5032163233894 - }, - { - "From": "KIP_PER_CUB_FT", - "Input": 42.42, - "To": "MKG_PER_LITRE", - "Expect": 679503216323.3894 - }, - { - "From": "KIP_PER_CUB_FT", - "Input": 42.42, - "To": "MG_PER_LITRE", - "Expect": 679503216.3233894 - }, - { - "From": "KIP_PER_CUB_FT", - "Input": 42.42, - "To": "LBM_PER_CUB_FT", - "Expect": 42420 - }, - { - "From": "KIP_PER_CUB_FT", - "Input": 42.42, - "To": "LBM_PER_GALLON", - "Expect": 5670.729166666668 - }, - { - "From": "KIP_PER_CUB_FT", - "Input": 42.42, - "To": "LBM_PER_GALLON_IMPERIAL", - "Expect": 6810.261770266543 - }, - { - "From": "KIP_PER_CUB_FT", - "Input": 42.42, - "To": "LBM_PER_CUB_IN", - "Expect": 24.548611111111114 - }, - { - "From": "KIP_PER_CUB_FT", - "Input": 42.42, - "To": "LBM_PER_MILLION_GALLON", - "Expect": 5670729166.666667 - }, - { - "From": "KIP_PER_CUB_FT", - "Input": 42.42, - "To": "SLUG_PER_CUB_FT", - "Expect": 1318.453906277883 - }, - { - "From": "KIP_PER_CUB_FT", - "Input": 42.42, - "To": "KIP_PER_CUB_FT", - "Expect": 42.42 - }, - { - "From": "KIP_PER_CUB_FT", - "Input": 42.42, - "To": "SHORT_TON_PER_CUB_FT", - "Expect": 21.21 - }, - { - "From": "SHORT_TON_PER_CUB_FT", - "Input": 42.42, - "To": "KG_PER_CUB_M", - "Expect": 1359006.4326467782 - }, - { - "From": "SHORT_TON_PER_CUB_FT", - "Input": 42.42, - "To": "KG_PER_CUB_CM", - "Expect": 1.3590064326467783 - }, - { - "From": "SHORT_TON_PER_CUB_FT", - "Input": 42.42, - "To": "KG_PER_LITRE", - "Expect": 1359.0064326467784 - }, - { - "From": "SHORT_TON_PER_CUB_FT", - "Input": 42.42, - "To": "G_PER_CUB_CM", - "Expect": 1359.0064326467789 - }, - { - "From": "SHORT_TON_PER_CUB_FT", - "Input": 42.42, - "To": "MKG_PER_LITRE", - "Expect": 1359006432646.7788 - }, - { - "From": "SHORT_TON_PER_CUB_FT", - "Input": 42.42, - "To": "MG_PER_LITRE", - "Expect": 1359006432.6467788 - }, - { - "From": "SHORT_TON_PER_CUB_FT", - "Input": 42.42, - "To": "LBM_PER_CUB_FT", - "Expect": 84840 - }, - { - "From": "SHORT_TON_PER_CUB_FT", - "Input": 42.42, - "To": "LBM_PER_GALLON", - "Expect": 11341.458333333336 - }, - { - "From": "SHORT_TON_PER_CUB_FT", - "Input": 42.42, - "To": "LBM_PER_GALLON_IMPERIAL", - "Expect": 13620.523540533086 - }, - { - "From": "SHORT_TON_PER_CUB_FT", - "Input": 42.42, - "To": "LBM_PER_CUB_IN", - "Expect": 49.09722222222223 - }, - { - "From": "SHORT_TON_PER_CUB_FT", - "Input": 42.42, - "To": "LBM_PER_MILLION_GALLON", - "Expect": 11341458333.333334 - }, - { - "From": "SHORT_TON_PER_CUB_FT", - "Input": 42.42, - "To": "SLUG_PER_CUB_FT", - "Expect": 2636.907812555766 - }, - { - "From": "SHORT_TON_PER_CUB_FT", - "Input": 42.42, - "To": "KIP_PER_CUB_FT", - "Expect": 84.84 - }, - { - "From": "SHORT_TON_PER_CUB_FT", - "Input": 42.42, - "To": "SHORT_TON_PER_CUB_FT", - "Expect": 42.42 - }, - { - "From": "PA_S", - "Input": 42.42, - "To": "PA_S", - "Expect": 42.42 - }, - { - "From": "PA_S", - "Input": 42.42, - "To": "POISE", - "Expect": 424.20000000000005 - }, - { - "From": "PA_S", - "Input": 42.42, - "To": "CENTIPOISE", - "Expect": 42420 - }, - { - "From": "PA_S", - "Input": 42.42, - "To": "LBM_PER_FT_S", - "Expect": 28.50492392541788 - }, - { - "From": "POISE", - "Input": 42.42, - "To": "PA_S", - "Expect": 4.242 - }, - { - "From": "POISE", - "Input": 42.42, - "To": "POISE", - "Expect": 42.42 - }, - { - "From": "POISE", - "Input": 42.42, - "To": "CENTIPOISE", - "Expect": 4242 - }, - { - "From": "POISE", - "Input": 42.42, - "To": "LBM_PER_FT_S", - "Expect": 2.850492392541788 - }, - { - "From": "CENTIPOISE", - "Input": 42.42, - "To": "PA_S", - "Expect": 0.04242 - }, - { - "From": "CENTIPOISE", - "Input": 42.42, - "To": "POISE", - "Expect": 0.4242 - }, - { - "From": "CENTIPOISE", - "Input": 42.42, - "To": "CENTIPOISE", - "Expect": 42.42 - }, - { - "From": "CENTIPOISE", - "Input": 42.42, - "To": "LBM_PER_FT_S", - "Expect": 0.02850492392541788 - }, - { - "From": "LBM_PER_FT_S", - "Input": 42.42, - "To": "PA_S", - "Expect": 63.12791448622048 - }, - { - "From": "LBM_PER_FT_S", - "Input": 42.42, - "To": "POISE", - "Expect": 631.2791448622047 - }, - { - "From": "LBM_PER_FT_S", - "Input": 42.42, - "To": "CENTIPOISE", - "Expect": 63127.91448622048 - }, - { - "From": "LBM_PER_FT_S", - "Input": 42.42, - "To": "LBM_PER_FT_S", - "Expect": 42.42 - }, - { - "From": "COULOMB", - "Input": 42.42, - "To": "COULOMB", - "Expect": 42.42 - }, - { - "From": "VOLT", - "Input": 42.42, - "To": "VOLT", - "Expect": 42.42 - }, - { - "From": "VOLT", - "Input": 42.42, - "To": "KILOVOLT", - "Expect": 0.04242 - }, - { - "From": "VOLT", - "Input": 42.42, - "To": "MEGAVOLT", - "Expect": 0.00004242 - }, - { - "From": "KILOVOLT", - "Input": 42.42, - "To": "VOLT", - "Expect": 42420 - }, - { - "From": "KILOVOLT", - "Input": 42.42, - "To": "KILOVOLT", - "Expect": 42.42 - }, - { - "From": "KILOVOLT", - "Input": 42.42, - "To": "MEGAVOLT", - "Expect": 0.04242 - }, - { - "From": "MEGAVOLT", - "Input": 42.42, - "To": "VOLT", - "Expect": 42420000 - }, - { - "From": "MEGAVOLT", - "Input": 42.42, - "To": "KILOVOLT", - "Expect": 42420 - }, - { - "From": "MEGAVOLT", - "Input": 42.42, - "To": "MEGAVOLT", - "Expect": 42.42 - }, - { - "From": "J_PER_CUB_M", - "Input": 42.42, - "To": "J_PER_CUB_M", - "Expect": 42.42 - }, - { - "From": "J_PER_CUB_M", - "Input": 42.42, - "To": "KJ_PER_CUB_M", - "Expect": 0.04242 - }, - { - "From": "J_PER_CUB_M", - "Input": 42.42, - "To": "KWH_PER_CUB_M", - "Expect": 0.000011783333333333335 - }, - { - "From": "J_PER_CUB_M", - "Input": 42.42, - "To": "KWH_PER_CUB_FT", - "Expect": 3.336668423423999e-7 - }, - { - "From": "J_PER_CUB_M", - "Input": 42.42, - "To": "KWH_PER_MILLION_GALLON", - "Expect": 0.0446047688548 - }, - { - "From": "KJ_PER_CUB_M", - "Input": 42.42, - "To": "J_PER_CUB_M", - "Expect": 42420 - }, - { - "From": "KJ_PER_CUB_M", - "Input": 42.42, - "To": "KJ_PER_CUB_M", - "Expect": 42.42 - }, - { - "From": "KJ_PER_CUB_M", - "Input": 42.42, - "To": "KWH_PER_CUB_M", - "Expect": 0.011783333333333333 - }, - { - "From": "KJ_PER_CUB_M", - "Input": 42.42, - "To": "KWH_PER_CUB_FT", - "Expect": 0.0003336668423424 - }, - { - "From": "KJ_PER_CUB_M", - "Input": 42.42, - "To": "KWH_PER_MILLION_GALLON", - "Expect": 44.60476885480001 - }, - { - "From": "KWH_PER_CUB_M", - "Input": 42.42, - "To": "J_PER_CUB_M", - "Expect": 152712000 - }, - { - "From": "KWH_PER_CUB_M", - "Input": 42.42, - "To": "KJ_PER_CUB_M", - "Expect": 152712 - }, - { - "From": "KWH_PER_CUB_M", - "Input": 42.42, - "To": "KWH_PER_CUB_M", - "Expect": 42.42 - }, - { - "From": "KWH_PER_CUB_M", - "Input": 42.42, - "To": "KWH_PER_CUB_FT", - "Expect": 1.2012006324326399 - }, - { - "From": "KWH_PER_CUB_M", - "Input": 42.42, - "To": "KWH_PER_MILLION_GALLON", - "Expect": 160577.16787728 - }, - { - "From": "KWH_PER_CUB_FT", - "Input": 42.42, - "To": "J_PER_CUB_M", - "Expect": 5392973384.371965 - }, - { - "From": "KWH_PER_CUB_FT", - "Input": 42.42, - "To": "KJ_PER_CUB_M", - "Expect": 5392973.384371966 - }, - { - "From": "KWH_PER_CUB_FT", - "Input": 42.42, - "To": "KWH_PER_CUB_M", - "Expect": 1498.0481623255462 - }, - { - "From": "KWH_PER_CUB_FT", - "Input": 42.42, - "To": "KWH_PER_CUB_FT", - "Expect": 42.42 - }, - { - "From": "KWH_PER_CUB_FT", - "Input": 42.42, - "To": "KWH_PER_MILLION_GALLON", - "Expect": 5670729.166666667 - }, - { - "From": "KWH_PER_MILLION_GALLON", - "Input": 42.42, - "To": "J_PER_CUB_M", - "Expect": 40342.24245971756 - }, - { - "From": "KWH_PER_MILLION_GALLON", - "Input": 42.42, - "To": "KJ_PER_CUB_M", - "Expect": 40.34224245971756 - }, - { - "From": "KWH_PER_MILLION_GALLON", - "Input": 42.42, - "To": "KWH_PER_CUB_M", - "Expect": 0.011206178461032655 - }, - { - "From": "KWH_PER_MILLION_GALLON", - "Input": 42.42, - "To": "KWH_PER_CUB_FT", - "Expect": 0.00031732363636363637 - }, - { - "From": "KWH_PER_MILLION_GALLON", - "Input": 42.42, - "To": "KWH_PER_MILLION_GALLON", - "Expect": 42.42 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 42.42 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 2545.2000000000003 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 152712 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 3665088 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 42419.99999999999 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 2545199.9999999995 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 152711999.99999997 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 3665087999.9999995 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 2588627.2244985434 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 155317633.46991262 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 1498.0481623255462 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 89882.88973953277 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 129431361.22492717 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 2971.335198001084 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 123.80563325004512 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 2.0634272208340856 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 35656.022376013 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 1485.6675990005415 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 24.76112665000903 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 9331.0955128473 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 559865.730770838 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 806206652.3100069 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 11206.178461032658 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 672370.7076619593 - }, - { - "From": "CUB_M_PER_SEC", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 968213819.0332216 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 0.7070000000000001 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 42.42 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 2545.2000000000003 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 61084.8 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 706.9999999999998 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 42419.99999999999 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 2545199.9999999995 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 61084799.99999999 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 43143.78707497573 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 2588627.2244985434 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 24.967469372092435 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 1498.0481623255462 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 2157189.3537487867 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 49.52225330001806 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 2.0634272208340856 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 0.03439045368056809 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 594.2670396002167 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 24.76112665000903 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 0.41268544416681713 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 155.51825854745502 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 9331.0955128473 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 13436777.538500112 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 186.76964101721094 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 11206.178461032658 - }, - { - "From": "CUB_M_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 16136896.983887026 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 0.011783333333333333 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 0.7070000000000001 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 42.42 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 1018.08 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 11.783333333333331 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 706.9999999999998 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 42419.99999999999 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 1018079.9999999998 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 719.0631179162621 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 43143.78707497573 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 0.4161244895348739 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 24.967469372092435 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 35953.155895813106 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 0.8253708883336343 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 0.03439045368056809 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 0.0005731742280094683 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 9.904450660003612 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 0.41268544416681713 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 0.00687809073611362 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 2.591970975790917 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 155.51825854745502 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 223946.29230833525 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 3.112827350286849 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 186.76964101721094 - }, - { - "From": "CUB_M_PER_HR", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 268948.2830647838 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 0.0004909722222222222 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 0.029458333333333336 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 1.7675 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 42.42 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 0.4909722222222221 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 29.45833333333333 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 1767.4999999999998 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 42419.99999999999 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 29.96096324651092 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 1797.6577947906553 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 0.01733852039728641 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 1.0403112238371848 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 1498.0481623255462 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 0.03439045368056809 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 0.0014329355700236702 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 0.00002388225950039451 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 0.41268544416681713 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 0.017195226840284045 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 0.00028658711400473413 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 0.10799879065795488 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 6.479927439477292 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 9331.0955128473 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 0.12970113959528537 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 7.782068375717123 - }, - { - "From": "CUB_M_PER_DAY", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 11206.178461032658 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 0.04242000000000001 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 2.545200000000001 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 152.71200000000002 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 3665.088000000001 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 42.42 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 2545.2000000000003 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 152712 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 3665088 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 2588.6272244985444 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 155317.63346991266 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 1.4980481623255462 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 89.88288973953279 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 129431.3612249272 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 2.971335198001084 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 0.12380563325004515 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 0.002063427220834086 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 35.656022376013006 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 1.485667599000542 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 0.024761126650009034 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 9.331095512847304 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 559.8657307708381 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 806206.652310007 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 11.20617846103266 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 672.3707076619595 - }, - { - "From": "LITRE_PER_SEC", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 968213.8190332217 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 0.0007070000000000002 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 0.04242000000000001 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 2.545200000000001 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 61.084800000000016 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 0.7070000000000001 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 42.42 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 2545.2000000000003 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 61084.8 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 43.14378707497575 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 2588.6272244985444 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 0.024967469372092443 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 1.4980481623255462 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 2157.1893537487867 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 0.04952225330001807 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 0.002063427220834086 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 0.0000343904536805681 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 0.5942670396002168 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 0.024761126650009034 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 0.0004126854441668172 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 0.15551825854745507 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 9.331095512847304 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 13436.777538500117 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 0.18676964101721097 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 11.20617846103266 - }, - { - "From": "LITRE_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 16136.896983887029 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 0.000011783333333333338 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 0.0007070000000000002 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 0.04242000000000001 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 1.0180800000000003 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 0.011783333333333333 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 0.7070000000000001 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 42.42 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 1018.08 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 0.7190631179162623 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 43.14378707497575 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 0.0004161244895348741 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 0.024967469372092443 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 35.95315589581312 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 0.0008253708883336344 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 0.0000343904536805681 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 5.731742280094682e-7 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 0.009904450660003613 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 0.0004126854441668172 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 0.000006878090736113619 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 0.0025919709757909176 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 0.15551825854745507 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 223.9462923083353 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 0.0031128273502868497 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 0.18676964101721097 - }, - { - "From": "LITRE_PER_HR", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 268.9482830647838 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 4.909722222222223e-7 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 0.00002945833333333334 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 0.0017675000000000006 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 0.04242000000000001 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 0.0004909722222222222 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 0.029458333333333336 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 1.7675 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 42.42 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 0.02996096324651093 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 1.7976577947906558 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 0.00001733852039728642 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 0.001040311223837185 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 1.4980481623255462 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 0.0000343904536805681 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 0.000001432935570023671 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 2.3882259500394516e-8 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 0.0004126854441668172 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 0.00001719522684028405 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 2.8658711400473416e-7 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 0.0001079987906579549 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 0.006479927439477294 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 9.331095512847304 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 0.00012970113959528541 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 0.007782068375717125 - }, - { - "From": "LITRE_PER_DAY", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 11.20617846103266 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 0.00069513925488 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 0.0417083552928 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 2.502501317568 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 60.06003162163199 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 0.6951392548799998 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 41.70835529279999 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 2502.5013175679996 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 60060.03162163198 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 42.42 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 2545.2000000000003 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 0.02454861111111111 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 1.4729166666666669 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 2121 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 0.048691460055096426 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 0.0020288108356290176 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 0.00003381351392715029 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 0.584297520661157 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 0.024345730027548213 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 0.0004057621671258035 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 0.15290925935914154 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 9.174555561548493 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 13211.360008629828 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 0.18363636363636365 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 11.018181818181818 - }, - { - "From": "CUB_IN_PER_SEC", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 15866.18181818182 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 0.000011585654248 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 0.00069513925488 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 0.0417083552928 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 1.0010005270272 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 0.011585654247999998 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 0.6951392548799998 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 41.70835529279999 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 1001.0005270271997 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 0.7070000000000001 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 42.42 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 0.00040914351851851854 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 0.02454861111111111 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 35.35 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 0.000811524334251607 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 0.00003381351392715029 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 5.635585654525048e-7 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 0.009738292011019284 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 0.0004057621671258035 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 0.000006762702785430058 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 0.0025484876559856923 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 0.15290925935914154 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 220.1893334771638 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 0.0030606060606060605 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 0.18363636363636365 - }, - { - "From": "CUB_IN_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 264.43636363636364 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 1.2012006324326399 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 72.0720379459584 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 4324.3222767575035 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 103783.73464218009 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 1201.2006324326396 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 72072.03794595838 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 4324322.276757503 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 103783734.64218006 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 73301.76000000001 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 4398105.600000001 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 42.42 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 2545.2000000000003 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 3665088 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 84.13884297520661 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 3.5057851239669424 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 0.058429752066115705 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 1009.6661157024794 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 42.06942148760331 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 0.7011570247933885 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 264.22720017259655 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 15853.632010355794 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 22829230.094912343 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 317.32363636363635 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 19039.418181818182 - }, - { - "From": "CUB_FT_PER_SEC", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 27416762.181818184 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 0.020020010540543996 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 1.2012006324326399 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 72.0720379459584 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 1729.7289107030015 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 20.020010540543993 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 1201.2006324326396 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 72072.03794595838 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 1729728.910703001 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 1221.6960000000001 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 73301.76000000001 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 0.7070000000000001 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 42.42 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 61084.8 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 1.402314049586777 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 0.058429752066115705 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 0.0009738292011019284 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 16.827768595041324 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 0.7011570247933885 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 0.011685950413223142 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 4.403786669543276 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 264.22720017259655 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 380487.1682485391 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 5.2887272727272725 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 317.32363636363635 - }, - { - "From": "CUB_FT_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 456946.0363636364 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 0.000013902785097599997 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 0.000834167105856 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 0.050050026351359994 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 1.2012006324326399 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 0.013902785097599995 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 0.8341671058559997 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 50.05002635135998 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 1201.2006324326396 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 0.8484 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 50.904 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 0.0004909722222222222 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 0.029458333333333336 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 42.42 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 0.0009738292011019284 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 0.000040576216712580355 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 6.76270278543006e-7 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 0.011685950413223142 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 0.0004869146005509642 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 0.00000811524334251607 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 0.00305818518718283 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 0.18349111123096984 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 264.22720017259655 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 0.003672727272727273 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 0.22036363636363637 - }, - { - "From": "CUB_FT_PER_DAY", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 317.32363636363635 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 0.605605318851456 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 36.33631913108736 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 2180.1791478652417 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 52324.2995487658 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 605.6053188514559 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 36336.31913108735 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 2180179.1478652414 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 52324299.548765786 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 36956.304 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 2217378.24 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 21.38675 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 1283.2050000000002 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 1847815.2000000002 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 42.42 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 1.7675 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 0.029458333333333336 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 509.04 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 21.21 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 0.35350000000000004 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 133.21454675368412 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 7992.872805221047 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 11509736.839518307 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 159.984 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 9599.04 - }, - { - "From": "ACRE_FT_PER_DAY", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 13822617.600000001 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 14.534527652434944 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 872.0716591460966 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 52324.2995487658 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 1255783.1891703792 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 14534.527652434941 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 872071.6591460964 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 52324299.548765786 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 1255783189.170379 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 886951.296 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 53217077.760000005 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 513.282 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 30796.920000000002 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 44347564.800000004 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 1018.08 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 42.42 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 0.7070000000000001 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 12216.960000000001 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 509.04 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 8.484 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 3197.1491220884186 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 191828.9473253051 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 276233684.14843935 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 3839.616 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 230376.96000000002 - }, - { - "From": "ACRE_FT_PER_HR", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 331742822.4 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 872.0716591460966 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 52324.2995487658 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 3139457.972925948 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 75346991.35022275 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 872071.6591460964 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 52324299.548765786 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 3139457972.9259477 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 75346991350.22275 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 53217077.760000005 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 3193024665.6 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 30796.920000000002 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 1847815.2000000002 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 2660853888 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 61084.8 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 2545.2000000000003 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 42.42 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 733017.6 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 30542.4 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 509.04 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 191828.9473253051 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 11509736.839518307 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 16574021048.906363 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 230376.96000000002 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 13822617.600000001 - }, - { - "From": "ACRE_FT_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 19904569344 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 0.050467109904288 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 3.02802659425728 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 181.6815956554368 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 4360.358295730483 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 50.46710990428799 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 3028.026594257279 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 181681.59565543677 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 4360358.295730483 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 3079.6920000000005 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 184781.52000000002 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 1.7822291666666665 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 106.93375000000002 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 153984.6 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 3.535 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 0.14729166666666665 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 0.0024548611111111112 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 42.42 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 1.7675 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 0.029458333333333336 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 11.101212229473676 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 666.0727337684204 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 959144.7366265256 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 13.332000000000003 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 799.9200000000001 - }, - { - "From": "ACRE_IN_PER_DAY", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 1151884.8 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 1.211210637702912 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 72.67263826217471 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 4360.358295730483 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 104648.5990975316 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 1211.2106377029118 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 72672.6382621747 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 4360358.295730483 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 104648599.09753157 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 73912.60800000001 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 4434756.48 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 42.7735 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 2566.4100000000003 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 3695630.4000000004 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 84.84 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 3.535 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 0.05891666666666666 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 1018.08 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 42.42 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 0.7070000000000001 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 266.42909350736824 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 15985.745610442094 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 23019473.679036614 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 319.968 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 19198.08 - }, - { - "From": "ACRE_IN_PER_HR", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 27645235.200000003 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 72.67263826217471 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 4360.358295730483 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 261621.497743829 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 6278915.945851896 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 72672.6382621747 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 4360358.295730483 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 261621497.74382895 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 6278915945.851895 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 4434756.48 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 266085388.8 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 2566.4100000000003 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 153984.6 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 221737824 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 5090.400000000001 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 212.10000000000002 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 3.535 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 61084.8 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 2545.2000000000003 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 42.42 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 15985.745610442094 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 959144.7366265256 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 1381168420.742197 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 19198.08 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 1151884.8 - }, - { - "From": "ACRE_IN_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 1658714112 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 0.1928451378000001 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 11.570708268000004 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 694.2424960800004 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 16661.819905920005 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 192.84513780000003 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 11570.708268 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 694242.49608 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 16661819.905920003 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 11768.132339020589 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 706087.9403412353 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 6.810261770266544 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 408.6157062159927 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 588406.6169510294 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 13.507957230280747 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 0.5628315512616979 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 0.009380525854361632 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 162.09548676336897 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 6.753978615140376 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 0.1125663102523396 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 42.42 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 2545.2000000000003 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 3665088 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 50.94429583991597 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 3056.6577503949584 - }, - { - "From": "GALLON_IMPERIAL_PER_SEC", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 4401587.160568739 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 0.0032140856300000016 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 0.1928451378000001 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 11.570708268000004 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 277.6969984320001 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 3.2140856300000005 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 192.84513780000003 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 11570.708268 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 277696.99843200005 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 196.1355389836765 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 11768.132339020589 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 0.11350436283777575 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 6.810261770266544 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 9806.776949183823 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 0.2251326205046791 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 0.009380525854361632 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 0.00015634209757269384 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 2.70159144605615 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 0.1125663102523396 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 0.0018761051708723261 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 0.7070000000000001 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 42.42 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 61084.8 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 0.8490715973319328 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 50.94429583991597 - }, - { - "From": "GALLON_IMPERIAL_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 73359.786009479 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 0.000002232003909722223 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 0.00013392023458333338 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 0.008035214075000003 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 0.1928451378000001 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 0.0022320039097222222 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 0.13392023458333335 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 8.035214075 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 192.84513780000003 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 0.1362052354053309 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 8.172314124319852 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 0.00007882247419289982 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 0.004729348451573989 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 6.810261770266544 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 0.00015634209757269384 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 0.00000651425406552891 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 1.0857090109214851e-7 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 0.0018761051708723261 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 0.00007817104878634692 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 0.0000013028508131057817 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 0.0004909722222222222 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 0.029458333333333336 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 42.42 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 0.0005896330537027311 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 0.035377983222163865 - }, - { - "From": "GALLON_IMPERIAL_PER_DAY", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 50.94429583991597 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 0.16057716787728 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 9.6346300726368 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 578.077804358208 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 13873.867304596992 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 160.57716787727998 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 9634.630072636797 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 578077.8043582079 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 13873867.304596988 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 9799.02 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 587941.2000000001 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 5.670729166666667 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 340.24375000000003 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 489951 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 11.247727272727275 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 0.46865530303030306 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 0.007810921717171716 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 134.9727272727273 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 5.6238636363636365 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 0.09373106060606061 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 35.32203891196169 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 2119.322334717702 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 3051824.1619934905 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 42.42 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 2545.2000000000003 - }, - { - "From": "GALLON_PER_SEC", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 3665088 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 0.0026762861312879995 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 0.16057716787728 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 9.6346300726368 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 231.23112174328318 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 2.6762861312879993 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 160.57716787727998 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 9634.630072636797 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 231231.12174328315 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 163.317 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 9799.02 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 0.09451215277777779 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 5.670729166666667 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 8165.85 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 0.18746212121212122 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 0.007810921717171716 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 0.00013018202861952863 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 2.2495454545454545 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 0.09373106060606061 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 0.0015621843434343433 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 0.5887006485326948 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 35.32203891196169 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 50863.73603322484 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 0.7070000000000001 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 42.42 - }, - { - "From": "GALLON_PER_MIN", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 61084.8 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_SEC", - "Expect": 0.0000018585320356166666 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_MIN", - "Expect": 0.00011151192213699999 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_HR", - "Expect": 0.00669071532822 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "CUB_M_PER_DAY", - "Expect": 0.16057716787728 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_SEC", - "Expect": 0.0018585320356166663 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_MIN", - "Expect": 0.11151192213699997 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_HR", - "Expect": 6.690715328219998 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "LITRE_PER_DAY", - "Expect": 160.57716787727998 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "CUB_IN_PER_SEC", - "Expect": 0.11341458333333333 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "CUB_IN_PER_MIN", - "Expect": 6.804875000000001 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "CUB_FT_PER_SEC", - "Expect": 0.00006563343942901234 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "CUB_FT_PER_MIN", - "Expect": 0.003938006365740741 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "CUB_FT_PER_DAY", - "Expect": 5.670729166666667 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "ACRE_FT_PER_DAY", - "Expect": 0.00013018202861952863 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "ACRE_FT_PER_HR", - "Expect": 0.000005424251192480359 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "ACRE_FT_PER_MIN", - "Expect": 9.040418654133933e-8 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "ACRE_IN_PER_DAY", - "Expect": 0.0015621843434343433 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "ACRE_IN_PER_HR", - "Expect": 0.0000650910143097643 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "ACRE_IN_PER_MIN", - "Expect": 0.0000010848502384960721 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_SEC", - "Expect": 0.00040881989481437146 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_MIN", - "Expect": 0.024529193688862288 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "GALLON_IMPERIAL_PER_DAY", - "Expect": 35.32203891196169 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "GALLON_PER_SEC", - "Expect": 0.0004909722222222222 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "GALLON_PER_MIN", - "Expect": 0.029458333333333336 - }, - { - "From": "GALLON_PER_DAY", - "Input": 42.42, - "To": "GALLON_PER_DAY", - "Expect": 42.42 - }, - { - "From": "N", - "Input": 42.42, - "To": "N", - "Expect": 42.42 - }, - { - "From": "N", - "Input": 42.42, - "To": "KN", - "Expect": 0.04242 - }, - { - "From": "N", - "Input": 42.42, - "To": "MN", - "Expect": 42420 - }, - { - "From": "N", - "Input": 42.42, - "To": "KGF", - "Expect": 4.325636175452372 - }, - { - "From": "N", - "Input": 42.42, - "To": "DYNE", - "Expect": 4242000 - }, - { - "From": "N", - "Input": 42.42, - "To": "PDL", - "Expect": 306.8244475683237 - }, - { - "From": "N", - "Input": 42.42, - "To": "SHORT_TON_FORCE", - "Expect": 0.00476819768314486 - }, - { - "From": "N", - "Input": 42.42, - "To": "LONG_TON_FORCE", - "Expect": 0.004257319359950767 - }, - { - "From": "N", - "Input": 42.42, - "To": "LBF", - "Expect": 9.53639536628972 - }, - { - "From": "N", - "Input": 42.42, - "To": "OZF", - "Expect": 152.5823258606355 - }, - { - "From": "N", - "Input": 42.42, - "To": "KPF", - "Expect": 0.009536395366289719 - }, - { - "From": "KN", - "Input": 42.42, - "To": "N", - "Expect": 42420 - }, - { - "From": "KN", - "Input": 42.42, - "To": "KN", - "Expect": 42.42 - }, - { - "From": "KN", - "Input": 42.42, - "To": "MN", - "Expect": 42420000 - }, - { - "From": "KN", - "Input": 42.42, - "To": "KGF", - "Expect": 4325.636175452372 - }, - { - "From": "KN", - "Input": 42.42, - "To": "DYNE", - "Expect": 4242000000 - }, - { - "From": "KN", - "Input": 42.42, - "To": "PDL", - "Expect": 306824.4475683237 - }, - { - "From": "KN", - "Input": 42.42, - "To": "SHORT_TON_FORCE", - "Expect": 4.76819768314486 - }, - { - "From": "KN", - "Input": 42.42, - "To": "LONG_TON_FORCE", - "Expect": 4.257319359950768 - }, - { - "From": "KN", - "Input": 42.42, - "To": "LBF", - "Expect": 9536.39536628972 - }, - { - "From": "KN", - "Input": 42.42, - "To": "OZF", - "Expect": 152582.32586063552 - }, - { - "From": "KN", - "Input": 42.42, - "To": "KPF", - "Expect": 9.53639536628972 - }, - { - "From": "MN", - "Input": 42.42, - "To": "N", - "Expect": 0.04242 - }, - { - "From": "MN", - "Input": 42.42, - "To": "KN", - "Expect": 0.00004242 - }, - { - "From": "MN", - "Input": 42.42, - "To": "MN", - "Expect": 42.42 - }, - { - "From": "MN", - "Input": 42.42, - "To": "KGF", - "Expect": 0.004325636175452372 - }, - { - "From": "MN", - "Input": 42.42, - "To": "DYNE", - "Expect": 4242 - }, - { - "From": "MN", - "Input": 42.42, - "To": "PDL", - "Expect": 0.3068244475683237 - }, - { - "From": "MN", - "Input": 42.42, - "To": "SHORT_TON_FORCE", - "Expect": 0.000004768197683144859 - }, - { - "From": "MN", - "Input": 42.42, - "To": "LONG_TON_FORCE", - "Expect": 0.000004257319359950767 - }, - { - "From": "MN", - "Input": 42.42, - "To": "LBF", - "Expect": 0.00953639536628972 - }, - { - "From": "MN", - "Input": 42.42, - "To": "OZF", - "Expect": 0.15258232586063553 - }, - { - "From": "MN", - "Input": 42.42, - "To": "KPF", - "Expect": 0.00000953639536628972 - }, - { - "From": "KGF", - "Input": 42.42, - "To": "N", - "Expect": 415.998093 - }, - { - "From": "KGF", - "Input": 42.42, - "To": "KN", - "Expect": 0.415998093 - }, - { - "From": "KGF", - "Input": 42.42, - "To": "MN", - "Expect": 415998.093 - }, - { - "From": "KGF", - "Input": 42.42, - "To": "KGF", - "Expect": 42.42 - }, - { - "From": "KGF", - "Input": 42.42, - "To": "DYNE", - "Expect": 41599809.300000004 - }, - { - "From": "KGF", - "Input": 42.42, - "To": "PDL", - "Expect": 3008.9199687459018 - }, - { - "From": "KGF", - "Input": 42.42, - "To": "SHORT_TON_FORCE", - "Expect": 0.04676004580941254 - }, - { - "From": "KGF", - "Input": 42.42, - "To": "LONG_TON_FORCE", - "Expect": 0.04175004090126119 - }, - { - "From": "KGF", - "Input": 42.42, - "To": "LBF", - "Expect": 93.52009161882506 - }, - { - "From": "KGF", - "Input": 42.42, - "To": "OZF", - "Expect": 1496.321465901201 - }, - { - "From": "KGF", - "Input": 42.42, - "To": "KPF", - "Expect": 0.09352009161882507 - }, - { - "From": "DYNE", - "Input": 42.42, - "To": "N", - "Expect": 0.00042420000000000007 - }, - { - "From": "DYNE", - "Input": 42.42, - "To": "KN", - "Expect": 4.242e-7 - }, - { - "From": "DYNE", - "Input": 42.42, - "To": "MN", - "Expect": 0.4242 - }, - { - "From": "DYNE", - "Input": 42.42, - "To": "KGF", - "Expect": 0.000043256361754523724 - }, - { - "From": "DYNE", - "Input": 42.42, - "To": "DYNE", - "Expect": 42.42 - }, - { - "From": "DYNE", - "Input": 42.42, - "To": "PDL", - "Expect": 0.003068244475683238 - }, - { - "From": "DYNE", - "Input": 42.42, - "To": "SHORT_TON_FORCE", - "Expect": 4.76819768314486e-8 - }, - { - "From": "DYNE", - "Input": 42.42, - "To": "LONG_TON_FORCE", - "Expect": 4.2573193599507684e-8 - }, - { - "From": "DYNE", - "Input": 42.42, - "To": "LBF", - "Expect": 0.00009536395366289719 - }, - { - "From": "DYNE", - "Input": 42.42, - "To": "OZF", - "Expect": 0.001525823258606355 - }, - { - "From": "DYNE", - "Input": 42.42, - "To": "KPF", - "Expect": 9.536395366289722e-8 - }, - { - "From": "PDL", - "Input": 42.42, - "To": "N", - "Expect": 5.86477516462992 - }, - { - "From": "PDL", - "Input": 42.42, - "To": "KN", - "Expect": 0.00586477516462992 - }, - { - "From": "PDL", - "Input": 42.42, - "To": "MN", - "Expect": 5864.77516462992 - }, - { - "From": "PDL", - "Input": 42.42, - "To": "KGF", - "Expect": 0.5980406320843428 - }, - { - "From": "PDL", - "Input": 42.42, - "To": "DYNE", - "Expect": 586477.5164629921 - }, - { - "From": "PDL", - "Input": 42.42, - "To": "PDL", - "Expect": 42.42 - }, - { - "From": "PDL", - "Input": 42.42, - "To": "SHORT_TON_FORCE", - "Expect": 0.0006592269531389415 - }, - { - "From": "PDL", - "Input": 42.42, - "To": "LONG_TON_FORCE", - "Expect": 0.0005885954938740547 - }, - { - "From": "PDL", - "Input": 42.42, - "To": "LBF", - "Expect": 1.3184539062778828 - }, - { - "From": "PDL", - "Input": 42.42, - "To": "OZF", - "Expect": 21.095262500446125 - }, - { - "From": "PDL", - "Input": 42.42, - "To": "KPF", - "Expect": 0.0013184539062778826 - }, - { - "From": "SHORT_TON_FORCE", - "Input": 42.42, - "To": "N", - "Expect": 377387.1218387008 - }, - { - "From": "SHORT_TON_FORCE", - "Input": 42.42, - "To": "KN", - "Expect": 377.3871218387008 - }, - { - "From": "SHORT_TON_FORCE", - "Input": 42.42, - "To": "MN", - "Expect": 377387121.8387008 - }, - { - "From": "SHORT_TON_FORCE", - "Input": 42.42, - "To": "KGF", - "Expect": 38482.776670800005 - }, - { - "From": "SHORT_TON_FORCE", - "Input": 42.42, - "To": "DYNE", - "Expect": 37738712183.87008 - }, - { - "From": "SHORT_TON_FORCE", - "Input": 42.42, - "To": "PDL", - "Expect": 2729646.279527559 - }, - { - "From": "SHORT_TON_FORCE", - "Input": 42.42, - "To": "SHORT_TON_FORCE", - "Expect": 42.42 - }, - { - "From": "SHORT_TON_FORCE", - "Input": 42.42, - "To": "LONG_TON_FORCE", - "Expect": 37.875 - }, - { - "From": "SHORT_TON_FORCE", - "Input": 42.42, - "To": "LBF", - "Expect": 84840 - }, - { - "From": "SHORT_TON_FORCE", - "Input": 42.42, - "To": "OZF", - "Expect": 1357440 - }, - { - "From": "SHORT_TON_FORCE", - "Input": 42.42, - "To": "KPF", - "Expect": 84.84 - }, - { - "From": "LONG_TON_FORCE", - "Input": 42.42, - "To": "N", - "Expect": 422673.5764593449 - }, - { - "From": "LONG_TON_FORCE", - "Input": 42.42, - "To": "KN", - "Expect": 422.6735764593449 - }, - { - "From": "LONG_TON_FORCE", - "Input": 42.42, - "To": "MN", - "Expect": 422673576.45934486 - }, - { - "From": "LONG_TON_FORCE", - "Input": 42.42, - "To": "KGF", - "Expect": 43100.70987129601 - }, - { - "From": "LONG_TON_FORCE", - "Input": 42.42, - "To": "DYNE", - "Expect": 42267357645.93449 - }, - { - "From": "LONG_TON_FORCE", - "Input": 42.42, - "To": "PDL", - "Expect": 3057203.833070866 - }, - { - "From": "LONG_TON_FORCE", - "Input": 42.42, - "To": "SHORT_TON_FORCE", - "Expect": 47.510400000000004 - }, - { - "From": "LONG_TON_FORCE", - "Input": 42.42, - "To": "LONG_TON_FORCE", - "Expect": 42.42 - }, - { - "From": "LONG_TON_FORCE", - "Input": 42.42, - "To": "LBF", - "Expect": 95020.8 - }, - { - "From": "LONG_TON_FORCE", - "Input": 42.42, - "To": "OZF", - "Expect": 1520332.8 - }, - { - "From": "LONG_TON_FORCE", - "Input": 42.42, - "To": "KPF", - "Expect": 95.02080000000001 - }, - { - "From": "LBF", - "Input": 42.42, - "To": "N", - "Expect": 188.6935609193504 - }, - { - "From": "LBF", - "Input": 42.42, - "To": "KN", - "Expect": 0.18869356091935038 - }, - { - "From": "LBF", - "Input": 42.42, - "To": "MN", - "Expect": 188693.5609193504 - }, - { - "From": "LBF", - "Input": 42.42, - "To": "KGF", - "Expect": 19.241388335400003 - }, - { - "From": "LBF", - "Input": 42.42, - "To": "DYNE", - "Expect": 18869356.09193504 - }, - { - "From": "LBF", - "Input": 42.42, - "To": "PDL", - "Expect": 1364.8231397637796 - }, - { - "From": "LBF", - "Input": 42.42, - "To": "SHORT_TON_FORCE", - "Expect": 0.02121 - }, - { - "From": "LBF", - "Input": 42.42, - "To": "LONG_TON_FORCE", - "Expect": 0.0189375 - }, - { - "From": "LBF", - "Input": 42.42, - "To": "LBF", - "Expect": 42.42 - }, - { - "From": "LBF", - "Input": 42.42, - "To": "OZF", - "Expect": 678.72 - }, - { - "From": "LBF", - "Input": 42.42, - "To": "KPF", - "Expect": 0.04242 - }, - { - "From": "OZF", - "Input": 42.42, - "To": "N", - "Expect": 11.7933475574594 - }, - { - "From": "OZF", - "Input": 42.42, - "To": "KN", - "Expect": 0.011793347557459399 - }, - { - "From": "OZF", - "Input": 42.42, - "To": "MN", - "Expect": 11793.3475574594 - }, - { - "From": "OZF", - "Input": 42.42, - "To": "KGF", - "Expect": 1.2025867709625002 - }, - { - "From": "OZF", - "Input": 42.42, - "To": "DYNE", - "Expect": 1179334.75574594 - }, - { - "From": "OZF", - "Input": 42.42, - "To": "PDL", - "Expect": 85.30144623523623 - }, - { - "From": "OZF", - "Input": 42.42, - "To": "SHORT_TON_FORCE", - "Expect": 0.001325625 - }, - { - "From": "OZF", - "Input": 42.42, - "To": "LONG_TON_FORCE", - "Expect": 0.00118359375 - }, - { - "From": "OZF", - "Input": 42.42, - "To": "LBF", - "Expect": 2.65125 - }, - { - "From": "OZF", - "Input": 42.42, - "To": "OZF", - "Expect": 42.42 - }, - { - "From": "OZF", - "Input": 42.42, - "To": "KPF", - "Expect": 0.00265125 - }, - { - "From": "KPF", - "Input": 42.42, - "To": "N", - "Expect": 188693.5609193504 - }, - { - "From": "KPF", - "Input": 42.42, - "To": "KN", - "Expect": 188.6935609193504 - }, - { - "From": "KPF", - "Input": 42.42, - "To": "MN", - "Expect": 188693560.9193504 - }, - { - "From": "KPF", - "Input": 42.42, - "To": "KGF", - "Expect": 19241.388335400003 - }, - { - "From": "KPF", - "Input": 42.42, - "To": "DYNE", - "Expect": 18869356091.93504 - }, - { - "From": "KPF", - "Input": 42.42, - "To": "PDL", - "Expect": 1364823.1397637795 - }, - { - "From": "KPF", - "Input": 42.42, - "To": "SHORT_TON_FORCE", - "Expect": 21.21 - }, - { - "From": "KPF", - "Input": 42.42, - "To": "LONG_TON_FORCE", - "Expect": 18.9375 - }, - { - "From": "KPF", - "Input": 42.42, - "To": "LBF", - "Expect": 42420 - }, - { - "From": "KPF", - "Input": 42.42, - "To": "OZF", - "Expect": 678720 - }, - { - "From": "KPF", - "Input": 42.42, - "To": "KPF", - "Expect": 42.42 - }, - { - "From": "N_PER_CUB_M", - "Input": 42.42, - "To": "N_PER_CUB_M", - "Expect": 42.42 - }, - { - "From": "N_PER_CUB_M", - "Input": 42.42, - "To": "KN_PER_CUB_M", - "Expect": 0.04242 - }, - { - "From": "N_PER_CUB_M", - "Input": 42.42, - "To": "N_PER_CUB_FT", - "Expect": 1.2012006324326399 - }, - { - "From": "N_PER_CUB_M", - "Input": 42.42, - "To": "KN_PER_CUB_FT", - "Expect": 0.0012012006324326398 - }, - { - "From": "KN_PER_CUB_M", - "Input": 42.42, - "To": "N_PER_CUB_M", - "Expect": 42420 - }, - { - "From": "KN_PER_CUB_M", - "Input": 42.42, - "To": "KN_PER_CUB_M", - "Expect": 42.42 - }, - { - "From": "KN_PER_CUB_M", - "Input": 42.42, - "To": "N_PER_CUB_FT", - "Expect": 1201.20063243264 - }, - { - "From": "KN_PER_CUB_M", - "Input": 42.42, - "To": "KN_PER_CUB_FT", - "Expect": 1.2012006324326399 - }, - { - "From": "N_PER_CUB_FT", - "Input": 42.42, - "To": "N_PER_CUB_M", - "Expect": 1498.0481623255462 - }, - { - "From": "N_PER_CUB_FT", - "Input": 42.42, - "To": "KN_PER_CUB_M", - "Expect": 1.4980481623255462 - }, - { - "From": "N_PER_CUB_FT", - "Input": 42.42, - "To": "N_PER_CUB_FT", - "Expect": 42.42 - }, - { - "From": "N_PER_CUB_FT", - "Input": 42.42, - "To": "KN_PER_CUB_FT", - "Expect": 0.04242 - }, - { - "From": "KN_PER_CUB_FT", - "Input": 42.42, - "To": "N_PER_CUB_M", - "Expect": 1498048.162325546 - }, - { - "From": "KN_PER_CUB_FT", - "Input": 42.42, - "To": "KN_PER_CUB_M", - "Expect": 1498.0481623255462 - }, - { - "From": "KN_PER_CUB_FT", - "Input": 42.42, - "To": "N_PER_CUB_FT", - "Expect": 42420 - }, - { - "From": "KN_PER_CUB_FT", - "Input": 42.42, - "To": "KN_PER_CUB_FT", - "Expect": 42.42 - }, - { - "From": "HZ", - "Input": 42.42, - "To": "HZ", - "Expect": 42.42 - }, - { - "From": "HZ", - "Input": 42.42, - "To": "KHZ", - "Expect": 0.04242 - }, - { - "From": "HZ", - "Input": 42.42, - "To": "MHZ", - "Expect": 0.00004242 - }, - { - "From": "KHZ", - "Input": 42.42, - "To": "HZ", - "Expect": 42420 - }, - { - "From": "KHZ", - "Input": 42.42, - "To": "KHZ", - "Expect": 42.42 - }, - { - "From": "KHZ", - "Input": 42.42, - "To": "MHZ", - "Expect": 0.04242 - }, - { - "From": "MHZ", - "Input": 42.42, - "To": "HZ", - "Expect": 42420000 - }, - { - "From": "MHZ", - "Input": 42.42, - "To": "KHZ", - "Expect": 42420 - }, - { - "From": "MHZ", - "Input": 42.42, - "To": "MHZ", - "Expect": 42.42 - }, - { - "From": "W_PER_SQ_M", - "Input": 42.42, - "To": "W_PER_SQ_M", - "Expect": 42.42 - }, - { - "From": "W_PER_SQ_M_K", - "Input": 42.42, - "To": "W_PER_SQ_M_K", - "Expect": 42.42 - }, - { - "From": "W_PER_SQ_M_K", - "Input": 42.42, - "To": "W_PER_SQ_M_CELSIUS", - "Expect": 42.42 - }, - { - "From": "W_PER_SQ_M_K", - "Input": 42.42, - "To": "BTU_PER_SQ_FT_HR_FAHRENHEIT", - "Expect": 7.4705939918034145 - }, - { - "From": "W_PER_SQ_M_CELSIUS", - "Input": 42.42, - "To": "W_PER_SQ_M_K", - "Expect": 42.42 - }, - { - "From": "W_PER_SQ_M_CELSIUS", - "Input": 42.42, - "To": "W_PER_SQ_M_CELSIUS", - "Expect": 42.42 - }, - { - "From": "W_PER_SQ_M_CELSIUS", - "Input": 42.42, - "To": "BTU_PER_SQ_FT_HR_FAHRENHEIT", - "Expect": 7.4705939918034145 - }, - { - "From": "BTU_PER_SQ_FT_HR_FAHRENHEIT", - "Input": 42.42, - "To": "W_PER_SQ_M_K", - "Expect": 240.87193093003418 - }, - { - "From": "BTU_PER_SQ_FT_HR_FAHRENHEIT", - "Input": 42.42, - "To": "W_PER_SQ_M_CELSIUS", - "Expect": 240.87193093003418 - }, - { - "From": "BTU_PER_SQ_FT_HR_FAHRENHEIT", - "Input": 42.42, - "To": "BTU_PER_SQ_FT_HR_FAHRENHEIT", - "Expect": 42.42 - }, - { - "From": "LUX", - "Input": 42.42, - "To": "LUX", - "Expect": 42.42 - }, - { - "From": "LUX", - "Input": 42.42, - "To": "LUMEN_PER_SQ_FT", - "Expect": 3.9409469568 - }, - { - "From": "LUMEN_PER_SQ_FT", - "Input": 42.42, - "To": "LUX", - "Expect": 456.6050798768265 - }, - { - "From": "LUMEN_PER_SQ_FT", - "Input": 42.42, - "To": "LUMEN_PER_SQ_FT", - "Expect": 42.42 - }, - { - "From": "SQ_M_PER_SEC", - "Input": 42.42, - "To": "SQ_M_PER_SEC", - "Expect": 42.42 - }, - { - "From": "SQ_M_PER_SEC", - "Input": 42.42, - "To": "SQ_FT_PER_SEC", - "Expect": 456.60507987682644 - }, - { - "From": "SQ_M_PER_SEC", - "Input": 42.42, - "To": "STOKE", - "Expect": 424200 - }, - { - "From": "SQ_M_PER_SEC", - "Input": 42.42, - "To": "CENTISTOKE", - "Expect": 42420000 - }, - { - "From": "SQ_FT_PER_SEC", - "Input": 42.42, - "To": "SQ_M_PER_SEC", - "Expect": 3.9409469568 - }, - { - "From": "SQ_FT_PER_SEC", - "Input": 42.42, - "To": "SQ_FT_PER_SEC", - "Expect": 42.42 - }, - { - "From": "SQ_FT_PER_SEC", - "Input": 42.42, - "To": "STOKE", - "Expect": 39409.46956799999 - }, - { - "From": "SQ_FT_PER_SEC", - "Input": 42.42, - "To": "CENTISTOKE", - "Expect": 3940946.9568 - }, - { - "From": "STOKE", - "Input": 42.42, - "To": "SQ_M_PER_SEC", - "Expect": 0.004242 - }, - { - "From": "STOKE", - "Input": 42.42, - "To": "SQ_FT_PER_SEC", - "Expect": 0.04566050798768265 - }, - { - "From": "STOKE", - "Input": 42.42, - "To": "STOKE", - "Expect": 42.42 - }, - { - "From": "STOKE", - "Input": 42.42, - "To": "CENTISTOKE", - "Expect": 4242.000000000001 - }, - { - "From": "CENTISTOKE", - "Input": 42.42, - "To": "SQ_M_PER_SEC", - "Expect": 0.00004242 - }, - { - "From": "CENTISTOKE", - "Input": 42.42, - "To": "SQ_FT_PER_SEC", - "Expect": 0.00045660507987682646 - }, - { - "From": "CENTISTOKE", - "Input": 42.42, - "To": "STOKE", - "Expect": 0.42419999999999997 - }, - { - "From": "CENTISTOKE", - "Input": 42.42, - "To": "CENTISTOKE", - "Expect": 42.42 - }, - { - "From": "M", - "Input": 42.42, - "To": "M", - "Expect": 42.42 - }, - { - "From": "M", - "Input": 42.42, - "To": "MM", - "Expect": 42420 - }, - { - "From": "M", - "Input": 42.42, - "To": "CM", - "Expect": 4242 - }, - { - "From": "M", - "Input": 42.42, - "To": "DM", - "Expect": 424.20000000000005 - }, - { - "From": "M", - "Input": 42.42, - "To": "KM", - "Expect": 0.04242 - }, - { - "From": "M", - "Input": 42.42, - "To": "UM", - "Expect": 42420000 - }, - { - "From": "M", - "Input": 42.42, - "To": "MILLIINCH", - "Expect": 1670078.7401574804 - }, - { - "From": "M", - "Input": 42.42, - "To": "MICROINCH", - "Expect": 1670078740.1574805 - }, - { - "From": "M", - "Input": 42.42, - "To": "MILLIFOOT", - "Expect": 139173.22834645672 - }, - { - "From": "M", - "Input": 42.42, - "To": "IN", - "Expect": 1670.0787401574805 - }, - { - "From": "M", - "Input": 42.42, - "To": "FT", - "Expect": 139.1732283464567 - }, - { - "From": "M", - "Input": 42.42, - "To": "YRD", - "Expect": 46.39107611548557 - }, - { - "From": "M", - "Input": 42.42, - "To": "CHAIN", - "Expect": 2.1086852779766168 - }, - { - "From": "M", - "Input": 42.42, - "To": "MILE", - "Expect": 0.02635856597470771 - }, - { - "From": "M", - "Input": 42.42, - "To": "US_SURVEY_IN", - "Expect": 1670.0754 - }, - { - "From": "M", - "Input": 42.42, - "To": "US_SURVEY_FT", - "Expect": 139.17295000000001 - }, - { - "From": "M", - "Input": 42.42, - "To": "US_SURVEY_YRD", - "Expect": 46.39098333333333 - }, - { - "From": "M", - "Input": 42.42, - "To": "US_SURVEY_CHAIN", - "Expect": 2.108681060606061 - }, - { - "From": "M", - "Input": 42.42, - "To": "US_SURVEY_MILE", - "Expect": 0.026358513257575756 - }, - { - "From": "M", - "Input": 42.42, - "To": "NAUT_MILE", - "Expect": 0.022904967602591794 - }, - { - "From": "MM", - "Input": 42.42, - "To": "M", - "Expect": 0.04242 - }, - { - "From": "MM", - "Input": 42.42, - "To": "MM", - "Expect": 42.42 - }, - { - "From": "MM", - "Input": 42.42, - "To": "CM", - "Expect": 4.242 - }, - { - "From": "MM", - "Input": 42.42, - "To": "DM", - "Expect": 0.4242 - }, - { - "From": "MM", - "Input": 42.42, - "To": "KM", - "Expect": 0.00004242 - }, - { - "From": "MM", - "Input": 42.42, - "To": "UM", - "Expect": 42420.00000000001 - }, - { - "From": "MM", - "Input": 42.42, - "To": "MILLIINCH", - "Expect": 1670.0787401574805 - }, - { - "From": "MM", - "Input": 42.42, - "To": "MICROINCH", - "Expect": 1670078.7401574804 - }, - { - "From": "MM", - "Input": 42.42, - "To": "MILLIFOOT", - "Expect": 139.1732283464567 - }, - { - "From": "MM", - "Input": 42.42, - "To": "IN", - "Expect": 1.6700787401574804 - }, - { - "From": "MM", - "Input": 42.42, - "To": "FT", - "Expect": 0.1391732283464567 - }, - { - "From": "MM", - "Input": 42.42, - "To": "YRD", - "Expect": 0.04639107611548557 - }, - { - "From": "MM", - "Input": 42.42, - "To": "CHAIN", - "Expect": 0.0021086852779766165 - }, - { - "From": "MM", - "Input": 42.42, - "To": "MILE", - "Expect": 0.000026358565974707707 - }, - { - "From": "MM", - "Input": 42.42, - "To": "US_SURVEY_IN", - "Expect": 1.6700754000000002 - }, - { - "From": "MM", - "Input": 42.42, - "To": "US_SURVEY_FT", - "Expect": 0.13917295000000002 - }, - { - "From": "MM", - "Input": 42.42, - "To": "US_SURVEY_YRD", - "Expect": 0.04639098333333333 - }, - { - "From": "MM", - "Input": 42.42, - "To": "US_SURVEY_CHAIN", - "Expect": 0.002108681060606061 - }, - { - "From": "MM", - "Input": 42.42, - "To": "US_SURVEY_MILE", - "Expect": 0.000026358513257575757 - }, - { - "From": "MM", - "Input": 42.42, - "To": "NAUT_MILE", - "Expect": 0.000022904967602591793 - }, - { - "From": "CM", - "Input": 42.42, - "To": "M", - "Expect": 0.4242 - }, - { - "From": "CM", - "Input": 42.42, - "To": "MM", - "Expect": 424.20000000000005 - }, - { - "From": "CM", - "Input": 42.42, - "To": "CM", - "Expect": 42.42 - }, - { - "From": "CM", - "Input": 42.42, - "To": "DM", - "Expect": 4.242 - }, - { - "From": "CM", - "Input": 42.42, - "To": "KM", - "Expect": 0.00042420000000000007 - }, - { - "From": "CM", - "Input": 42.42, - "To": "UM", - "Expect": 424200 - }, - { - "From": "CM", - "Input": 42.42, - "To": "MILLIINCH", - "Expect": 16700.787401574806 - }, - { - "From": "CM", - "Input": 42.42, - "To": "MICROINCH", - "Expect": 16700787.401574805 - }, - { - "From": "CM", - "Input": 42.42, - "To": "MILLIFOOT", - "Expect": 1391.7322834645672 - }, - { - "From": "CM", - "Input": 42.42, - "To": "IN", - "Expect": 16.700787401574804 - }, - { - "From": "CM", - "Input": 42.42, - "To": "FT", - "Expect": 1.391732283464567 - }, - { - "From": "CM", - "Input": 42.42, - "To": "YRD", - "Expect": 0.4639107611548558 - }, - { - "From": "CM", - "Input": 42.42, - "To": "CHAIN", - "Expect": 0.021086852779766168 - }, - { - "From": "CM", - "Input": 42.42, - "To": "MILE", - "Expect": 0.0002635856597470771 - }, - { - "From": "CM", - "Input": 42.42, - "To": "US_SURVEY_IN", - "Expect": 16.700754 - }, - { - "From": "CM", - "Input": 42.42, - "To": "US_SURVEY_FT", - "Expect": 1.3917295 - }, - { - "From": "CM", - "Input": 42.42, - "To": "US_SURVEY_YRD", - "Expect": 0.46390983333333335 - }, - { - "From": "CM", - "Input": 42.42, - "To": "US_SURVEY_CHAIN", - "Expect": 0.021086810606060605 - }, - { - "From": "CM", - "Input": 42.42, - "To": "US_SURVEY_MILE", - "Expect": 0.0002635851325757576 - }, - { - "From": "CM", - "Input": 42.42, - "To": "NAUT_MILE", - "Expect": 0.00022904967602591794 - }, - { - "From": "DM", - "Input": 42.42, - "To": "M", - "Expect": 4.242 - }, - { - "From": "DM", - "Input": 42.42, - "To": "MM", - "Expect": 4242 - }, - { - "From": "DM", - "Input": 42.42, - "To": "CM", - "Expect": 424.20000000000005 - }, - { - "From": "DM", - "Input": 42.42, - "To": "DM", - "Expect": 42.42 - }, - { - "From": "DM", - "Input": 42.42, - "To": "KM", - "Expect": 0.004242 - }, - { - "From": "DM", - "Input": 42.42, - "To": "UM", - "Expect": 4242000.000000001 - }, - { - "From": "DM", - "Input": 42.42, - "To": "MILLIINCH", - "Expect": 167007.8740157481 - }, - { - "From": "DM", - "Input": 42.42, - "To": "MICROINCH", - "Expect": 167007874.01574808 - }, - { - "From": "DM", - "Input": 42.42, - "To": "MILLIFOOT", - "Expect": 13917.32283464567 - }, - { - "From": "DM", - "Input": 42.42, - "To": "IN", - "Expect": 167.00787401574803 - }, - { - "From": "DM", - "Input": 42.42, - "To": "FT", - "Expect": 13.917322834645672 - }, - { - "From": "DM", - "Input": 42.42, - "To": "YRD", - "Expect": 4.639107611548558 - }, - { - "From": "DM", - "Input": 42.42, - "To": "CHAIN", - "Expect": 0.21086852779766171 - }, - { - "From": "DM", - "Input": 42.42, - "To": "MILE", - "Expect": 0.002635856597470771 - }, - { - "From": "DM", - "Input": 42.42, - "To": "US_SURVEY_IN", - "Expect": 167.00754 - }, - { - "From": "DM", - "Input": 42.42, - "To": "US_SURVEY_FT", - "Expect": 13.917295000000001 - }, - { - "From": "DM", - "Input": 42.42, - "To": "US_SURVEY_YRD", - "Expect": 4.639098333333334 - }, - { - "From": "DM", - "Input": 42.42, - "To": "US_SURVEY_CHAIN", - "Expect": 0.21086810606060608 - }, - { - "From": "DM", - "Input": 42.42, - "To": "US_SURVEY_MILE", - "Expect": 0.0026358513257575757 - }, - { - "From": "DM", - "Input": 42.42, - "To": "NAUT_MILE", - "Expect": 0.0022904967602591798 - }, - { - "From": "KM", - "Input": 42.42, - "To": "M", - "Expect": 42420 - }, - { - "From": "KM", - "Input": 42.42, - "To": "MM", - "Expect": 42420000 - }, - { - "From": "KM", - "Input": 42.42, - "To": "CM", - "Expect": 4242000 - }, - { - "From": "KM", - "Input": 42.42, - "To": "DM", - "Expect": 424200 - }, - { - "From": "KM", - "Input": 42.42, - "To": "KM", - "Expect": 42.42 - }, - { - "From": "KM", - "Input": 42.42, - "To": "UM", - "Expect": 42420000000 - }, - { - "From": "KM", - "Input": 42.42, - "To": "MILLIINCH", - "Expect": 1670078740.1574805 - }, - { - "From": "KM", - "Input": 42.42, - "To": "MICROINCH", - "Expect": 1670078740157.4802 - }, - { - "From": "KM", - "Input": 42.42, - "To": "MILLIFOOT", - "Expect": 139173228.3464567 - }, - { - "From": "KM", - "Input": 42.42, - "To": "IN", - "Expect": 1670078.7401574806 - }, - { - "From": "KM", - "Input": 42.42, - "To": "FT", - "Expect": 139173.2283464567 - }, - { - "From": "KM", - "Input": 42.42, - "To": "YRD", - "Expect": 46391.07611548556 - }, - { - "From": "KM", - "Input": 42.42, - "To": "CHAIN", - "Expect": 2108.6852779766164 - }, - { - "From": "KM", - "Input": 42.42, - "To": "MILE", - "Expect": 26.3585659747077 - }, - { - "From": "KM", - "Input": 42.42, - "To": "US_SURVEY_IN", - "Expect": 1670075.4000000001 - }, - { - "From": "KM", - "Input": 42.42, - "To": "US_SURVEY_FT", - "Expect": 139172.95 - }, - { - "From": "KM", - "Input": 42.42, - "To": "US_SURVEY_YRD", - "Expect": 46390.98333333334 - }, - { - "From": "KM", - "Input": 42.42, - "To": "US_SURVEY_CHAIN", - "Expect": 2108.681060606061 - }, - { - "From": "KM", - "Input": 42.42, - "To": "US_SURVEY_MILE", - "Expect": 26.35851325757576 - }, - { - "From": "KM", - "Input": 42.42, - "To": "NAUT_MILE", - "Expect": 22.904967602591793 - }, - { - "From": "UM", - "Input": 42.42, - "To": "M", - "Expect": 0.00004242 - }, - { - "From": "UM", - "Input": 42.42, - "To": "MM", - "Expect": 0.04242 - }, - { - "From": "UM", - "Input": 42.42, - "To": "CM", - "Expect": 0.004242 - }, - { - "From": "UM", - "Input": 42.42, - "To": "DM", - "Expect": 0.00042419999999999996 - }, - { - "From": "UM", - "Input": 42.42, - "To": "KM", - "Expect": 4.242e-8 - }, - { - "From": "UM", - "Input": 42.42, - "To": "UM", - "Expect": 42.42 - }, - { - "From": "UM", - "Input": 42.42, - "To": "MILLIINCH", - "Expect": 1.6700787401574804 - }, - { - "From": "UM", - "Input": 42.42, - "To": "MICROINCH", - "Expect": 1670.0787401574805 - }, - { - "From": "UM", - "Input": 42.42, - "To": "MILLIFOOT", - "Expect": 0.1391732283464567 - }, - { - "From": "UM", - "Input": 42.42, - "To": "IN", - "Expect": 0.0016700787401574806 - }, - { - "From": "UM", - "Input": 42.42, - "To": "FT", - "Expect": 0.00013917322834645668 - }, - { - "From": "UM", - "Input": 42.42, - "To": "YRD", - "Expect": 0.00004639107611548557 - }, - { - "From": "UM", - "Input": 42.42, - "To": "CHAIN", - "Expect": 0.0000021086852779766164 - }, - { - "From": "UM", - "Input": 42.42, - "To": "MILE", - "Expect": 2.6358565974707707e-8 - }, - { - "From": "UM", - "Input": 42.42, - "To": "US_SURVEY_IN", - "Expect": 0.0016700754 - }, - { - "From": "GRAD", - "Input": 42.42, - "To": "ARC_DEG", - "Expect": 38.178 - }, - { - "From": "ARC_DEG", - "Input": 42.42, - "To": "GRAD", - "Expect": 47.13333333333334 - }, - { - "From": "ARC_MINUTE", - "Input": 42.42, - "To": "ARC_DEG", - "Expect": 0.7070000000000001 - }, - { - "From": "ARC_DEG", - "Input": 42.42, - "To": "ARC_MINUTE", - "Expect": 2545.2000000000003 - }, - { - "From": "GRAD", - "Input": 42.42, - "To": "ARC_QUADRANT", - "Expect": 0.42419999999999997 - }, - { - "From": "ARC_QUADRANT", - "Input": 42.42, - "To": "GRAD", - "Expect": 4242 - }, - { - "From": "ARC_MINUTE", - "Input": 42.42, - "To": "ARC_QUADRANT", - "Expect": 0.007855555555555555 - }, - { - "From": "ARC_QUADRANT", - "Input": 42.42, - "To": "ARC_MINUTE", - "Expect": 229068 - }, - { - "From": "GRAD", - "Input": 42.42, - "To": "REVOLUTION", - "Expect": 0.10604999999999999 - }, - { - "From": "REVOLUTION", - "Input": 42.42, - "To": "GRAD", - "Expect": 16968 - }, - { - "From": "ARC_MINUTE", - "Input": 42.42, - "To": "REVOLUTION", - "Expect": 0.0019638888888888887 - }, - { - "From": "REVOLUTION", - "Input": 42.42, - "To": "ARC_MINUTE", - "Expect": 916272 - }, - { - "From": "DEG_PER_SEC", - "Input": 42.42, - "To": "RAD_PER_MIN", - "Expect": 44.422120121759676 - }, - { - "From": "RAD_PER_MIN", - "Input": 42.42, - "To": "DEG_PER_SEC", - "Expect": 40.5081161157492 - }, - { - "From": "DEG_PER_HR", - "Input": 42.42, - "To": "RPM", - "Expect": 0.001963888888888889 - }, - { - "From": "RPM", - "Input": 42.42, - "To": "DEG_PER_HR", - "Expect": 916272 - }, - { - "From": "KILOPASCAL", - "Input": 42.42, - "To": "PSI", - "Expect": 6.152500834515474 - }, - { - "From": "BARYE", - "Input": 42.42, - "To": "HECTOPASCAL", - "Expect": 0.04242 - }, - { - "From": "SQ_FT_HR_FAHRENHEIT_PER_BTU", - "Input": 42.42, - "To": "SQ_M_CELSIUS_PER_WATT", - "Expect": 7.470593991803412 - }, - { - "From": "SQ_M_CELSIUS_PER_WATT", - "Input": 42.42, - "To": "SQ_FT_HR_FAHRENHEIT_PER_BTU", - "Expect": 240.8719309300342 - }, - { - "From": "FT_PER_IN", - "Input": 42.42, - "To": "PERCENT_SLOPE", - "Expect": 50904 - }, - { - "From": "PERCENT_SLOPE", - "Input": 42.42, - "To": "FT_PER_IN", - "Expect": 0.03535 - }, - { - "From": "AT", - "Input": 42.42, - "To": "AT_GAUGE", - "Expect": 41.38677254720012 - }, - { - "From": "AT_GAUGE", - "Input": 42.42, - "To": "AT", - "Expect": 43.45322745279989 - }, - { - "From": "AT_GAUGE", - "Input": 42.42, - "To": "PA_GAUGE", - "Expect": 4159980.93 - }, - { - "From": "PA_GAUGE", - "Input": 42.42, - "To": "AT_GAUGE", - "Expect": 0.0004325636175450151 - }, - { - "From": "BAR", - "Input": 42.42, - "To": "BAR_GAUGE", - "Expect": 41.406749999999995 - }, - { - "From": "BAR_GAUGE", - "Input": 42.42, - "To": "BAR", - "Expect": 43.433249999999994 - }, - { - "From": "TORR", - "Input": 42.42, - "To": "AT_GAUGE", - "Expect": -0.9755570468159765 - }, - { - "From": "AT_GAUGE", - "Input": 42.42, - "To": "TORR", - "Expect": 31962.42296373057 - }, - { - "From": "KILOPASCAL_GAUGE", - "Input": 42.42, - "To": "PA_GAUGE", - "Expect": 42420 - }, - { - "From": "PA_GAUGE", - "Input": 42.42, - "To": "KILOPASCAL_GAUGE", - "Expect": 0.04242 - }, - { - "From": "KILOPASCAL_GAUGE", - "Input": 42.42, - "To": "PSIG", - "Expect": 6.152500834515474 - }, - { - "From": "PSIG", - "Input": 42.42, - "To": "KILOPASCAL_GAUGE", - "Expect": 292.47560437620194 - }, - { - "From": "FAHRENHEIT", - "Input": 42.42, - "To": "K", - "Expect": 278.93888888888887 - }, - { - "From": "K", - "Input": 42.42, - "To": "FAHRENHEIT", - "Expect": -383.31399999999996 - }, - { - "From": "CELSIUS", - "Input": 42.42, - "To": "RANKINE", - "Expect": 568.026 - }, - { - "From": "RANKINE", - "Input": 42.42, - "To": "CELSIUS", - "Expect": -249.58333333333331 - }, - { - "From": "FAHRENHEIT", - "Input": 42.42, - "To": "CELSIUS", - "Expect": 5.7888888888888985 - }, - { - "From": "CELSIUS", - "Input": 42.42, - "To": "FAHRENHEIT", - "Expect": 108.35600000000001 - } -] diff --git a/core/electron-manager/package.json b/core/electron-manager/package.json index 7f3b3cd7308..49c59a58113 100644 --- a/core/electron-manager/package.json +++ b/core/electron-manager/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/electron-manager", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iTwin.js ElectronHost and ElectronApp", "license": "MIT", "engines": { @@ -30,21 +30,23 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/imodeljs-common": "^2.15.0-dev.6", - "@bentley/imodeljs-frontend": "^2.15.0-dev.6", - "@bentley/imodeljs-backend": "^2.15.0-dev.6", - "@bentley/itwin-client": "^2.15.0-dev.6", + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/imodeljs-common": "^2.15.0-dev.13", + "@bentley/imodeljs-frontend": "^2.15.0-dev.13", + "@bentley/imodeljs-backend": "^2.15.0-dev.13", + "@bentley/itwin-client": "^2.15.0-dev.13", + "@bentley/presentation-common": "^2.15.0-dev.13", "electron": "^11.1.0" }, "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/presentation-common": "2.15.0-dev.13", "@types/node": "10.14.1", "electron": "^11.1.0", "eslint": "^6.8.0", diff --git a/core/electron-manager/src/backend/ElectronAuthorizationBackend.ts b/core/electron-manager/src/backend/ElectronAuthorizationBackend.ts index 2d045df69ba..1cd965d76d2 100644 --- a/core/electron-manager/src/backend/ElectronAuthorizationBackend.ts +++ b/core/electron-manager/src/backend/ElectronAuthorizationBackend.ts @@ -72,9 +72,9 @@ export class ElectronAuthorizationBackend extends NativeAppAuthorizationBackend if (tokenResponse === undefined || tokenResponse.refreshToken === undefined) return undefined; try { - Logger.logTrace(loggerCategory, "Refreshing token from storage"); return await this.refreshAccessToken(tokenResponse.refreshToken); } catch (err) { + Logger.logError(loggerCategory, `Error refreshing access token`, () => err); return undefined; } } @@ -99,7 +99,7 @@ export class ElectronAuthorizationBackend extends NativeAppAuthorizationBackend // Create the authorization request const nativeConfig = this._config!; const authReqJson: AuthorizationRequestJson = { - client_id: nativeConfig.clientId, // eslint-disable-line @typescript-eslint/naming-convention + client_id: nativeConfig.clientId, // eslint-disable-line @typescript-eslint/naming-convention redirect_uri: nativeConfig.redirectUri, // eslint-disable-line @typescript-eslint/naming-convention scope: nativeConfig.scope, response_type: AuthorizationRequest.RESPONSE_TYPE_CODE, // eslint-disable-line @typescript-eslint/naming-convention @@ -133,7 +133,7 @@ export class ElectronAuthorizationBackend extends NativeAppAuthorizationBackend if (!tokenResponse) await this.clearTokenResponse(); else - this.setAccessToken(await this.setTokenResponse(tokenResponse)); + await this.setTokenResponse(tokenResponse); }); // Start the signin @@ -204,12 +204,14 @@ export class ElectronAuthorizationBackend extends NativeAppAuthorizationBackend private async clearTokenResponse() { this._tokenResponse = undefined; await this.tokenStore.delete(); + this.setAccessToken(undefined); } private async setTokenResponse(tokenResponse: TokenResponse): Promise { const accessToken = await this.createAccessTokenFromResponse(tokenResponse); this._tokenResponse = tokenResponse; await this.tokenStore.save(this._tokenResponse); + this.setAccessToken(accessToken); return accessToken; } diff --git a/core/electron-manager/src/backend/ElectronHost.ts b/core/electron-manager/src/backend/ElectronHost.ts index d0bb4a9c064..7298a7f5a1a 100644 --- a/core/electron-manager/src/backend/ElectronHost.ts +++ b/core/electron-manager/src/backend/ElectronHost.ts @@ -17,7 +17,7 @@ import { IModelError, IpcListener, IpcSocketBackend, RemoveFunction, RpcConfigur import { ElectronRpcConfiguration, ElectronRpcManager } from "../common/ElectronRpcManager"; import { ElectronAuthorizationBackend } from "./ElectronAuthorizationBackend"; -// cSpell:ignore signin devserver webcontents copyfile +// cSpell:ignore signin devserver webcontents copyfile unmaximize eopt class ElectronIpc implements IpcSocketBackend { public addListener(channel: string, listener: IpcListener): RemoveFunction { @@ -57,6 +57,8 @@ export interface ElectronHostOptions { rpcInterfaces?: RpcInterfaceDefinition[]; /** list of [IpcHandler]($common) classes to register */ ipcHandlers?: (typeof IpcHandler)[]; + /** name of application. Used for naming settings file. */ + applicationName?: string; } /** @beta */ @@ -64,6 +66,21 @@ export interface ElectronHostOpts extends NativeHostOpts { electronHost?: ElectronHostOptions; } +/** @beta */ +export interface ElectronHostWindowOptions extends BrowserWindowConstructorOptions { + storeWindowName?: string; +} + +/** the size and position of a window as stored in the settings file. + * @beta + */ +export interface WindowSizeAndPositionProps { + width: number; + height: number; + x: number; + y: number; +} + /** * The backend for Electron-based desktop applications * @beta @@ -113,7 +130,7 @@ export class ElectronHost { return assetPath; } - private static _openWindow(options: BrowserWindowConstructorOptions = {}) { + private static _openWindow(options?: ElectronHostWindowOptions) { const opts: BrowserWindowConstructorOptions = { autoHideMenuBar: true, webPreferences: { @@ -133,16 +150,53 @@ export class ElectronHost { this._mainWindow.on("closed", () => this._mainWindow = undefined); this._mainWindow.loadURL(this.frontendURL); // eslint-disable-line @typescript-eslint/no-floating-promises + /** Monitors and saves main window size, position and maximized state */ + if (options?.storeWindowName) { + const mainWindow = this._mainWindow; + const name = options.storeWindowName; + const saveWindowPosition = () => { + const resolution = mainWindow.getSize(); + const position = mainWindow.getPosition(); + const pos: WindowSizeAndPositionProps = { + width: resolution[0], + height: resolution[1], + x: position[0], + y: position[1], + }; + NativeHost.settingsStore.setData(`windowPos-${name}`, JSON.stringify(pos)); + }; + const saveMaximized = (maximized: boolean) => { + if (!maximized) + saveWindowPosition(); + NativeHost.settingsStore.setData(`windowMaximized-${name}`, maximized); + }; + + mainWindow.on("resized", () => saveWindowPosition()); + mainWindow.on("moved", () => saveWindowPosition()); + mainWindow.on("maximize", () => saveMaximized(true)); + mainWindow.on("unmaximize", () => saveMaximized(false)); + } } /** The "main" BrowserWindow for this application. */ public static get mainWindow() { return this._mainWindow; } + /** Gets window size and position for a window, by name, from settings file, if present */ + public static getWindowSizeSetting(windowName: string): WindowSizeAndPositionProps | undefined { + const saved = NativeHost.settingsStore.getString(`windowPos-${windowName}`); + return saved ? JSON.parse(saved) as WindowSizeAndPositionProps : undefined; + } + + /** Gets "window maximized" flag for a window, by name, from settings file if present */ + public static getWindowMaximizedSetting(windowName: string): boolean | undefined { + return NativeHost.settingsStore.getBoolean(`windowMaximized-${windowName}`); + } + /** * Open the main Window when the app is ready. * @param windowOptions Options for constructing the main BrowserWindow. See: https://electronjs.org/docs/api/browser-window#new-browserwindowoptions */ - public static async openMainWindow(windowOptions?: BrowserWindowConstructorOptions): Promise { + public static async openMainWindow(windowOptions?: ElectronHostWindowOptions): Promise { const app = this.app; // quit the application when all windows are closed (unless we're running on MacOS) app.on("window-all-closed", () => { @@ -179,6 +233,7 @@ export class ElectronHost { this._openWindow(windowOptions); } + public static get isValid() { return this._ipc !== undefined; } /** @@ -207,6 +262,7 @@ export class ElectronHost { this.appIconPath = path.join(this.webResourcesPath, eopt?.iconName ?? "appicon.ico"); this.rpcConfig = ElectronRpcManager.initializeBackend(this._ipc, eopt?.rpcInterfaces); } + opts = opts ?? {}; opts.ipcHost = opts.ipcHost ?? {}; opts.ipcHost.socket = this._ipc; diff --git a/core/electron-manager/src/common/ElectronRpcManager.ts b/core/electron-manager/src/common/ElectronRpcManager.ts index a263da82909..bc7bca9ed9b 100644 --- a/core/electron-manager/src/common/ElectronRpcManager.ts +++ b/core/electron-manager/src/common/ElectronRpcManager.ts @@ -6,8 +6,9 @@ * @module RpcInterface */ +import { IModelReadRpcInterface, IModelTileRpcInterface, IModelWriteRpcInterface, IpcSocket, IpcSocketBackend, IpcSocketFrontend, RpcConfiguration, RpcInterfaceDefinition, RpcManager, SnapshotIModelRpcInterface } from "@bentley/imodeljs-common"; +import { PresentationRpcInterface } from "@bentley/presentation-common"; import { ElectronRpcProtocol } from "./ElectronRpcProtocol"; -import { IpcSocket, IpcSocketBackend, IpcSocketFrontend, RpcConfiguration, RpcInterfaceDefinition, RpcManager } from "@bentley/imodeljs-common"; /** RPC interface configuration for an Electron-based application. * @internal @@ -35,7 +36,13 @@ export class ElectronRpcManager extends RpcManager { } private static performInitialization(ipcSocket: IpcSocket, rpcs?: RpcInterfaceDefinition[]): ElectronRpcConfiguration { - const interfaces = rpcs ?? []; + const interfaces = rpcs ?? [ + IModelReadRpcInterface, + IModelTileRpcInterface, + IModelWriteRpcInterface, + SnapshotIModelRpcInterface, + PresentationRpcInterface, + ]; const config = class extends ElectronRpcConfiguration { public interfaces = () => interfaces; public protocol: ElectronRpcProtocol = new ElectronRpcProtocol(this, ipcSocket); diff --git a/core/express-server/package.json b/core/express-server/package.json index 48a248f3ab7..13584a313a1 100644 --- a/core/express-server/package.json +++ b/core/express-server/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/express-server", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js express utilities", "main": "lib/ExpressServer.js", "typings": "lib/ExpressServer", @@ -32,9 +32,9 @@ "url": "http://www.bentley.com" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", "@types/body-parser": "^1.17.0", "@types/chai": "^4.1.4", "@types/express": "^4.16.1", diff --git a/core/frontend-devtools/README.md b/core/frontend-devtools/README.md index e69e8bf25de..1b57cbf5731 100644 --- a/core/frontend-devtools/README.md +++ b/core/frontend-devtools/README.md @@ -225,8 +225,9 @@ These keysins control the planar masking of reality models. ### Other key-ins -* `fdt save view` - Copies to the clipboard a JSON representation of the view currently displayed in the active viewport. -* `fdt apply view` - Accepts an unquoted JSON representation of a view, e.g., as obtained from `fdt save view`, and applies that view to the active viewport. +* `fdt save view` - Copies to the clipboard a JSON representation of the view currently displayed in the active viewport. Accepts a single optional argument: + * `quote=1`: format the result so it can be directly parsed by `fdt apply view` as a single quoted string argument. +* `fdt apply view` - Given a saved view as a JSON string (see `fdt save view`), applies it to the active view. Takes a single required argument: the JSON string, in quotes, with interior quotation marks escaped as `""`. Use the `quote=1` argument to `fdt save view` to enquote and escape the JSON. * `fdt apply viewid` - Accepts the Id of a persistent ViewDefinition in hexadecimal format and applies that view to the active viewport. * `fdt save rendering style` - Outputs selected aspects of the active viewport's display style as JSON. See `DisplayStyleSettings.toOverrides`. Each argument is of the format "option=value" where `value` is 0 for false or 1 for true. All arguments default to false. * `all`: include all settings. diff --git a/core/frontend-devtools/package.json b/core/frontend-devtools/package.json index cbb377f1b21..d493c818c82 100644 --- a/core/frontend-devtools/package.json +++ b/core/frontend-devtools/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/frontend-devtools", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "Debug menu and supporting UI widgets", "main": "lib/frontend-devtools.js", "imodeljsSharedLibrary": true, @@ -33,19 +33,19 @@ "url": "http://www.bentley.com" }, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/context-registry-client": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/context-registry-client": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", "file-saver": "^2.0.2" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/extension-webpack-tools": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/extension-webpack-tools": "2.15.0-dev.13", "@types/file-saver": "^2.0.1", "@types/node": "10.14.1", "cpx": "^1.5.0", diff --git a/core/frontend-devtools/src/tools/PlanarMaskTools.ts b/core/frontend-devtools/src/tools/PlanarMaskTools.ts index 990d3eaf0f0..e7aa3d533a6 100644 --- a/core/frontend-devtools/src/tools/PlanarMaskTools.ts +++ b/core/frontend-devtools/src/tools/PlanarMaskTools.ts @@ -339,7 +339,7 @@ export class SetHigherPriorityRealityModelMasking extends PlanarMaskBaseTool { public parseAndRun(...args: string[]): boolean { super.parseAndRun(...args); - const priority = parseInt(args[1], 10); + const priority = parseInt(args[0], 10); this._priority = (priority === undefined || isNaN(priority)) ? 0 : priority; return this.run(); } diff --git a/core/frontend-devtools/src/tools/SavedViews.ts b/core/frontend-devtools/src/tools/SavedViews.ts index 486be2c9cf6..b9bb2473922 100644 --- a/core/frontend-devtools/src/tools/SavedViews.ts +++ b/core/frontend-devtools/src/tools/SavedViews.ts @@ -12,6 +12,7 @@ import { EntityState, IModelApp, IModelConnection, NotifyMessageDetails, OutputMessagePriority, Tool, ViewState, } from "@bentley/imodeljs-frontend"; import { copyStringToClipboard } from "../ClipboardUtilities"; +import { parseArgs } from "./parseArgs"; /** Serialize a ViewState to JSON. The returned JSON can later be passed to [deserializeViewState] to reinstantiate the ViewState. * @beta @@ -37,11 +38,34 @@ export async function deserializeViewState(props: ViewStateProps, iModel: IModel } /** Copies a JSON representation of the active viewport's view to the clipboard. + * * Arguments: + * * `quote`: format the JSON so it can be parsed directly by [ApplyViewTool]. * @beta */ export class SaveViewTool extends Tool { + private _quote = false; + public static get minArgs() { return 0; } + public static get maxArgs() { return 1; } public static toolId = "SaveView"; + public parse(inputArgs: string[]) { + const args = parseArgs(inputArgs); + function getArg(name: string): true | undefined { + return args.getBoolean(name) ? true : undefined; + } + + this._quote = true === getArg("q"); + + return true; + } + + public parseAndRun(...args: string[]): boolean { + if (this.parse(args)) + return this.run(); + else + return false; + } + public run(): boolean { const vp = IModelApp.viewManager.selectedView; if (undefined === vp) { @@ -50,8 +74,10 @@ export class SaveViewTool extends Tool { } try { - const json = serializeViewState(vp.view); - copyStringToClipboard(JSON.stringify(json)); + let json = JSON.stringify(serializeViewState(vp.view)); + if (this._quote) + json = `"${json.replace(/"/g, '""')}"`; + copyStringToClipboard(json); IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Info, "JSON copied to clipboard")); } catch (err) { IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, err.toString())); @@ -62,12 +88,13 @@ export class SaveViewTool extends Tool { } /** Given a string containing a JSON representation of a ViewState, applies that ViewState to the active viewport. - * ###TODO The JSON string should be enclosed in double quotes. Currently parser does not handle quoted arguments at all. + * The JSON string should be enclosed in double quotes and embedded double quote should be duplicated, example: + * - "{""viewDefinitionProps"":{""classFullName"":""BisCore:SpatialViewDefinition"",""id"":""0x1a""}}" * @beta */ export class ApplyViewTool extends Tool { public static toolId = "ApplyView"; - public static get maxArgs() { return undefined; } // ###TODO Once quoted argument parsing is implemented, this should be 1. + public static get maxArgs() { return 1; } public static get minArgs() { return 1; } public run(view?: ViewState): boolean { @@ -84,8 +111,7 @@ export class ApplyViewTool extends Tool { return true; try { - const arg = args.join(""); - const json = JSON.parse(arg); + const json = JSON.parse(args[0]); // ###TODO: async... deserializeViewState(json, vp.iModel).then((view) => this.run(view)); // eslint-disable-line @typescript-eslint/no-floating-promises diff --git a/core/frontend-devtools/src/widgets/MemoryTracker.ts b/core/frontend-devtools/src/widgets/MemoryTracker.ts index 1361d74ee18..12a011c1274 100644 --- a/core/frontend-devtools/src/widgets/MemoryTracker.ts +++ b/core/frontend-devtools/src/widgets/MemoryTracker.ts @@ -215,7 +215,7 @@ export class MemoryTracker { table.appendChild(row1); this._textures = new MemoryPanel(cell00, "Textures", ["Surface Textures", "Vertex Tables", "Feature Tables", "Feature Overrides", "Clip Volumes", "Planar Classifiers", "Shadow Maps", "Texture Attachments", "Thematic Textures"]); - this._buffers = new MemoryPanel(cell01, "Buffers", ["Surfaces", "Visible Edges", "Silhouettes", "Polyline Edges", "Polylines", "Point Strings", "Point Clouds", "Instances", "Terrain"]); + this._buffers = new MemoryPanel(cell01, "Buffers", ["Surfaces", "Visible Edges", "Silhouettes", "Polyline Edges", "Polylines", "Point Strings", "Point Clouds", "Instances", "Terrain", "Reality Mesh"]); this._totalElem = this.addStatistics(cell10); this._totalTreesElem = this.addStatistics(cell11); diff --git a/core/frontend/package.json b/core/frontend/package.json index 4c90fd25c8d..7a5f6a4da24 100644 --- a/core/frontend/package.json +++ b/core/frontend/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/imodeljs-frontend", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js frontend components", "main": "lib/imodeljs-frontend.js", "typings": "lib/imodeljs-frontend", @@ -36,43 +36,43 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/frontend-authorization-client": "^2.15.0-dev.6", - "@bentley/geometry-core": "^2.15.0-dev.6", - "@bentley/imodelhub-client": "^2.15.0-dev.6", - "@bentley/imodeljs-common": "^2.15.0-dev.6", - "@bentley/imodeljs-i18n": "^2.15.0-dev.6", - "@bentley/imodeljs-quantity": "^2.15.0-dev.6", - "@bentley/itwin-client": "^2.15.0-dev.6", - "@bentley/orbitgt-core": "^2.15.0-dev.6", - "@bentley/product-settings-client": "^2.15.0-dev.6", - "@bentley/rbac-client": "^2.15.0-dev.6", - "@bentley/telemetry-client": "^2.15.0-dev.6", - "@bentley/ui-abstract": "^2.15.0-dev.6", - "@bentley/webgl-compatibility": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/frontend-authorization-client": "^2.15.0-dev.13", + "@bentley/geometry-core": "^2.15.0-dev.13", + "@bentley/imodelhub-client": "^2.15.0-dev.13", + "@bentley/imodeljs-common": "^2.15.0-dev.13", + "@bentley/imodeljs-i18n": "^2.15.0-dev.13", + "@bentley/imodeljs-quantity": "^2.15.0-dev.13", + "@bentley/itwin-client": "^2.15.0-dev.13", + "@bentley/orbitgt-core": "^2.15.0-dev.13", + "@bentley/product-settings-client": "^2.15.0-dev.13", + "@bentley/rbac-client": "^2.15.0-dev.13", + "@bentley/telemetry-client": "^2.15.0-dev.13", + "@bentley/ui-abstract": "^2.15.0-dev.13", + "@bentley/webgl-compatibility": "^2.15.0-dev.13" }, "//devDependencies": [ "NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install", "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/certa": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/frontend-authorization-client": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodelhub-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", - "@bentley/imodeljs-quantity": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/orbitgt-core": "2.15.0-dev.6", - "@bentley/product-settings-client": "2.15.0-dev.6", - "@bentley/rbac-client": "2.15.0-dev.6", - "@bentley/telemetry-client": "2.15.0-dev.6", - "@bentley/ui-abstract": "2.15.0-dev.6", - "@bentley/webgl-compatibility": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/certa": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/frontend-authorization-client": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodelhub-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", + "@bentley/imodeljs-quantity": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/orbitgt-core": "2.15.0-dev.13", + "@bentley/product-settings-client": "2.15.0-dev.13", + "@bentley/rbac-client": "2.15.0-dev.13", + "@bentley/telemetry-client": "2.15.0-dev.13", + "@bentley/ui-abstract": "2.15.0-dev.13", + "@bentley/webgl-compatibility": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", "@types/js-base64": "^2.3.1", @@ -99,13 +99,12 @@ ], "dependencies": { "@azure/storage-blob": "10.4.0", - "@bentley/context-registry-client": "2.15.0-dev.6", - "@bentley/extension-client": "2.15.0-dev.6", - "@bentley/reality-data-client": "2.15.0-dev.6", - "@bentley/usage-logging-client": "2.15.0-dev.6", + "@bentley/context-registry-client": "2.15.0-dev.13", + "@bentley/extension-client": "2.15.0-dev.13", + "@bentley/reality-data-client": "2.15.0-dev.13", + "@bentley/usage-logging-client": "2.15.0-dev.13", "fuse.js": "^3.3.0", "js-base64": "^2.4.5", - "ldclient-js": "^2.6.0", "oidc-client": "^1.9.1", "semver": "^5.5.0", "xml-js": "~1.6.11", diff --git a/core/frontend/src/AccuSnap.ts b/core/frontend/src/AccuSnap.ts index 677492c0a21..914edf2d465 100644 --- a/core/frontend/src/AccuSnap.ts +++ b/core/frontend/src/AccuSnap.ts @@ -15,6 +15,7 @@ import { IModelApp } from "./IModelApp"; import { CanvasDecoration } from "./render/CanvasDecoration"; import { IconSprites, Sprite, SpriteLocation } from "./Sprites"; import { BeButton, BeButtonEvent, BeTouchEvent, InputSource } from "./tools/Tool"; +import { ToolSettings } from "./tools/ToolSettings"; import { DecorateContext } from "./ViewContext"; import { Decorator } from "./ViewManager"; import { ScreenViewport, Viewport } from "./Viewport"; @@ -125,6 +126,8 @@ export class TouchCursor implements CanvasDecoration { } public doTouchEnd(ev: BeTouchEvent): void { + if (this._isDragging && undefined !== ev.viewport) + IModelApp.toolAdmin.currentInputState.fromPoint(ev.viewport, this._offsetPosition, InputSource.Touch); // Current location should reflect virtual cursor offset... this._isSelected = this._isDragging = false; if (undefined !== ev.viewport) ev.viewport.invalidateDecorations(); @@ -566,7 +569,7 @@ export class AccuSnap implements Decorator { this.toolState.enabled = yesNo; if (!yesNo) { this.clear(); - if (undefined !== this.touchCursor) { + if (undefined !== this.touchCursor && !this.wantVirtualCursor) { this.touchCursor = undefined; IModelApp.viewManager.invalidateDecorationsAllViews(); } @@ -705,28 +708,39 @@ export class AccuSnap implements Decorator { }; // If this hit is from a plan projection model, apply the model's elevation to the snap point for display. + // Likewise, if it is a hit on a model with a display transform, apply the model's transform to the snap point. let snapPoint = result.snapPoint!; const elevation = undefined !== thisHit.modelId ? thisHit.viewport.view.getModelElevation(thisHit.modelId) : 0; - if (0 !== elevation) { + if (0 !== elevation || undefined !== thisHit.viewport.view.modelDisplayTransformProvider) { const adjustedSnapPoint = Point3d.fromJSON(snapPoint); + thisHit.viewport.view.transformPointByModelDisplayTransform(thisHit.modelId, adjustedSnapPoint, false); adjustedSnapPoint.z += elevation; snapPoint = adjustedSnapPoint; } const snap = new SnapDetail(thisHit, result.snapMode, result.heat, snapPoint); - // Apply model's elevation to curve for display. + // Apply model's elevation and display transform to curve for display. let transform; - if (0 !== elevation) + if (undefined !== thisHit.modelId && undefined !== thisHit.viewport.view.modelDisplayTransformProvider) { + transform = thisHit.viewport.view.getModelDisplayTransform(thisHit.modelId, Transform.createIdentity()); + if (0 !== elevation) + transform.origin.set(0, 0, elevation); + } else if (0 !== elevation) { transform = Transform.createTranslationXYZ(0, 0, elevation); + } snap.setCurvePrimitive(parseCurve(result.curve), transform, result.geomType); if (undefined !== result.parentGeomType) snap.parentGeomType = result.parentGeomType; - if (undefined !== result.hitPoint) + if (undefined !== result.hitPoint) { snap.hitPoint.setFromJSON(result.hitPoint); // Update hitPoint from readPixels with exact point location corrected to surface/edge geometry... - if (undefined !== result.normal) + thisHit.viewport.view.transformPointByModelDisplayTransform(thisHit.modelId, snap.hitPoint, false); + } + if (undefined !== result.normal) { snap.normal = Vector3d.fromJSON(result.normal); + thisHit.viewport.view.transformNormalByModelDisplayTransform(thisHit.modelId, snap.normal); + } if (SnapMode.Intersection !== snap.snapMode) return snap; @@ -943,11 +957,16 @@ export class AccuSnap implements Decorator { /** @internal */ public onTouchMoveStart(ev: BeTouchEvent, startEv: BeTouchEvent): boolean { return (undefined !== this.touchCursor) ? this.touchCursor.doTouchMoveStart(ev, startEv) : false; } + /** @internal */ + public get wantVirtualCursor(): boolean { + return this._doSnapping || (this.isLocateEnabled && ToolSettings.enableVirtualCursorForLocate); + } + /** @internal */ public async onTouchTap(ev: BeTouchEvent): Promise { if (undefined !== this.touchCursor) return this.touchCursor.doTouchTap(ev); - if (!this._doSnapping) + if (!this.wantVirtualCursor) return false; this.touchCursor = TouchCursor.createFromTouchTap(ev); if (undefined === this.touchCursor) @@ -1007,7 +1026,13 @@ export class AccuSnap implements Decorator { /** Enable locating elements. * @public */ - public enableLocate(yesNo: boolean) { this.toolState.locate = yesNo; } + public enableLocate(yesNo: boolean) { + this.toolState.locate = yesNo; + if (!yesNo && undefined !== this.touchCursor && !this.wantVirtualCursor) { + this.touchCursor = undefined; + IModelApp.viewManager.invalidateDecorationsAllViews(); + } + } /** Called whenever a new [[Tool]] is started. * @internal diff --git a/core/frontend/src/BriefcaseConnection.ts b/core/frontend/src/BriefcaseConnection.ts index 9b110b86fa4..9d020def33e 100644 --- a/core/frontend/src/BriefcaseConnection.ts +++ b/core/frontend/src/BriefcaseConnection.ts @@ -49,9 +49,8 @@ export class BriefcaseConnection extends IModelConnection { return connection; } - /** Open a BriefcaseConnection to a StandaloneDb. + /** Open a BriefcaseConnection to a [StandaloneDb]($backend) * @note StandaloneDbs, by definition, may not push or pull changes. Attempting to do so will throw exceptions. - * @internal */ public static async openStandalone(filePath: string, openMode: OpenMode = OpenMode.ReadWrite, opts?: StandaloneOpenOptions): Promise { const openResponse = await IpcApp.callIpcHost("openStandalone", filePath, openMode, opts); @@ -65,7 +64,7 @@ export class BriefcaseConnection extends IModelConnection { /** * Close this BriefcaseConnection. - * @note make sure to call SaveChanges before calling this method. Unsaved local changes are abandoned. + * @note make sure to call [[saveChanges]] before calling this method. Unsaved local changes are abandoned. */ public async close(): Promise { if (this.isClosed) diff --git a/core/frontend/src/FeatureToggleClient.ts b/core/frontend/src/FeatureToggleClient.ts deleted file mode 100644 index 21378023360..00000000000 --- a/core/frontend/src/FeatureToggleClient.ts +++ /dev/null @@ -1,74 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Bentley Systems, Incorporated. All rights reserved. -* Licensed under the MIT License. See LICENSE.md in the project root for license terms. -*--------------------------------------------------------------------------------------------*/ -/** @packageDocumentation - * @module Features - */ - -import { initialize, LDClient, LDFlagValue, LDUser } from "ldclient-js"; -import { assert, Config, Guid, Logger } from "@bentley/bentleyjs-core"; -import { AccessToken } from "@bentley/itwin-client"; -import { FrontendLoggerCategory } from "./FrontendLoggerCategory"; - -/** Client-side class for checking and toggling feature flags - * The [[FeatureToggleClient]] includes methods for applications to do the following via Launch Darkly: - * * Set the current user - * * Check to see whether a specific feature flag is enabled - * * Toggle a feature flag - * - * @internal - */ -export class FeatureToggleClient { - /** App-defined options */ - /** App ids for Launch Darkly deployment environments environments */ - /** Direct reference to the Launch Darkly client in the event the app wants to make its own async calls */ - protected _ldClient?: LDClient; - protected _offlineValue: boolean = false; - - protected readonly _loggingCategory = FrontendLoggerCategory.FeatureToggle; - - /** initialize initializes the Launch Darkly client. Must be called by the app to set the proper environment and user id */ - public async initialize(envKey?: string): Promise { - if (!!this._ldClient) - return; - - const imjsEnvKey: string | undefined = Config.App.get("imjs_launch_darkly_key"); - const ldId = envKey === undefined ? imjsEnvKey : envKey; - if (ldId === undefined) - return; - - const ldClient: LDClient = initialize(ldId, { key: Guid.createValue(), anonymous: true }); - await ldClient.waitUntilReady(); - - this._ldClient = ldClient; - } - - /** sets the Launch Darkly user info from the AccessToken */ - public async setUser(accessToken: AccessToken): Promise { - const userInfo = accessToken.getUserInfo(); - assert(userInfo !== undefined, "FeatureToggleClient unable get user id."); - assert(!!this._ldClient, "FeatureToggleClient.initialize must be called first."); - if (userInfo !== undefined) { - const ldUser: LDUser = { key: userInfo.id }; - await this.ldClient.identify(ldUser); - } - Logger.logTrace(this._loggingCategory, "Changed LaunchDarkly Feature Flags user context to current user.", () => ({ userId: userInfo === undefined ? "undefined" : userInfo.id })); - } - - /** Returns true is the flag specified by is enabled */ - public isFeatureEnabled(featureKey: string, defaultValue?: boolean): boolean { - return this.evaluateFeature(featureKey, !!defaultValue ? defaultValue : this._offlineValue) as boolean; - } - - /** Checks the status of the flag specified by featureKey */ - public evaluateFeature(featureKey: string, defaultValue?: LDFlagValue): LDFlagValue { - assert(!!this._ldClient, "FeatureToggleClient.initialize hasn't been called yet."); - const val: LDFlagValue = this.ldClient.variation(featureKey, defaultValue); - Logger.logTrace(this._loggingCategory, "Evaluated feature flag.", () => ({ featureKey, result: val })); - return val; - } - - /** The native LaunchDarkly client if the basic methods do not suffice. */ - public get ldClient(): LDClient { assert(!!this._ldClient, "FeatureToggleClient.initialize hasn't been called yet."); return this._ldClient; } -} diff --git a/core/frontend/src/FrontendLoggerCategory.ts b/core/frontend/src/FrontendLoggerCategory.ts index 7690c40cc7f..55cc73cbd9a 100644 --- a/core/frontend/src/FrontendLoggerCategory.ts +++ b/core/frontend/src/FrontendLoggerCategory.ts @@ -32,9 +32,6 @@ export enum FrontendLoggerCategory { /** The logger category used for NativeApp */ NativeApp = "imodeljs-frontend.NativeApp", - /** The logger category used by feature-flag-related functions */ - FeatureToggle = "imodeljs-frontend.FeatureToggles", - /** * The logger category used by FeatureTrackingManager * @alpha diff --git a/core/frontend/src/IModelApp.ts b/core/frontend/src/IModelApp.ts index df875e431e7..53a42be87ce 100644 --- a/core/frontend/src/IModelApp.ts +++ b/core/frontend/src/IModelApp.ts @@ -30,7 +30,6 @@ import * as drawingViewState from "./DrawingViewState"; import { ElementLocateManager } from "./ElementLocateManager"; import { EntityState } from "./EntityState"; import { ExtensionAdmin } from "./extension/ExtensionAdmin"; -import { FeatureToggleClient } from "./FeatureToggleClient"; import { FrontendLoggerCategory } from "./FrontendLoggerCategory"; import { FrontendRequestContext } from "./FrontendRequestContext"; import * as modelselector from "./ModelSelectorState"; @@ -129,10 +128,6 @@ export interface IModelAppOptions { extensionAdmin?: ExtensionAdmin; /** If present, supplies the [[UiAdmin]] for this session. */ uiAdmin?: UiAdmin; - /** if present, supplies the [[FeatureToggleClient]] for this session - * @internal - */ - featureToggles?: FeatureToggleClient; rpcInterfaces?: RpcInterfaceDefinition[]; } @@ -199,7 +194,6 @@ export class IModelApp { private static _animationRequested = false; private static _animationInterval: BeDuration | undefined = BeDuration.fromSeconds(1); private static _animationIntervalId?: number; - private static _featureToggles: FeatureToggleClient; private static _securityOptions: FrontendSecurityOptions; private static _mapLayerFormatRegistry: MapLayerFormatRegistry; @@ -270,11 +264,6 @@ export class IModelApp { */ public static readonly telemetry: TelemetryManager = new TelemetryManager(); - /** The [[FeatureToggleClient]] for this session - * @internal - */ - public static get featureToggles() { return this._featureToggles; } - /** Map of classFullName to EntityState class */ private static _entityClasses = new Map(); @@ -424,7 +413,6 @@ export class IModelApp { this._extensionAdmin = (opts.extensionAdmin !== undefined) ? opts.extensionAdmin : new ExtensionAdmin({}); this._quantityFormatter = (opts.quantityFormatter !== undefined) ? opts.quantityFormatter : new QuantityFormatter(); this._uiAdmin = (opts.uiAdmin !== undefined) ? opts.uiAdmin : new UiAdmin(); - this._featureToggles = (opts.featureToggles !== undefined) ? opts.featureToggles : new FeatureToggleClient(); this._mapLayerFormatRegistry = new MapLayerFormatRegistry(opts.mapLayerOptions); [ diff --git a/core/frontend/src/IpcApp.ts b/core/frontend/src/IpcApp.ts index a544802a678..71c4e8d8d8f 100644 --- a/core/frontend/src/IpcApp.ts +++ b/core/frontend/src/IpcApp.ts @@ -14,17 +14,17 @@ import { IModelApp, IModelAppOptions } from "./IModelApp"; /** * type check for an function that returns a Promise - * @beta + * @public */ export type AsyncFunction = (...args: any) => Promise; /** * a type that is the list of the asynchronous functions in an interface - * @beta + * @public */ export type AsyncMethodsOf = { [P in keyof T]: T[P] extends AsyncFunction ? P : never }[keyof T]; /** * get the type of the promised value of an asynchronous function - * @beta + * @public */ export type PromiseReturnType = T extends (...args: any) => Promise ? R : any; diff --git a/core/frontend/src/NativeApp.ts b/core/frontend/src/NativeApp.ts index 0d328e0afa2..1383b23a42b 100644 --- a/core/frontend/src/NativeApp.ts +++ b/core/frontend/src/NativeApp.ts @@ -18,7 +18,7 @@ import { IModelApp } from "./imodeljs-frontend"; import { AsyncMethodsOf, IpcApp, IpcAppOptions, NotificationHandler, PromiseReturnType } from "./IpcApp"; import { NativeAppLogger } from "./NativeAppLogger"; -/** Properties for specifying the Briefcaseid for downloading +/** Properties for specifying the BriefcaseId for downloading * @beta */ export type DownloadBriefcaseId = @@ -49,7 +49,7 @@ class NativeAppNotifyHandler extends NotificationHandler implements NativeAppNot * and then listens for the `onUserStateChanged` event to cache the accessToken. The token is cached * here on the frontend because it is used for every RPC operation, even when we're running as a NativeApp. * We must therefore check for expiration and request refreshes as/when necessary. - * @alpha + * @beta */ export class NativeAppAuthorization { private _config: NativeAppAuthorizationConfiguration; @@ -101,7 +101,7 @@ export class NativeAppAuthorization { /** * Options for [[NativeApp.startup]] - * @alpha + * @beta */ export interface NativeAppOpts extends IpcAppOptions { nativeApp?: { @@ -113,7 +113,7 @@ export interface NativeAppOpts extends IpcAppOptions { /** * The frontend of a native application * @see [Native Applications]($docs/learning/NativeApps.md) - * @alpha + * @beta */ export class NativeApp { public static async callNativeHost>(methodName: T, ...args: Parameters) { @@ -234,134 +234,77 @@ export class NativeApp { await this.callNativeHost("deleteBriefcaseFiles", fileName); } - /** - * Gets briefcases - * @returns list of BriefcaseProps in cache - */ + /** Get a list of all briefcase files held in the local briefcase cache directory */ public static async getCachedBriefcases(iModelId?: GuidString): Promise { return this.callNativeHost("getCachedBriefcases", iModelId); } /** - * Opens storage. This automatically create the storage with that name if it does not exist - * @param name Should confirm to a filename rules without extension. - * @returns storage object that represent the [[Storage]] object. + * Open a [[Storage]]. Creates a new Storage with that name if it does not already exist. + * @param name Should be a local filename without an extension. + * @returns a Promise for the [[Storage]]. */ public static async openStorage(name: string): Promise { - if (this._storages.has(name)) { + if (this._storages.has(name)) return this._storages.get(name)!; - } + const storage = new Storage(await this.callNativeHost("storageMgrOpen", name)); this._storages.set(storage.id, storage); return storage; } /** - * Closes storage cache + * Close a Storage and optionally delete it. * @param storage normally not call directly instead use Storage.close() - * @param deleteId if set attempt is made to delete the storage from disk permanently. + * @param deleteStorage if true, delete the storage from disk after closing it. */ - public static async closeStorage(storage: Storage, deleteId: boolean): Promise { - if (!this._storages.has(storage.id)) { - throw new Error(`Storage [Id=${storage.id}] not found`); - } - await this.callNativeHost("storageMgrClose", storage.id, deleteId); - (storage as any)._isOpen = false; + public static async closeStorage(storage: Storage, deleteStorage: boolean = false): Promise { + if (!this._storages.has(storage.id)) + throw new Error(`Storage [Id=${storage.id}] not open`); + + await this.callNativeHost("storageMgrClose", storage.id, deleteStorage); this._storages.delete(storage.id); } - /** - * Gets storage names - * @returns return list of storage available on disk. - */ + /** Get the list of existing Storages on the local disk. */ public static async getStorageNames(): Promise { return NativeApp.callNativeHost("storageMgrNames"); } } /** - * A local disk-based cache for key value pairs available for NativeApps. + * A local disk-based cache for key value pairs for NativeApps. * @note This should be used only for local caching, since its not guaranteed to exist permanently. - * @alpha + * @beta */ export class Storage { - constructor(public readonly id: string, private _isOpen: boolean = true) { } + constructor(public readonly id: string) { } - /** - * Gets data against a key - * @param key a string that represent a key. - * @returns data return value against the key - * @internal - */ + /** Get the value for a key */ public async getData(key: string): Promise { - if (!this._isOpen) { - throw new Error(`Storage [Id=${this.id}] is not open`); - } return NativeApp.callNativeHost("storageGet", this.id, key); } - /** - * Sets data against a key - * @param key a string that represent a key - * @param value a value that need to be persisted - * @internal - */ + /** Set value for a key */ public async setData(key: string, value: StorageValue): Promise { - if (!this._isOpen) { - throw new Error(`Storage [Id=${this.id}] is not open`); - } return NativeApp.callNativeHost("storageSet", this.id, key, value); } /** - * Return all keys. - * @note This could be expensive and may block backend depending on size and number of keys - * @returns keys a string array of all the keys in storage + * Return an array of all keys in this Storage. + * @note This can be expensive, depending on the number of keys present. */ public async getKeys(): Promise { - if (!this._isOpen) { - throw new Error(`Storage [Id=${this.id}] is not open`); - } return NativeApp.callNativeHost("storageKeys", this.id); } - /** - * Remove all keys - * @note Delete all keys and data. - */ + /** Remove a key and its data. */ public async removeData(key: string): Promise { - if (!this._isOpen) { - throw new Error(`Storage [Id=${this.id}] is not open`); - } return NativeApp.callNativeHost("storageRemove", this.id, key); } - /** - * Remove all keys - * @note Delete all keys and data. - */ + /** Remove all keys and their data. */ public async removeAll(): Promise { - if (!this._isOpen) { - throw new Error(`Storage [Id=${this.id}] is not open`); - } return NativeApp.callNativeHost("storageRemoveAll", this.id); } - - /** - * Closes storage and optionally delete it permanently - * @param [deleteIt] if set a attempt is made to delete the storage from disk. - */ - public async close(deleteIt: boolean = false): Promise { - if (!this._isOpen) { - throw new Error(`Storage [Id=${this.id}] is not open`); - } - return NativeApp.closeStorage(this, deleteIt); - } - - /** - * Can be check to see if the storage is still open - */ - public get isOpen(): boolean { - return this._isOpen; - } } diff --git a/core/frontend/src/PlanarClipMaskState.ts b/core/frontend/src/PlanarClipMaskState.ts index dc148c538e9..579739240ba 100644 --- a/core/frontend/src/PlanarClipMaskState.ts +++ b/core/frontend/src/PlanarClipMaskState.ts @@ -57,7 +57,7 @@ export class PlanarClipMaskState { if (this._modelIds) { for (const modelId of this._modelIds) { const model = view.iModel.models.getLoaded(modelId); - assert(model !== undefined); // Models should be loaded by RealitModelTileTree + assert(model !== undefined); // Models should be loaded by RealityModelTileTree if (model?.asGeometricModel) this._tileTreeRefs.push(createMaskTreeReference(model.asGeometricModel)); } diff --git a/core/frontend/src/ViewContext.ts b/core/frontend/src/ViewContext.ts index 597fa57a4b0..68cc8d886bb 100644 --- a/core/frontend/src/ViewContext.ts +++ b/core/frontend/src/ViewContext.ts @@ -44,13 +44,19 @@ export class GridDisplaySettings { /** Culling option based on distance to neighbor when camera is off. 0 for none, 1 for previous neighbor, 2 for previous displayed neighbor */ public static cullingOption: 0 | 1 | 2 = 2; /** Culling option based on distance to neighbor when camera is on. 0 for none, 1 for previous neighbor, 2 for previous displayed neighbor */ - public static cullingPerspectiveOption: 0 | 1 | 2 = 1; + public static cullingPerspectiveOption: 0 | 1 | 2 = 2; /** Clipping option based on distance to neighbor. 0 for none */ - public static clippingOption: 0 | 1 = 1; + public static clippingOption: 0 | 1 = 0; /** crude upper limit on lines to draw. * This is applied symmetrically above and below the limits at "frontMost" points of the grid plane intersection with the frustum */ - public static lineLimiter: number = 1000; + public static lineLimiter: number = 2000; + /** + * GridInViewContext optionally limits grid lines to size of view frustum. + * * This clip may be compute intensive + * * But if it is skipped, the logic for "nearby neighbor culling" may not work + */ + public static clipToViewFrustum: boolean = true; } /** Provides context for producing [[RenderGraphic]]s for drawing within a [[Viewport]]. @@ -423,7 +429,8 @@ export class DecorateContext extends RenderContext { (vp.isCameraOn && Math.abs(zVec.dotProduct(vp.rotation.getRow(2))) < 0.9) ? GridDisplaySettings.minPerspectiveSeparation : GridDisplaySettings.minSeparation, (vp.isCameraOn ? GridDisplaySettings.cullingPerspectiveOption : GridDisplaySettings.cullingOption), GridDisplaySettings.clippingOption, - 10 // first pass only gets major block lines !!! + 10, // first pass only gets major block lines !!! + GridDisplaySettings.clipToViewFrustum ); const gridRefXStep = rMatrix.rowX().scale(refSpacing.x); diff --git a/core/frontend/src/ViewCreator2d.ts b/core/frontend/src/ViewCreator2d.ts index 44e4562a40e..f1e56a244c1 100644 --- a/core/frontend/src/ViewCreator2d.ts +++ b/core/frontend/src/ViewCreator2d.ts @@ -20,7 +20,7 @@ import { IModelConnection } from "./IModelConnection"; import { ViewState, ViewState2d } from "./ViewState"; import { DrawingViewState } from "./DrawingViewState"; import { SheetViewState } from "./SheetViewState"; -import { loggerCategory } from "./imodeljs-frontend"; +import { EntityState, loggerCategory } from "./imodeljs-frontend"; /** @beta Options for creating a 2d [[ViewState]] via [[ViewCreator2d]] */ export interface ViewCreator2dOptions { @@ -37,7 +37,7 @@ export interface ViewCreator2dOptions { * const viewCreator = new ViewCreator2d(imodel); * const models = await imodel.models.queryProps({ from: "BisCore.GeometricModel2d" }); * if (models.length > 0) - * const view = await viewCreator.createViewForModel(models[0].id!, models[0].classFullName); + * const view = await viewCreator.createViewForModel(models[0].id!); * ``` */ export class ViewCreator2d { @@ -52,40 +52,15 @@ export class ViewCreator2d { */ constructor(private _imodel: IModelConnection) { } - /** - * Checks to see if given model is of [[DrawingModelState]]. - * @param modelType classFullName of model. - */ - public static isDrawingModelClass(modelType: string) { - if (ViewCreator2d._drawingModelClasses.includes(modelType)) { - return true; - } - return false; - } - - /** - * Checks to see if given model is of [[SheetModelState]]. - * @param modelType classFullName of model. - */ - public static isSheetModelClass(modelType: string) { - if (ViewCreator2d._sheetModelClasses.includes(modelType)) { - return true; - } - return false; - } - /** * Creates and returns view for given 2D model. * @param modelId of target 2D model. - * @param modelType classFullName of target 2D model. * @param options for view creation. * @throws [IModelError]($common) if modelType is not supported. */ - public async createViewForModel(modelId: Id64String, modelType: string, options?: ViewCreator2dOptions): Promise { - const baseClassName = await this._imodel.findClassFor(modelType, undefined); + public async createViewForModel(modelId: Id64String, options?: ViewCreator2dOptions): Promise { - if (baseClassName === undefined) - throw new IModelError(IModelStatus.WrongClass, "ViewCreator2d.getViewForModel: modelType is invalid", Logger.logError, loggerCategory, () => ({ modelType, modelId })); + const baseClassName = await this._getModelBaseClassName(modelId); const viewState = await this._createViewState2d(modelId, baseClassName.classFullName, options); try { @@ -95,6 +70,28 @@ export class ViewCreator2d { return viewState; } + /** + * Gets model base class name from id. + * @param modelId of target model. + * @throws [IModelError]($common) if modelId is invalid. + */ + private async _getModelBaseClassName(modelId: Id64String): Promise { + + let baseClassName; + + const modelProps = await this._imodel.models.getProps(modelId); + if (modelProps.length > 0) { + const modelType = modelProps[0].classFullName; + baseClassName = await this._imodel.findClassFor(modelType, undefined); + } else + throw new IModelError(IModelStatus.BadModel, "ViewCreator2d._getModelBaseClassName: modelId is invalid", Logger.logError, loggerCategory, () => ({ modelId })); + + if (baseClassName === undefined) + throw new IModelError(IModelStatus.WrongClass, "ViewCreator2d.getViewForModel: modelType is invalid", Logger.logError, loggerCategory, () => ({ modelId })); + + return baseClassName; + } + /** * Creates view from any 2D model type (Drawing/SectionDrawing/Sheet) * @param modelId of target model. @@ -104,10 +101,10 @@ export class ViewCreator2d { */ private async _createViewState2d(modelId: Id64String, modelType: string, options?: ViewCreator2dOptions): Promise { let viewState: ViewState2d; - if (ViewCreator2d.isDrawingModelClass(modelType)) { + if (this._isDrawingModelClass(modelType)) { const props = await this._createViewStateProps(modelId, options); viewState = DrawingViewState.createFromProps(props, this._imodel); - } else if (ViewCreator2d.isSheetModelClass(modelType)) { + } else if (this._isSheetModelClass(modelType)) { let props = await this._createViewStateProps(modelId, options); props = await this._addSheetViewProps(modelId, props); viewState = SheetViewState.createFromProps(props, this._imodel); @@ -117,6 +114,28 @@ export class ViewCreator2d { return viewState; } + /** + * Checks to see if given model is of [[DrawingModelState]]. + * @param modelType classFullName of model. + */ + private _isDrawingModelClass(modelType: string) { + if (ViewCreator2d._drawingModelClasses.includes(modelType)) { + return true; + } + return false; + } + + /** + * Checks to see if given model is of [[SheetModelState]]. + * @param modelType classFullName of model. + */ + private _isSheetModelClass(modelType: string) { + if (ViewCreator2d._sheetModelClasses.includes(modelType)) { + return true; + } + return false; + } + /** * Creates ViewStateProps for the model. ViewStateProps are composed of the 4 sets of Props below. * @param modelId of target model. diff --git a/core/frontend/src/ViewState.ts b/core/frontend/src/ViewState.ts index 2d83e0ec530..817a39eda82 100644 --- a/core/frontend/src/ViewState.ts +++ b/core/frontend/src/ViewState.ts @@ -1034,6 +1034,28 @@ export abstract class ViewState extends ElementState { return this.modelDisplayTransformProvider ? this.modelDisplayTransformProvider.getModelDisplayTransform(modelId, baseTransform) : baseTransform; } + /** @internal */ + public transformPointByModelDisplayTransform(modelId: string | undefined, pnt: Point3d, inverse: boolean): void { + if (undefined !== modelId && undefined !== this.modelDisplayTransformProvider) { + const transform = this.modelDisplayTransformProvider.getModelDisplayTransform(modelId, Transform.createIdentity()); + const newPnt = inverse ? transform.multiplyInversePoint3d(pnt) : transform.multiplyPoint3d(pnt); + if (undefined !== newPnt) + pnt.set(newPnt.x, newPnt.y, newPnt.z); + } + } + + /** @internal */ + public transformNormalByModelDisplayTransform(modelId: string | undefined, normal: Vector3d): void { + if (undefined !== modelId && undefined !== this.modelDisplayTransformProvider) { + const transform = this.modelDisplayTransformProvider.getModelDisplayTransform(modelId, Transform.createIdentity()); + const newVec = transform.matrix.multiplyInverse(normal); + if (undefined !== newVec) { + newVec.normalizeInPlace(); + normal.set(newVec.x, newVec.y, newVec.z); + } + } + } + /** Invoked when this view becomes the view displayed by the specified [[Viewport]]. * A ViewState can be attached to at most **one** Viewport. * @note If you override this method you **must** call `super.attachToViewport`. diff --git a/core/frontend/src/Viewport.ts b/core/frontend/src/Viewport.ts index fe50f05e06b..1d5c7c93148 100644 --- a/core/frontend/src/Viewport.ts +++ b/core/frontend/src/Viewport.ts @@ -2366,9 +2366,12 @@ export abstract class Viewport implements IDisposable { this.npcToWorld(npc, npc); // If this is a plan projection model, invert the elevation applied to its display transform. + // Likewise, if it is a hit on a model with a display transform, reverse the display transform. const modelId = pixels.getPixel(x, y).featureTable?.modelId; - if (undefined !== modelId) + if (undefined !== modelId) { npc.z -= this.view.getModelElevation(modelId); + this.view.transformPointByModelDisplayTransform(modelId, npc, true); + } } return npc; @@ -2765,6 +2768,9 @@ export class ScreenViewport extends Viewport { return result; } + /** @internal */ + public picker = new ElementPicker(); // Picker used in pickDepthPoint below so it hangs around and can be querried later. + /** Find a point on geometry visible in this Viewport, within a radius of supplied pick point. * If no geometry is selected, return the point projected to the most appropriate reference plane. * @param pickPoint Point to search about, in world coordinates @@ -2781,14 +2787,14 @@ export class ScreenViewport extends Viewport { if (undefined === radius) radius = this.pixelsFromInches(ToolSettings.viewToolPickRadiusInches); - const picker = new ElementPicker(); + this.picker.empty(); const locateOpts = new LocateOptions(); locateOpts.allowNonLocatable = (undefined === options || !options.excludeNonLocatable); locateOpts.allowDecorations = (undefined === options || !options.excludeDecorations); locateOpts.allowExternalIModels = (undefined === options || !options.excludeExternalIModels); - if (0 !== picker.doPick(this, pickPoint, radius, locateOpts)) { - const hitDetail = picker.getHit(0)!; + if (0 !== this.picker.doPick(this, pickPoint, radius, locateOpts)) { + const hitDetail = this.picker.getHit(0)!; const hitPoint = hitDetail.getPoint(); if (hitDetail.isModelHit) return { plane: Plane3dByOriginAndUnitNormal.create(hitPoint, this.view.getUpVector(hitPoint))!, source: DepthPointSource.Model, sourceId: hitDetail.sourceId }; diff --git a/core/frontend/src/render/RenderMemory.ts b/core/frontend/src/render/RenderMemory.ts index bd494afadea..f827584a690 100644 --- a/core/frontend/src/render/RenderMemory.ts +++ b/core/frontend/src/render/RenderMemory.ts @@ -40,6 +40,7 @@ export namespace RenderMemory { PointClouds, Instances, Terrain, + RealityMesh, COUNT, } @@ -66,6 +67,7 @@ export namespace RenderMemory { public get pointClouds() { return this.consumers[BufferType.PointClouds]; } public get instances() { return this.consumers[BufferType.Instances]; } public get terrain() { return this.consumers[BufferType.Terrain]; } + public get reality() { return this.consumers[BufferType.RealityMesh]; } public clear(): void { for (const consumer of this.consumers) @@ -151,7 +153,12 @@ export namespace RenderMemory { public addPolyline(numBytes: number) { this.addBuffer(BufferType.Polylines, numBytes); } public addPointString(numBytes: number) { this.addBuffer(BufferType.PointStrings, numBytes); } public addPointCloud(numBytes: number) { this.addBuffer(BufferType.PointClouds, numBytes); } - public addTerrain(numBytes: number) { this.addBuffer(BufferType.Terrain, numBytes); } + public addTerrain(numBytes: number) { + this.addBuffer(BufferType.Terrain, numBytes); + } + public addRealityMesh(numBytes: number) { + this.addBuffer(BufferType.RealityMesh, numBytes); + } public addInstances(numBytes: number) { this.addBuffer(BufferType.Instances, numBytes); } } diff --git a/core/frontend/src/render/RenderPlan.ts b/core/frontend/src/render/RenderPlan.ts index f2e241ee362..0d60cccb88e 100644 --- a/core/frontend/src/render/RenderPlan.ts +++ b/core/frontend/src/render/RenderPlan.ts @@ -38,8 +38,6 @@ export interface RenderPlan { readonly analysisTexture?: RenderTexture; readonly frustum: Frustum; readonly fraction: number; - readonly terrainTransparency: number; - readonly locatableTerrain: boolean; readonly globalViewTransition: number; readonly isGlobeMode3D: boolean; readonly backgroundMapOn: boolean; @@ -60,8 +58,6 @@ export function createEmptyRenderPlan(): RenderPlan { frustum: new Frustum(), fraction: 0, isFadeOutActive: false, - terrainTransparency: 1, - locatableTerrain: true, globalViewTransition: 0, isGlobeMode3D: false, backgroundMapOn: false, @@ -75,13 +71,6 @@ export function createRenderPlanFromViewport(vp: Viewport): RenderPlan { const style = view.displayStyle; const is3d = view.is3d(); - let terrainTransparency = 0; - let locatableTerrain = false; - const style3d = view.is3d() ? view.getDisplayStyle3d() : undefined; - if (style3d) { - terrainTransparency = style3d.backgroundMapSettings.transparency || 0; - locatableTerrain = style3d.backgroundMapSettings.locatable; - } const globalViewTransition = view.is3d() ? view.globalViewTransition() : 0.0; const backgroundMapOn = view.displayStyle.viewFlags.backgroundMap; @@ -136,8 +125,6 @@ export function createRenderPlanFromViewport(vp: Viewport): RenderPlan { analysisTexture, frustum, fraction, - terrainTransparency, - locatableTerrain, globalViewTransition, isGlobeMode3D, backgroundMapOn, diff --git a/core/frontend/src/render/RenderSystem.ts b/core/frontend/src/render/RenderSystem.ts index b031c411893..a3690eaa1e1 100644 --- a/core/frontend/src/render/RenderSystem.ts +++ b/core/frontend/src/render/RenderSystem.ts @@ -15,6 +15,7 @@ import { imageElementFromImageSource } from "../ImageUtil"; import { IModelApp } from "../IModelApp"; import { IModelConnection } from "../IModelConnection"; import { MapTileTreeReference, TileTreeReference } from "../tile/internal"; +import { ToolAdmin } from "../tools/ToolAdmin"; import { SceneContext } from "../ViewContext"; import { Viewport } from "../Viewport"; import { ViewRect } from "../ViewRect"; @@ -22,6 +23,7 @@ import { GraphicBranch, GraphicBranchOptions } from "./GraphicBranch"; import { GraphicBuilder, GraphicType } from "./GraphicBuilder"; import { InstancedGraphicParams } from "./InstancedGraphicParams"; import { MeshArgs, PolylineArgs } from "./primitives/mesh/MeshPrimitives"; +import { RealityMeshPrimitive } from "./primitives/mesh/RealityMeshPrimitive"; import { TerrainMeshPrimitive } from "./primitives/mesh/TerrainMeshPrimitive"; import { PointCloudArgs } from "./primitives/PointCloudPrimitive"; import { MeshParams, PointStringParams, PolylineParams } from "./primitives/VertexTable"; @@ -30,7 +32,6 @@ import { RenderGraphic, RenderGraphicOwner } from "./RenderGraphic"; import { RenderMemory } from "./RenderMemory"; import { RenderTarget } from "./RenderTarget"; import { ScreenSpaceEffectBuilder, ScreenSpaceEffectBuilderParams } from "./ScreenSpaceEffectBuilder"; -import { ToolAdmin } from "../tools/ToolAdmin"; /* eslint-disable no-restricted-syntax */ // cSpell:ignore deserializing subcat uninstanced wiremesh qorigin trimesh @@ -132,11 +133,10 @@ export interface RenderSystemDebugControl { } /** @internal */ -export abstract class RenderTerrainMeshGeometry implements IDisposable, RenderMemory.Consumer { +export abstract class RenderRealityMeshGeometry implements IDisposable, RenderMemory.Consumer { public abstract dispose(): void; public abstract collectStatistics(stats: RenderMemory.Statistics): void; } - /** @internal */ export class TerrainTexture { public constructor(public readonly texture: RenderTexture, public featureId: number, public readonly scale: Vector2d, public readonly translate: Vector2d, public readonly targetRectangle: Range2d, public readonly layerIndex: number, public transparency: number, public readonly clipRectangle?: Range2d) { @@ -191,6 +191,9 @@ export abstract class RenderSystem implements IDisposable { /** @internal */ public get supportsInstancing(): boolean { return true; } + /** @internal */ + public get supportsNonuniformScaledInstancing(): boolean { return true; } + /** @internal */ public get dpiAwareLOD(): boolean { return true === this.options.dpiAwareLOD; } @@ -265,11 +268,13 @@ export abstract class RenderSystem implements IDisposable { /** @internal */ public createPolyline(_params: PolylineParams, _instances?: InstancedGraphicParams | Point3d): RenderGraphic | undefined { return undefined; } /** @internal */ - public createTerrainMeshGeometry(_terrainMesh: TerrainMeshPrimitive, _transform: Transform): RenderTerrainMeshGeometry | undefined { return undefined; } + public createRealityMeshFromTerrain(_terrainMesh: TerrainMeshPrimitive, _transform?: Transform): RenderRealityMeshGeometry | undefined { return undefined; } + /** @internal */ + public createRealityMeshGraphic(_terrainGeometry: RenderRealityMeshGeometry, _featureTable: PackedFeatureTable, _tileId: string | undefined, _baseColor: ColorDef | undefined, _baseTransparent: boolean, _textures?: TerrainTexture[]): RenderGraphic | undefined { return undefined; } /** @internal */ - public createTerrainMeshGraphic(_terrainGeometry: RenderTerrainMeshGeometry, _featureTable: PackedFeatureTable, _tileId: string, _baseColor: ColorDef | undefined, _baseTransparent: boolean, _textures?: TerrainTexture[]): RenderGraphic | undefined { return undefined; } + public createRealityMesh(_realityMesh: RealityMeshPrimitive): RenderGraphic | undefined { return undefined; } /** @internal */ - public get maxTerrainImageryLayers() { return 0; } + public get maxRealityImageryLayers() { return 0; } /** @internal */ public createPointString(_params: PointStringParams, _instances?: InstancedGraphicParams | Point3d): RenderGraphic | undefined { return undefined; } /** @internal */ diff --git a/core/frontend/src/render/primitives/Primitives.ts b/core/frontend/src/render/primitives/Primitives.ts index 5fdf5648233..c6987a020a2 100644 --- a/core/frontend/src/render/primitives/Primitives.ts +++ b/core/frontend/src/render/primitives/Primitives.ts @@ -99,6 +99,14 @@ export class TriangleList { this._flags.push(flags); } + public addFromTypedArray(indices: Uint16Array | Uint32Array, flags: number = 0) { + for (let i = 0; i < indices.length; ) { + this.indices.push(indices[i++]); + this.indices.push(indices[i++]); + this.indices.push(indices[i++]); + this._flags.push(flags); + } + } public getTriangle(index: number, out?: Triangle): Triangle { const triangle = undefined !== out ? out : new Triangle(); diff --git a/core/frontend/src/render/primitives/mesh/RealityMeshPrimitive.ts b/core/frontend/src/render/primitives/mesh/RealityMeshPrimitive.ts new file mode 100644 index 00000000000..6b0923ddb95 --- /dev/null +++ b/core/frontend/src/render/primitives/mesh/RealityMeshPrimitive.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Bentley Systems, Incorporated. All rights reserved. +* See LICENSE.md in the project root for license terms and full copyright notice. +*--------------------------------------------------------------------------------------------*/ +/** @packageDocumentation + * @module Rendering + */ + +import { QParams2d, QParams3d, RenderTexture } from "@bentley/imodeljs-common"; +import { GltfMeshData } from "../../../tile/internal"; +import { RenderMemory } from "../../RenderMemory"; +import { Mesh } from "./MeshPrimitives"; + +export interface RealityMeshProps { + readonly indices: Uint16Array; + readonly pointQParams: QParams3d; + readonly points: Uint16Array; + readonly normals?: Uint16Array; + readonly uvQParams: QParams2d; + readonly uvs: Uint16Array; + readonly featureID: number; + readonly texture?: RenderTexture; +} + +/** @internal */ +export class RealityMeshPrimitive implements RenderMemory.Consumer { + public readonly indices: Uint16Array; + public readonly pointQParams: QParams3d; + public readonly points: Uint16Array; + public readonly normals?: Uint16Array; + public readonly uvQParams: QParams2d; + public readonly uvs: Uint16Array; + public readonly featureID: number = 0; + public readonly texture?: RenderTexture; + protected constructor(props: RealityMeshProps) { + this.pointQParams = props.pointQParams; + this.points = props.points; + this.uvQParams = props.uvQParams; + this.uvs = props.uvs; + this.normals = props.normals; + this.indices = props.indices; + this.featureID = props.featureID; + this.texture = props.texture; + } + + public static createFromGltfMesh(mesh: GltfMeshData): RealityMeshPrimitive | undefined { + if (mesh.primitive.type !== Mesh.PrimitiveType.Mesh || mesh.primitive.edges || !mesh.primitive.displayParams.textureMapping || !mesh.pointQParams || !mesh.uvQParams || !mesh.points || !mesh.uvs || !mesh.indices || !(mesh.indices instanceof Uint16Array)) + return undefined; // Simple meshes have only triangles without edges and are textured. + + return new RealityMeshPrimitive({ indices: mesh.indices, pointQParams: mesh.pointQParams, points: mesh.points, uvQParams: mesh.uvQParams, uvs: mesh.uvs, normals: mesh.normals, featureID: 0, texture: mesh.primitive.displayParams.textureMapping.texture }); + } + + public collectStatistics(stats: RenderMemory.Statistics): void { + stats.addTerrain(this.bytesUsed); + } + public get bytesUsed() { + return 2 * (this.indices.length + this.points.length + this.uvs.length + (this.normals ? this.normals.length : 0)); + } +} diff --git a/core/frontend/src/render/primitives/mesh/TerrainMeshPrimitive.ts b/core/frontend/src/render/primitives/mesh/TerrainMeshPrimitive.ts index 38f76facd29..93df25d7a21 100644 --- a/core/frontend/src/render/primitives/mesh/TerrainMeshPrimitive.ts +++ b/core/frontend/src/render/primitives/mesh/TerrainMeshPrimitive.ts @@ -6,12 +6,13 @@ * @module Rendering */ -import { Point3d, Range1d, Range2d, Range3d, Vector3d } from "@bentley/geometry-core"; -import { OctEncodedNormal, QParams2d, QParams3d, QPoint2d, QPoint2dList, QPoint3d, QPoint3dList, Quantization } from "@bentley/imodeljs-common"; +import { assert } from "@bentley/bentleyjs-core"; +import { Point3d, Range1d, Range2d, Vector3d } from "@bentley/geometry-core"; +import { OctEncodedNormal, QParams2d, QParams3d, QPoint2d, QPoint3d, Quantization } from "@bentley/imodeljs-common"; import { RenderMemory } from "../../RenderMemory"; +import { RealityMeshPrimitive, RealityMeshProps } from "./RealityMeshPrimitive"; export enum Child { Q00, Q01, Q10, Q11 } - class UpsampleIndexMap extends Map { private _next = 0; public indices = new Array(); @@ -28,38 +29,81 @@ class ClipAxis { constructor(public vertical: boolean, public lessThan: boolean, public value: number) { } } +const scratchQPoint3d = QPoint3d.fromScalars(0, 0, 0), scratchQPoint3d1 = QPoint3d.fromScalars(0, 0, 0); +const scratchQPoint2d = new QPoint2d(), scratchQPoint2d1 = new QPoint2d(); + /** These are currently retained on terrain leaf tiles for upsampling. * It may be worthwhile to pack the data into buffers... * @internal. */ -export class TerrainMeshPrimitive implements RenderMemory.Consumer { - public readonly indices: number[]; - public readonly points: QPoint3dList; - public readonly normals: OctEncodedNormal[]; - public readonly uvParams: QPoint2dList; - public readonly featureID: number = 0; - - private constructor(pointParams: QParams3d, indices?: number[]) { - this.points = new QPoint3dList(pointParams); - this.normals = []; - this.uvParams = new QPoint2dList(QParams2d.fromRange(Range2d.createXYXY(0, 0, 1, 1))); - this.indices = indices ? indices : new Array(); - } - public static create(props: TerrainMesh.Props, indices?: number[]) { - return new TerrainMeshPrimitive(QParams3d.fromRange(props.range), indices); +export class TerrainMeshPrimitive extends RealityMeshPrimitive { + private _currPointCount = 0; + private _currIndexCount = 0; + + private constructor(props: RealityMeshProps, private _pointCapacity: number, private _indexCapacity: number) { + super(props); } - public get bytesUsed() { - return 8 * (this.indices.length + this.points.length * 3 + this.uvParams.length * 2) + 2 * this.normals.length; + + public get isCompleted() { return this._currIndexCount === this._indexCapacity && this._currPointCount === this._pointCapacity; } + public get nextPointIndex() { return this._currPointCount; } + + public static create(props: TerrainMesh.Props) { + let totalIndices = props.indexCount; + let totalPoints = props.pointCount; + if (props.wantSkirts) { + totalIndices += 6 * (Math.max(0, props.northCount - 1) + Math.max(0, props.southCount - 1) + Math.max(0, props.eastCount - 1) + Math.max(0, props.westCount - 1)); + totalPoints += (props.northCount + props.southCount + props.eastCount + props.westCount); + } + const realityMeshProps = { indices: new Uint16Array(totalIndices), pointQParams: props.pointQParams, points: new Uint16Array(3 * totalPoints), + uvQParams: QParams2d.fromZeroToOne(), uvs: new Uint16Array(2 * totalPoints), normals: props.wantNormals ? new Uint16Array(totalPoints) : undefined, featureID: 0 }; + return new TerrainMeshPrimitive(realityMeshProps, totalPoints, totalIndices); } + public collectStatistics(stats: RenderMemory.Statistics): void { stats.addTerrain(this.bytesUsed); } - public addVertex(point: Point3d, uvParam: QPoint2d, normal?: OctEncodedNormal) { - this.points.add(point); - this.uvParams.push(uvParam.clone()); - if (undefined !== normal) - this.normals.push(normal); + public addVertex(point: Point3d, uvParam: QPoint2d, normal?: number) { + scratchQPoint3d.init(point, this.pointQParams); + this.addQuantizedVertex(scratchQPoint3d, uvParam, normal); + } + + public addQuantizedVertex(point: QPoint3d, uv: QPoint2d, normal?: number) { + if (this._currPointCount >= this._pointCapacity) { + assert(false, "terrain point capacity exceeded"); + return; + } + let pointIndex = 3 * this._currPointCount; + this.points[pointIndex++] = point.x; + this.points[pointIndex++] = point.y; + this.points[pointIndex++] = point.z; + + let paramIndex = 2 * this._currPointCount; + this.uvs[paramIndex++] = uv.x; + this.uvs[paramIndex++] = uv.y; + + if (normal && this.normals) + this.normals[this._currPointCount] = normal; + + this._currPointCount++; + } + public addQuad(i0: number, i1: number, i2: number, i3: number) { + this.addTriangle(i0, i1, i2); + this.addTriangle(i1, i3, i2); + } + + public addTriangle(i0: number, i1: number, i2: number) { + if (this._currIndexCount + 3 > this._indexCapacity) { + assert(false, "terrain index capacity exceeded"); + return; + } + this.indices[this._currIndexCount++] = i0; + this.indices[this._currIndexCount++] = i1; + this.indices[this._currIndexCount++] = i2; + } + public addIndices(indices: number[]) { + for (const index of indices) + this.indices[this._currIndexCount++] = index; } private static _scratchZRange = Range1d.createNull(); @@ -72,6 +116,7 @@ export class TerrainMeshPrimitive implements RenderMemory.Consumer { const uvHigh = QPoint2d.create(uvSampleRange.high, TerrainMeshPrimitive._scratchUVQParams); const uvRange = Range2d.createXYXY(uvLow.x, uvLow.y, uvHigh.x, uvHigh.y, TerrainMeshPrimitive._scratchUVRange); const clipAxes = new Array(); + const addedPoints = new Array(), addedParams = new Array(), addedNormals = new Array(); if (uvLow.x > 0) clipAxes.push(new ClipAxis(true, false, uvLow.x)); if (uvHigh.x < Quantization.rangeScale16) @@ -85,38 +130,57 @@ export class TerrainMeshPrimitive implements RenderMemory.Consumer { const triangleIndices = [this.indices[i++], this.indices[i++], this.indices[i++]]; const triangleRange = Range2d.createNull(TerrainMeshPrimitive._scratchTriangleRange); - for (const index of triangleIndices) - triangleRange.extendPoint(this.uvParams.list[index]); + for (const index of triangleIndices) { + const paramIndex = 2 * index; + triangleRange.extendXY(this.uvs[paramIndex], this.uvs[paramIndex+1]); + } if (uvRange.intersectsRange(triangleRange)) { if (uvRange.containsRange(triangleRange)) { indexMap.addTriangle(triangleIndices); } else { - this.addClipped(triangleIndices, indexMap, clipAxes, 0); + this.addClipped(triangleIndices, indexMap, clipAxes, 0, addedPoints, addedParams, addedNormals); } } } - const parentPoints = this.points.list; - const parentParams = this.uvParams.list; + const parentPoints = this.points; + const parentParams = this.uvs; const parentNormals = this.normals; - const qParams = this.points.params; + const parentPointCount = this.points.length / 3; const zRange = Range1d.createNull(TerrainMeshPrimitive._scratchZRange); - const mesh = new TerrainMeshPrimitive(qParams, indexMap.indices); + + const mesh = TerrainMeshPrimitive.create({ pointQParams: this.pointQParams, pointCount: indexMap.size, indexCount: indexMap.indices.length, wantSkirts: false, northCount: 0, southCount: 0, eastCount: 0, westCount: 0, wantNormals: this.normals !== undefined }); for (const mapEntry of indexMap.entries()) { const parentIndex = mapEntry[0]; - const parentPoint = parentPoints[parentIndex]; - zRange.extendX(parentPoint.z); - mesh.points.list.push(parentPoint); - mesh.uvParams.list.push(parentParams[parentIndex]); - if (parentNormals.length > 0) - mesh.normals.push(parentNormals[parentIndex]); + + let normal: number | undefined; + if (parentIndex < parentPointCount) { + const pointIndex = 3 * parentIndex; + scratchQPoint3d.setFromScalars(parentPoints[pointIndex], parentPoints[pointIndex+1], parentPoints[pointIndex+2]); + const paramIndex = 2 * parentIndex; + scratchQPoint2d.setFromScalars(parentParams[paramIndex], parentParams[paramIndex + 1]); + if (parentNormals) + normal = parentNormals[parentIndex]; + } else { + const addedIndex = parentIndex - parentPointCount; + addedPoints[addedIndex].clone(scratchQPoint3d); + addedParams[addedIndex].clone(scratchQPoint2d); + if (addedNormals.length) + normal = addedNormals[addedIndex]; + } + mesh.addQuantizedVertex(scratchQPoint3d, scratchQPoint2d, normal); + zRange.extendX(scratchQPoint3d.z); } + mesh.addIndices(indexMap.indices); + + assert (mesh.isCompleted); + const qParams = this.pointQParams; const heightRange = Range1d.createXX(Quantization.unquantize(zRange.low, qParams.origin.z, qParams.scale.z), Quantization.unquantize(zRange.high, qParams.origin.z, qParams.scale.z)); return { heightRange, mesh }; } - private addClipped(triangleIndices: number[], indexMap: UpsampleIndexMap, clipAxes: ClipAxis[], clipIndex: number) { + private addClipped(triangleIndices: number[], indexMap: UpsampleIndexMap, clipAxes: ClipAxis[], clipIndex: number, addedPoints: QPoint3d[], addedParams: QPoint2d[], addedNormals: number[]) { if (clipIndex === clipAxes.length) { indexMap.addTriangle(triangleIndices); return; @@ -126,13 +190,40 @@ export class TerrainMeshPrimitive implements RenderMemory.Consumer { const values = new Array(3); const clipOutput = new Array(); const clipAxis = clipAxes[clipIndex++]; - const parentPoints = this.points.list; - const parentParams = this.uvParams.list; + const parentPoints = this.points; + const parentParams = this.uvs; const parentNormals = this.normals; const clipValue = clipAxis.value; + const parentPointCount = parentPoints.length / 3; + const getPoint = (index: number, result: QPoint3d): QPoint3d => { + if (index < parentPointCount) { + const pointIndex = index * 3; + result.setFromScalars(parentPoints[pointIndex], parentPoints[pointIndex + 1], parentPoints[pointIndex + 2]); + } else { + addedPoints[index - parentPointCount].clone(result); + } + return result; + }; + const getParam = (index: number, result: QPoint2d): QPoint2d => { + if (index < parentPointCount) { + const pointIndex = index * 2; + result.setFromScalars(parentParams[pointIndex], parentParams[pointIndex + 1]); + } else { + addedParams[index - parentPointCount].clone(result); + } + return result; + }; + const getNormal = (index: number): number | undefined => { + if (!parentNormals) + return undefined; + + return (index < parentPointCount) ? parentNormals[index] : addedNormals[index - parentPointCount]; + }; + for (let i = 0; i < 3; i++) { const index = triangleIndices[i]; - const thisValue = clipAxis.vertical ? parentParams[index].x : parentParams[index].y; + const thisParam = getParam(index, scratchQPoint2d); + const thisValue = clipAxis.vertical ? thisParam.x : thisParam.y; values[i] = thisValue; inside[i] = clipAxis.lessThan ? (thisValue < clipValue) : (thisValue > clipValue); } @@ -146,18 +237,18 @@ export class TerrainMeshPrimitive implements RenderMemory.Consumer { const nextIndex = triangleIndices[next]; const fraction = (clipValue - values[i]) / (values[next] - values[i]); - clipOutput.push(parentPoints.length); - parentPoints.push(interpolateQPoint3d(parentPoints[index], parentPoints[nextIndex], fraction)); - parentParams.push(interpolateQPoint2d(parentParams[index], parentParams[nextIndex], fraction)); - if (parentNormals.length > 0) - parentNormals.push(interpolateOctEncodedNormal(parentNormals[index], parentNormals[nextIndex], fraction)); + clipOutput.push(parentPointCount + addedPoints.length); + addedPoints.push(interpolateQPoint3d(getPoint(index, scratchQPoint3d), getPoint(nextIndex, scratchQPoint3d1), fraction)); + addedParams.push(interpolateQPoint2d(getParam(index, scratchQPoint2d), getParam(nextIndex, scratchQPoint2d1), fraction)); + if (parentNormals) + addedNormals.push(interpolateOctEncodedNormal(getNormal(index)!, getNormal(nextIndex)!, fraction)); } } if (clipOutput.length > 2) { - this.addClipped(clipOutput.slice(0, 3), indexMap, clipAxes, clipIndex); + this.addClipped(clipOutput.slice(0, 3), indexMap, clipAxes, clipIndex, addedPoints, addedParams, addedNormals); if (clipOutput.length > 3) - this.addClipped([clipOutput[0], clipOutput[2], clipOutput[3]], indexMap, clipAxes, clipIndex); + this.addClipped([clipOutput[0], clipOutput[2], clipOutput[3]], indexMap, clipAxes, clipIndex, addedPoints, addedParams, addedNormals); } } } @@ -165,30 +256,36 @@ export class TerrainMeshPrimitive implements RenderMemory.Consumer { function interpolate(value0: number, value1: number, fraction: number) { return value0 + (value1 - value0) * fraction; } function interpolateInt(value0: number, value1: number, fraction: number) { return Math.floor(.5 + interpolate(value0, value1, fraction)); } -function interpolateQPoint3d(p0: QPoint3d, p1: QPoint3d, fraction: number): QPoint3d { - return QPoint3d.fromScalars(interpolateInt(p0.x, p1.x, fraction), interpolateInt(p0.y, p1.y, fraction), interpolateInt(p0.z, p1.z, fraction)); +function interpolateQPoint3d(qPoint: QPoint3d, qNext: QPoint3d, fraction: number): QPoint3d { + return QPoint3d.fromScalars(interpolateInt(qPoint.x, qNext.x, fraction), interpolateInt(qPoint.y, qNext.y, fraction), interpolateInt(qPoint.z, qNext.z, fraction)); } -function interpolateQPoint2d(p0: QPoint2d, p1: QPoint2d, fraction: number): QPoint2d { - return QPoint2d.fromScalars(interpolateInt(p0.x, p1.x, fraction), interpolateInt(p0.y, p1.y, fraction)); +function interpolateQPoint2d(qPoint: QPoint2d, qNext: QPoint2d, fraction: number): QPoint2d { + return QPoint2d.fromScalars(interpolateInt(qPoint.x, qNext.x, fraction), interpolateInt(qPoint.y, qNext.y, fraction)); } -function interpolateOctEncodedNormal(oen0: OctEncodedNormal, oen1: OctEncodedNormal, fraction: number): OctEncodedNormal { - const n0 = oen0.decode(); - const n1 = oen1.decode(); +function interpolateOctEncodedNormal(normal0: number, normal1: number, fraction: number): number { + const n0 = OctEncodedNormal.decodeValue(normal0); + const n1 = OctEncodedNormal.decodeValue(normal1); if (undefined !== n0 && undefined !== n1) { const n = Vector3d.create(interpolate(n0.x, n1.x, fraction), interpolate(n0.y, n1.y, fraction), interpolate(n0.z, n1.z, fraction)); n.normalizeInPlace(); - return OctEncodedNormal.fromVector(n); + return OctEncodedNormal.encode(n); } else { - return OctEncodedNormal.fromVector(Vector3d.create(0, 0, 1)); + return OctEncodedNormal.encode(Vector3d.create(0, 0, 1)); } } export namespace TerrainMesh { export interface Props { - /** Mesh range -- used for quantization */ - readonly range: Range3d; - + readonly wantNormals: boolean; + readonly pointQParams: QParams3d; + readonly pointCount: number; + readonly indexCount: number; + readonly wantSkirts: boolean; + readonly eastCount: number; + readonly westCount: number; + readonly northCount: number; + readonly southCount: number; } } diff --git a/core/frontend/src/render/webgl/AttributeMap.ts b/core/frontend/src/render/webgl/AttributeMap.ts index bbfec88436f..a90680a31c0 100644 --- a/core/frontend/src/render/webgl/AttributeMap.ts +++ b/core/frontend/src/render/webgl/AttributeMap.ts @@ -83,7 +83,7 @@ export class AttributeMap { ["a_pos", 0, VariableType.Vec3], ["a_color", 1, VariableType.Vec3], ]); - const terrainMesh = new AttributeMapEntry([ + const realityMesh = new AttributeMapEntry([ ["a_pos", 0, VariableType.Vec3], ["a_norm", 1, VariableType.Vec2], ["a_uvParam", 2, VariableType.Vec2], @@ -101,7 +101,7 @@ export class AttributeMap { [TechniqueId.SilhouetteEdge, silhouette], [TechniqueId.PointCloud, pointCloud], [TechniqueId.VolClassCopyZ, screenPoints], - [TechniqueId.TerrainMesh, terrainMesh], + [TechniqueId.RealityMesh, realityMesh], ]); } diff --git a/core/frontend/src/render/webgl/BranchUniforms.ts b/core/frontend/src/render/webgl/BranchUniforms.ts index 4c574969c17..19685637803 100644 --- a/core/frontend/src/render/webgl/BranchUniforms.ts +++ b/core/frontend/src/render/webgl/BranchUniforms.ts @@ -61,6 +61,7 @@ export class BranchUniforms { private readonly _mvp32 = new Matrix4(); private readonly _m32 = new Matrix4(); private readonly _v32 = new Matrix3(); + private readonly _mvn32 = new Matrix3(); // Working state private readonly _scratchTransform = Transform.createIdentity(); @@ -169,6 +170,11 @@ export class BranchUniforms { uniform.setMatrix3(this._v32); } + public bindModelViewNTransform(uniform: UniformHandle, geom: CachedGeometry, isViewCoords: boolean) { + if (this.update(uniform, geom, isViewCoords)) + uniform.setMatrix3(this._mvn32); + } + private update(uniform: UniformHandle, geometry: CachedGeometry, isViewCoords: boolean): boolean { const uniforms = this._target.uniforms[isViewCoords ? "viewRect" : "frustum"]; if (!sync(uniforms, this)) @@ -230,6 +236,12 @@ export class BranchUniforms { Matrix4d.createTransform(mv, this._mv); this._mv32.initFromTransform(mv); + const inv = this._mv.createInverse(); + if (undefined !== inv) { + const invTr = inv.cloneTransposed(); + this._mvn32.initFromMatrix3d(invTr.matrixPart()); + } + // Don't bother computing mvp for instanced geometry - it's not used. if (!this._isInstanced) { uniforms.projectionMatrix.multiplyMatrixMatrix(this._mv, this._mvp); diff --git a/core/frontend/src/render/webgl/CachedGeometry.ts b/core/frontend/src/render/webgl/CachedGeometry.ts index 99a4e998e0f..d6f06196bae 100644 --- a/core/frontend/src/render/webgl/CachedGeometry.ts +++ b/core/frontend/src/render/webgl/CachedGeometry.ts @@ -28,7 +28,7 @@ import { CompositeFlags, FlashMode, RenderOrder, RenderPass } from "./RenderFlag import { System } from "./System"; import { Target } from "./Target"; import { computeCompositeTechniqueId, TechniqueId } from "./TechniqueId"; -import { TerrainMeshGeometry } from "./TerrainMesh"; +import { RealityMeshGeometry } from "./RealityMesh"; import { TextureHandle } from "./Texture"; import { VertexLUT } from "./VertexLUT"; @@ -55,7 +55,7 @@ export abstract class CachedGeometry implements WebGLDisposable, RenderMemory.Co public get asSurface(): SurfaceGeometry | undefined { return undefined; } public get asMesh(): MeshGeometry | undefined { return undefined; } public get asEdge(): EdgeGeometry | undefined { return undefined; } - public get asTerrainMesh(): TerrainMeshGeometry | undefined { return undefined; } + public get asRealityMesh(): RealityMeshGeometry | undefined { return undefined; } public get asSilhouette(): SilhouetteEdgeGeometry | undefined { return undefined; } public get asInstanced(): InstancedGeometry | undefined { return undefined; } public get isInstanced() { return undefined !== this.asInstanced; } @@ -102,7 +102,6 @@ export abstract class CachedGeometry implements WebGLDisposable, RenderMemory.Co public get polylineBuffers(): PolylineBuffers | undefined { return undefined; } public get hasFeatures(): boolean { return false; } - public get viewIndependentOrigin(): Point3d | undefined { return undefined; } public get isViewIndependent(): boolean { return undefined !== this.viewIndependentOrigin; } diff --git a/core/frontend/src/render/webgl/DrawCommand.ts b/core/frontend/src/render/webgl/DrawCommand.ts index 9da669e7109..85b05e83c38 100644 --- a/core/frontend/src/render/webgl/DrawCommand.ts +++ b/core/frontend/src/render/webgl/DrawCommand.ts @@ -204,7 +204,7 @@ export class PrimitiveCommand { const target = exec.target; const thematic = this.primitive.cachedGeometry.supportsThematicDisplay && target.wantThematicDisplay; - const shadowable = (techniqueId === TechniqueId.Surface || techniqueId === TechniqueId.TerrainMesh) && target.solarShadowMap.isReady && target.currentViewFlags.shadows && !thematic; + const shadowable = (techniqueId === TechniqueId.Surface || techniqueId === TechniqueId.RealityMesh) && target.solarShadowMap.isReady && target.currentViewFlags.shadows && !thematic; const isShadowable = shadowable ? IsShadowable.Yes : IsShadowable.No; let isThematic = thematic ? IsThematic.Yes : IsThematic.No; const isClassified = (undefined !== target.currentPlanarClassifierOrDrape || undefined !== target.activeVolumeClassifierTexture) ? IsClassified.Yes : IsClassified.No; diff --git a/core/frontend/src/render/webgl/TerrainMesh.ts b/core/frontend/src/render/webgl/RealityMesh.ts similarity index 63% rename from core/frontend/src/render/webgl/TerrainMesh.ts rename to core/frontend/src/render/webgl/RealityMesh.ts index b4c8bd06a68..aca06eb6d7c 100644 --- a/core/frontend/src/render/webgl/TerrainMesh.ts +++ b/core/frontend/src/render/webgl/RealityMesh.ts @@ -7,17 +7,16 @@ * @module WebGL */ -import { assert, dispose } from "@bentley/bentleyjs-core"; -import { Range2d, Range3d, Transform } from "@bentley/geometry-core"; +import { assert, dispose, IDisposable } from "@bentley/bentleyjs-core"; +import { Range2d, Range3d, Transform, Vector2d } from "@bentley/geometry-core"; import { ColorDef, PackedFeatureTable, Quantization, RenderTexture } from "@bentley/imodeljs-common"; -import { IndexedGeometry, IndexedGeometryParams, Matrix4 } from "../../webgl"; +import { AttributeMap, BufferHandle, BufferParameters, IndexedGeometry, IndexedGeometryParams, Matrix4, QBufferHandle2d, QBufferHandle3d } from "../../webgl"; import { GraphicBranch } from "../GraphicBranch"; +import { RealityMeshPrimitive } from "../primitives/mesh/RealityMeshPrimitive"; import { TerrainMeshPrimitive } from "../primitives/mesh/TerrainMeshPrimitive"; import { RenderGraphic } from "../RenderGraphic"; import { RenderMemory } from "../RenderMemory"; -import { RenderSystem, RenderTerrainMeshGeometry, TerrainTexture } from "../RenderSystem"; -import { BufferHandle, BufferParameters, QBufferHandle2d, QBufferHandle3d } from "./AttributeBuffers"; -import { AttributeMap } from "./AttributeMap"; +import { RenderSystem, TerrainTexture } from "../RenderSystem"; import { GL } from "./GL"; import { Primitive } from "./Primitive"; import { RenderOrder, RenderPass } from "./RenderFlags"; @@ -28,11 +27,11 @@ import { TechniqueId } from "./TechniqueId"; const scratchOverlapRange = Range2d.createNull(); /** @internal */ -export class TerrainTextureParams { +export class RealityTextureParams { constructor(public matrices: Matrix4[], public textures: RenderTexture[]) { } public static create(terrainTextures: TerrainTexture[]) { - const maxTexturesPerMesh = System.instance.maxTerrainImageryLayers; + const maxTexturesPerMesh = System.instance.maxRealityImageryLayers; assert(terrainTextures.length <= maxTexturesPerMesh); const renderTextures = []; @@ -68,25 +67,26 @@ export class TerrainTextureParams { matrix.data[6] = matrix.data[7] = -1; matrices.push(matrix); } - return new TerrainTextureParams(matrices, renderTextures); + return new RealityTextureParams(matrices, renderTextures); } } /** @internal */ -export class TerrainMeshParams extends IndexedGeometryParams { + +export class RealityMeshGeometryParams extends IndexedGeometryParams { public readonly uvParams: QBufferHandle2d; public readonly featureID?: number; public readonly normals?: BufferHandle; protected constructor(positions: QBufferHandle3d, normals: BufferHandle | undefined, uvParams: QBufferHandle2d, indices: BufferHandle, numIndices: number, featureID?: number) { super(positions, indices, numIndices); - let attrParams = AttributeMap.findAttribute("a_uvParam", TechniqueId.TerrainMesh, false); + let attrParams = AttributeMap.findAttribute("a_uvParam", TechniqueId.RealityMesh, false); assert(attrParams !== undefined); this.buffers.addBuffer(uvParams, [BufferParameters.create(attrParams.location, 2, GL.DataType.UnsignedShort, false, 0, 0, false)]); this.uvParams = uvParams; if (undefined !== normals) { - attrParams = AttributeMap.findAttribute("a_norm", TechniqueId.TerrainMesh, false); + attrParams = AttributeMap.findAttribute("a_norm", TechniqueId.RealityMesh, false); assert(attrParams !== undefined); if (normals.bytesUsed > 0) this.buffers.addBuffer(normals, [BufferParameters.create(attrParams.location, 2, GL.DataType.UnsignedByte, false, 0, 0, false)]); @@ -96,29 +96,21 @@ export class TerrainMeshParams extends IndexedGeometryParams { this.featureID = featureID; } - public static createFromTerrain(terrainMesh: TerrainMeshPrimitive) { - const posBuf = QBufferHandle3d.create(terrainMesh.points.params, terrainMesh.points.toTypedArray()); - const uvParamBuf = QBufferHandle2d.create(terrainMesh.uvParams.params, terrainMesh.uvParams.toTypedArray()); - - const indArray = new Uint16Array(terrainMesh.indices.length); - for (let i = 0; i < terrainMesh.indices.length; i++) - indArray[i] = terrainMesh.indices[i]; + private static createFromBuffers(posBuf: QBufferHandle3d, uvParamBuf: QBufferHandle2d, indices: Uint16Array, normBuf: BufferHandle | undefined, featureID: number) { + const indBuf = BufferHandle.createBuffer(GL.Buffer.Target.ElementArrayBuffer, indices); - const indBuf = BufferHandle.createBuffer(GL.Buffer.Target.ElementArrayBuffer, indArray); + if (undefined === indBuf) + return undefined; - let normBuf: BufferHandle | undefined; - if (terrainMesh.normals.length > 0) { - const normalBytes = new Uint8Array(terrainMesh.normals.length * 2); - const normalShorts = new Uint16Array(normalBytes.buffer); - for (let i = 0; i < terrainMesh.normals.length; i++) - normalShorts[i] = terrainMesh.normals[i].value; - normBuf = BufferHandle.createArrayBuffer(normalBytes); - } + return new RealityMeshGeometryParams(posBuf, normBuf, uvParamBuf, indBuf, indices.length, featureID); - if (undefined === posBuf || undefined === uvParamBuf || undefined === indBuf || (terrainMesh.normals.length > 0 && undefined === normBuf)) - return undefined; + } - return new TerrainMeshParams(posBuf, normBuf, uvParamBuf, indBuf, terrainMesh.indices.length, terrainMesh.featureID); + public static createFromRealityMesh(mesh: RealityMeshPrimitive) { + const posBuf = QBufferHandle3d.create(mesh.pointQParams, mesh.points); + const uvParamBuf = QBufferHandle2d.create(mesh.uvQParams, mesh.uvs); + const normalBuf = mesh.normals ? BufferHandle.createArrayBuffer(mesh.normals) : undefined; + return (undefined === posBuf || undefined === uvParamBuf) ? undefined : this.createFromBuffers(posBuf, uvParamBuf, mesh.indices, normalBuf, mesh.featureID); } public get isDisposed(): boolean { @@ -133,36 +125,47 @@ export class TerrainMeshParams extends IndexedGeometryParams { } /** @internal */ -export class TerrainMeshGeometry extends IndexedGeometry implements RenderTerrainMeshGeometry { - public get asTerrainMesh(): TerrainMeshGeometry | undefined { return this; } - public get isDisposed(): boolean { return this._terrainMeshParams.isDisposed; } - public get uvQParams() { return this._terrainMeshParams.uvParams.params; } - public get hasFeatures(): boolean { return this._terrainMeshParams.featureID !== undefined; } +export class RealityMeshGeometry extends IndexedGeometry implements IDisposable, RenderMemory.Consumer { + public get asRealityMesh(): RealityMeshGeometry | undefined { return this; } + public get isDisposed(): boolean { return this._realityMeshParams.isDisposed; } + public get uvQParams() { return this._realityMeshParams.uvParams.params; } + public get hasFeatures(): boolean { return this._realityMeshParams.featureID !== undefined; } public get supportsThematicDisplay() { return true; } + public get overrideColorMix() { return .5; } // TThis could be a setting from either the mesh or the override if required. - private constructor(private _terrainMeshParams: TerrainMeshParams, public textureParams: TerrainTextureParams | undefined, private readonly _transform: Transform | undefined, public readonly baseColor: ColorDef | undefined, private _baseIsTransparent: boolean) { - super(_terrainMeshParams); + private constructor(private _realityMeshParams: RealityMeshGeometryParams, public textureParams: RealityTextureParams | undefined, private readonly _transform: Transform | undefined, public readonly baseColor: ColorDef | undefined, private _baseIsTransparent: boolean, private _isTerrain: boolean) { + super(_realityMeshParams); } public dispose() { super.dispose(); - dispose(this._terrainMeshParams); + dispose(this._realityMeshParams); } - public static createGeometry(terrainMesh: TerrainMeshPrimitive, transform: Transform | undefined) { - const params = TerrainMeshParams.createFromTerrain(terrainMesh); - return new TerrainMeshGeometry(params!, undefined, transform, undefined, false); + public static createFromTerrainMesh(terrainMesh: TerrainMeshPrimitive, transform: Transform | undefined) { + const params = RealityMeshGeometryParams.createFromRealityMesh(terrainMesh); + return params ? new RealityMeshGeometry(params, undefined, transform, undefined, false, true) : undefined; } + + public static createFromRealityMesh(realityMesh: RealityMeshPrimitive): RealityMeshGeometry | undefined { + const params = RealityMeshGeometryParams.createFromRealityMesh(realityMesh); + if (!params) + return undefined; + const texture = realityMesh.texture ? new TerrainTexture(realityMesh.texture, realityMesh.featureID, Vector2d.create(1.0, -1.0), Vector2d.create(0.0, 1.0), Range2d.createXYXY(0, 0, 1, 1), 0, 0) : undefined; + + return new RealityMeshGeometry(params, texture ? RealityTextureParams.create([texture]) : undefined, undefined, undefined, false, false); + } + public getRange(): Range3d { return Range3d.createXYZXYZ(this.qOrigin[0], this.qOrigin[1], this.qOrigin[2], this.qOrigin[0] + Quantization.rangeScale16 * this.qScale[0], this.qOrigin[1] + Quantization.rangeScale16 * this.qScale[1], this.qOrigin[2] + Quantization.rangeScale16 * this.qScale[2]); } - public static createGraphic(system: RenderSystem, terrainMesh: TerrainMeshGeometry, featureTable: PackedFeatureTable, tileId: string, baseColor: ColorDef | undefined, baseTransparent: boolean, textures?: TerrainTexture[]): RenderGraphic | undefined { + public static createGraphic(system: RenderSystem, realityMesh: RealityMeshGeometry, featureTable: PackedFeatureTable, tileId: string | undefined, baseColor: ColorDef | undefined, baseTransparent: boolean, textures?: TerrainTexture[]): RenderGraphic | undefined { const meshes = []; if (textures === undefined) textures = []; - const texturesPerMesh = System.instance.maxTerrainImageryLayers; + const texturesPerMesh = System.instance.maxRealityImageryLayers; const layers = new Array(); let layerCount = 0; for (const texture of textures) { @@ -176,7 +179,7 @@ export class TerrainMeshGeometry extends IndexedGeometry implements RenderTerrai } if (layerCount < 2) { // If only there is not more than one layer then we can group all of the textures into a single draw call. - meshes.push(new TerrainMeshGeometry(terrainMesh._terrainMeshParams, TerrainTextureParams.create(textures), terrainMesh._transform, baseColor, baseTransparent)); + meshes.push(new RealityMeshGeometry(realityMesh._realityMeshParams, RealityTextureParams.create(textures), realityMesh._transform, baseColor, baseTransparent, realityMesh._isTerrain)); } else { const primaryLayer = layers.shift()!; for (const primaryTexture of primaryLayer) { @@ -201,7 +204,7 @@ export class TerrainMeshGeometry extends IndexedGeometry implements RenderTerrai layerTextures.push(new TerrainTexture(secondaryTexture.texture, secondaryTexture.featureId, secondaryTexture.scale, secondaryTexture.translate, secondaryTexture.targetRectangle, secondaryTexture.layerIndex, secondaryTexture.transparency, textureRange)); } layerTextures.length = Math.min(layerTextures.length, texturesPerMesh); - meshes.push(new TerrainMeshGeometry(terrainMesh._terrainMeshParams, TerrainTextureParams.create(layerTextures), terrainMesh._transform, baseColor, baseTransparent)); + meshes.push(new RealityMeshGeometry(realityMesh._realityMeshParams, RealityTextureParams.create(layerTextures), realityMesh._transform, baseColor, baseTransparent, realityMesh._isTerrain)); } } } @@ -216,24 +219,20 @@ export class TerrainMeshGeometry extends IndexedGeometry implements RenderTerrai branch.add(system.createBatch(primitive!, featureTable, mesh.getRange(), tileId)); } - return system.createBranch(branch, terrainMesh._transform ? terrainMesh._transform : Transform.createIdentity()); + return system.createBranch(branch, realityMesh._transform ? realityMesh._transform : Transform.createIdentity()); } public collectStatistics(stats: RenderMemory.Statistics): void { - stats.addTerrain(this._terrainMeshParams.bytesUsed); + this._isTerrain ? stats.addTerrain(this._realityMeshParams.bytesUsed) : stats.addRealityMesh(this._realityMeshParams.bytesUsed); } - public get techniqueId(): TechniqueId { return TechniqueId.TerrainMesh; } + public get techniqueId(): TechniqueId { return TechniqueId.RealityMesh; } public getRenderPass(target: Target): RenderPass { if (target.isDrawingShadowMap) return RenderPass.None; - if (target.nonLocatableTerrain && !target.drawNonLocatable) - return RenderPass.None; - - if (target.terrainTransparency > 0.0 || this._baseIsTransparent || target.currentPlanarClassifier?.anyTranslucent || - (target.wantThematicDisplay && target.uniforms.thematic.wantIsoLines)) + if (this._baseIsTransparent || (target.wantThematicDisplay && target.uniforms.thematic.wantIsoLines)) return RenderPass.Translucent; return RenderPass.OpaqueGeneral; diff --git a/core/frontend/src/render/webgl/RenderFlags.ts b/core/frontend/src/render/webgl/RenderFlags.ts index 782e12b1a43..06670ebfd01 100644 --- a/core/frontend/src/render/webgl/RenderFlags.ts +++ b/core/frontend/src/render/webgl/RenderFlags.ts @@ -77,13 +77,13 @@ export enum TextureUnit { ShadowMap = Seven, ThematicSensors = Seven, // The number of allowable map layers for either background or overlay map is limited to 3 if only 8 texture units is available (IOS)... 6 layers are available if the hardware supports them. - TerrainMesh0 = Two, - TerrainMesh1 = VertexLUT, // Terrain meshes do not use VertexLUT. - TerrainMesh2 = ShadowMap, // Shadow map when picking -- PickDepthAndOrder otherwise.... - TerrainMesh3 = WebGLRenderingContext.TEXTURE8, // These are used only if available. - TerrainMesh4 = WebGLRenderingContext.TEXTURE9, - TerrainMesh5 = WebGLRenderingContext.TEXTURE10, - TerrainThematicGradient = WebGLRenderingContext.TEXTURE11, + RealityMesh0 = Two, + RealityMesh1 = VertexLUT, // Reality meshes do not use VertexLUT. + RealityMesh2 = ShadowMap, // Shadow map when picking -- PickDepthAndOrder otherwise.... + RealityMesh3 = WebGLRenderingContext.TEXTURE8, // These are used only if available. + RealityMesh4 = WebGLRenderingContext.TEXTURE9, + RealityMesh5 = WebGLRenderingContext.TEXTURE10, + RealityMeshThematicGradient = WebGLRenderingContext.TEXTURE11, } /** diff --git a/core/frontend/src/render/webgl/System.ts b/core/frontend/src/render/webgl/System.ts index 1804469f7ca..cd2d78aff66 100644 --- a/core/frontend/src/render/webgl/System.ts +++ b/core/frontend/src/render/webgl/System.ts @@ -13,6 +13,7 @@ import { } from "@bentley/imodeljs-common"; import { Capabilities, DepthType, WebGLContext } from "@bentley/webgl-compatibility"; import { SkyBox } from "../../DisplayStyleState"; +import { imageElementFromImageSource } from "../../ImageUtil"; import { IModelApp } from "../../IModelApp"; import { IModelConnection } from "../../IModelConnection"; import { MapTileTreeReference, TileTreeReference } from "../../tile/internal"; @@ -22,16 +23,16 @@ import { GraphicBranch, GraphicBranchOptions } from "../GraphicBranch"; import { GraphicBuilder, GraphicType } from "../GraphicBuilder"; import { InstancedGraphicParams } from "../InstancedGraphicParams"; import { PrimitiveBuilder } from "../primitives/geometry/GeometryListBuilder"; +import { RealityMeshPrimitive } from "../primitives/mesh/RealityMeshPrimitive"; import { TerrainMeshPrimitive } from "../primitives/mesh/TerrainMeshPrimitive"; import { PointCloudArgs } from "../primitives/PointCloudPrimitive"; import { MeshParams, PointStringParams, PolylineParams } from "../primitives/VertexTable"; import { RenderClipVolume } from "../RenderClipVolume"; import { RenderGraphic, RenderGraphicOwner } from "../RenderGraphic"; import { RenderMemory } from "../RenderMemory"; -import { DebugShaderFile, GLTimerResultCallback, RenderDiagnostics, RenderSystem, RenderSystemDebugControl, RenderTerrainMeshGeometry, TerrainTexture } from "../RenderSystem"; -import { ScreenSpaceEffectBuilder, ScreenSpaceEffectBuilderParams } from "../ScreenSpaceEffectBuilder"; +import { DebugShaderFile, GLTimerResultCallback, RenderDiagnostics, RenderSystem, RenderSystemDebugControl, TerrainTexture } from "../RenderSystem"; import { RenderTarget } from "../RenderTarget"; -import { imageElementFromImageSource } from "../../ImageUtil"; +import { ScreenSpaceEffectBuilder, ScreenSpaceEffectBuilderParams } from "../ScreenSpaceEffectBuilder"; import { BackgroundMapDrape } from "./BackgroundMapDrape"; import { CachedGeometry, SkyBoxQuadsGeometry, SkySphereViewportQuadGeometry } from "./CachedGeometry"; import { ClipVolume } from "./ClipVolume"; @@ -41,7 +42,6 @@ import { DepthBuffer, FrameBufferStack } from "./FrameBuffer"; import { GL } from "./GL"; import { GLTimer } from "./GLTimer"; import { Batch, Branch, Graphic, GraphicOwner, GraphicsArray } from "./Graphic"; -import { UniformHandle } from "./UniformHandle"; import { Layer, LayerContainer } from "./Layer"; import { LineCode } from "./LineCode"; import { Material } from "./Material"; @@ -53,11 +53,12 @@ import { Primitive, SkyCubePrimitive, SkySpherePrimitive } from "./Primitive"; import { RenderBuffer, RenderBufferMultiSample } from "./RenderBuffer"; import { TextureUnit } from "./RenderFlags"; import { RenderState } from "./RenderState"; +import { createScreenSpaceEffectBuilder, ScreenSpaceEffects } from "./ScreenSpaceEffect"; import { OffScreenTarget, OnScreenTarget } from "./Target"; import { Techniques } from "./Technique"; -import { TerrainMeshGeometry } from "./TerrainMesh"; +import { RealityMeshGeometry } from "./RealityMesh"; import { Texture, TextureHandle } from "./Texture"; -import { createScreenSpaceEffectBuilder, ScreenSpaceEffects } from "./ScreenSpaceEffect"; +import { UniformHandle } from "./UniformHandle"; /* eslint-disable no-restricted-syntax */ @@ -391,6 +392,7 @@ export class System extends RenderSystem implements RenderSystemDebugControl, Re public get maxTextureSize(): number { return this.capabilities.maxTextureSize; } public get supportsInstancing(): boolean { return this.capabilities.supportsInstancing; } + public get supportsNonuniformScaledInstancing(): boolean { return this.capabilities.isWebGL2; } public get isWebGL2(): boolean { return this.capabilities.isWebGL2; } public get isMobile(): boolean { return this.capabilities.isMobile; } @@ -506,12 +508,16 @@ export class System extends RenderSystem implements RenderSystemDebugControl, Re return MeshGraphic.create(params, instances); } - public createTerrainMeshGeometry(terrainMesh: TerrainMeshPrimitive, transform: Transform): RenderTerrainMeshGeometry | undefined { - return TerrainMeshGeometry.createGeometry(terrainMesh, transform); + public createRealityMeshFromTerrain(terrainMesh: TerrainMeshPrimitive, transform?: Transform): RealityMeshGeometry | undefined { + return RealityMeshGeometry.createFromTerrainMesh(terrainMesh, transform); } - public createTerrainMeshGraphic(terrainGeometry: RenderTerrainMeshGeometry, featureTable: PackedFeatureTable, tileId: string, baseColor: ColorDef | undefined, baseTransparent: boolean, textures?: TerrainTexture[]): RenderGraphic | undefined { - return TerrainMeshGeometry.createGraphic(this, terrainGeometry as TerrainMeshGeometry, featureTable, tileId, baseColor, baseTransparent, textures); + public createRealityMeshGraphic(terrainGeometry: RealityMeshGeometry, featureTable: PackedFeatureTable, tileId: string | undefined, baseColor: ColorDef | undefined, baseTransparent: boolean, textures?: TerrainTexture[]): RenderGraphic | undefined { + return RealityMeshGeometry.createGraphic(this, terrainGeometry, featureTable, tileId, baseColor, baseTransparent, textures); + } + public createRealityMesh(realityMesh: RealityMeshPrimitive): RenderGraphic | undefined { + const geom = RealityMeshGeometry.createFromRealityMesh(realityMesh); + return geom ? Primitive.create(() => geom) : undefined; } public createPolyline(params: PolylineParams, instances?: InstancedGraphicParams | Point3d): RenderGraphic | undefined { @@ -739,7 +745,7 @@ export class System extends RenderSystem implements RenderSystemDebugControl, Re public ensureSamplerBound(uniform: UniformHandle, unit: TextureUnit): void { this.lineCodeTexture!.bindSampler(uniform, unit); } - public get maxTerrainImageryLayers() { return Math.min(this.capabilities.maxFragTextureUnits, this.capabilities.maxVertTextureUnits) < 16 ? 3 : 6; } + public get maxRealityImageryLayers() { return Math.min(this.capabilities.maxFragTextureUnits, this.capabilities.maxVertTextureUnits) < 16 ? 3 : 6; } public disposeTexture(texture: WebGLTexture) { System.instance.context.deleteTexture(texture); diff --git a/core/frontend/src/render/webgl/Target.ts b/core/frontend/src/render/webgl/Target.ts index c40decffe54..dd3e6c47add 100644 --- a/core/frontend/src/render/webgl/Target.ts +++ b/core/frontend/src/render/webgl/Target.ts @@ -122,8 +122,6 @@ export abstract class Target extends RenderTarget implements RenderTargetDebugCo public activeVolumeClassifierTexture?: WebGLTexture; public activeVolumeClassifierProps?: SpatialClassificationProps.Classifier; public activeVolumeClassifierModelId?: Id64String; - public terrainTransparency = 0.0; - public nonLocatableTerrain = false; // RenderTargetDebugControl public vcSupportIntersectingVolumes: boolean = false; @@ -508,9 +506,6 @@ export abstract class Target extends RenderTarget implements RenderTargetDebugCo if (!this.assignDC()) return; - this.terrainTransparency = plan.terrainTransparency; - this.nonLocatableTerrain = !plan.locatableTerrain; - this.isFadeOutActive = plan.isFadeOutActive; this.analysisStyle = plan.analysisStyle === undefined ? undefined : plan.analysisStyle.clone(); this.analysisTexture = plan.analysisTexture; diff --git a/core/frontend/src/render/webgl/Technique.ts b/core/frontend/src/render/webgl/Technique.ts index 90a0d273f9e..af089ea8a3b 100644 --- a/core/frontend/src/render/webgl/Technique.ts +++ b/core/frontend/src/render/webgl/Technique.ts @@ -8,12 +8,14 @@ import { assert, dispose, using } from "@bentley/bentleyjs-core"; import { WebGLContext } from "@bentley/webgl-compatibility"; +import { ClippingProgram, createClippingProgram } from "./ClippingProgram"; import { WebGLDisposable } from "./Disposable"; import { DrawCommands, DrawParams } from "./DrawCommand"; import { createAmbientOcclusionProgram } from "./glsl/AmbientOcclusion"; import { createBlurProgram } from "./glsl/Blur"; import { createClearPickAndColorProgram } from "./glsl/ClearPickAndColor"; import { createClearTranslucentProgram } from "./glsl/ClearTranslucent"; +import { createCombine3TexturesProgram } from "./glsl/Combine3Textures"; import { createCombineTexturesProgram } from "./glsl/CombineTextures"; import { addEyeSpace, addFrustum, addShaderFlags } from "./glsl/Common"; import { createCompositeProgram } from "./glsl/Composite"; @@ -32,15 +34,14 @@ import { addUnlitMonochrome } from "./glsl/Monochrome"; import { createPointCloudBuilder, createPointCloudHiliter } from "./glsl/PointCloud"; import { createPointStringBuilder, createPointStringHiliter } from "./glsl/PointString"; import { createPolylineBuilder, createPolylineHiliter } from "./glsl/Polyline"; +import createRealityMeshBuilder, { createClassifierRealityMeshHiliter } from "./glsl/RealityMesh"; import { createSkyBoxProgram } from "./glsl/SkyBox"; import { createSkySphereProgram } from "./glsl/SkySphere"; import { createSurfaceBuilder, createSurfaceHiliter } from "./glsl/Surface"; -import createTerrainMeshBuilder from "./glsl/TerrainMesh"; import { addTranslucency } from "./glsl/Translucency"; import { addModelViewMatrix } from "./glsl/Vertex"; import { RenderPass } from "./RenderFlags"; import { ProgramBuilder } from "./ShaderBuilder"; -import { ClippingProgram, createClippingProgram } from "./ClippingProgram"; import { CompileStatus, ShaderProgram, ShaderProgramExecutor } from "./ShaderProgram"; import { System } from "./System"; import { Target } from "./Target"; @@ -48,7 +49,6 @@ import { FeatureMode, IsAnimated, IsClassified, IsEdgeTestNeeded, IsInstanced, IsShadowable, IsThematic, TechniqueFlags, } from "./TechniqueFlags"; import { computeCompositeTechniqueId, TechniqueId } from "./TechniqueId"; -import { createCombine3TexturesProgram } from "./glsl/Combine3Textures"; /** Defines a rendering technique implemented using one or more shader programs. * @internal @@ -638,29 +638,30 @@ class PointCloudTechnique extends VariedTechnique { } } -class TerrainMeshTechnique extends VariedTechnique { - private static readonly _numVariants = 32; +class RealityMeshTechnique extends VariedTechnique { + private static readonly _numVariants = 49; public constructor(gl: WebGLRenderingContext) { - super(TerrainMeshTechnique._numVariants); + super(RealityMeshTechnique._numVariants); + this._earlyZFlags = [ TechniqueFlags.fromDescription("Opaque-Hilite-Classified") ]; + this.addHiliteShader(gl, IsInstanced.No, IsClassified.Yes, createClassifierRealityMeshHiliter); for (let iClassified = IsClassified.No; iClassified <= IsClassified.Yes; iClassified++) { for (let iTranslucent = 0; iTranslucent <= 1; iTranslucent++) { for (let shadowable = IsShadowable.No; shadowable <= IsShadowable.Yes; shadowable++) { for (let thematic = IsThematic.No; thematic <= IsThematic.Yes; thematic++) { const flags = scratchTechniqueFlags; - const terrainMeshFeatureModes = [FeatureMode.None, FeatureMode.Pick]; - for (const featureMode of terrainMeshFeatureModes) { + for (const featureMode of featureModes) { flags.reset(featureMode, IsInstanced.No, shadowable, thematic); flags.isClassified = iClassified; flags.isTranslucent = 1 === iTranslucent; - const builder = createTerrainMeshBuilder(flags, featureMode, thematic); - if (FeatureMode.Pick === featureMode) - addUniformFeatureSymbology(builder, false); + const builder = createRealityMeshBuilder(flags); + if (flags.isTranslucent) { addShaderFlags(builder); addTranslucency(builder); } else this.addFeatureId(builder, featureMode); + this.addShader(builder, flags, gl); } } @@ -671,20 +672,21 @@ class TerrainMeshTechnique extends VariedTechnique { this.finishConstruction(); } - protected get _debugDescription() { return "TerrainMesh"; } + protected get _debugDescription() { return "RealityMesh"; } public computeShaderIndex(flags: TechniqueFlags): number { - let ndx = 0; + if (flags.isHilite) + return 0; + let ndx = 1; if (flags.isClassified) ndx++; if (flags.isShadowable) ndx += 2; if (flags.isTranslucent) ndx += 4; - if (flags.featureMode !== FeatureMode.None) - ndx += 8; + ndx += 8 * flags.featureMode; if (flags.isThematic) - ndx += 16; + ndx += 24; return ndx; } } @@ -723,7 +725,7 @@ const techniquesByPriority: PrioritizedTechniqueOrShader[] = [ { techniqueId: TechniqueId.Polyline }, { techniqueId: TechniqueId.PointString }, { techniqueId: TechniqueId.PointCloud }, - { techniqueId: TechniqueId.TerrainMesh }, + { techniqueId: TechniqueId.RealityMesh }, // The following techniques take a trivial amount of time to compile - do them last { techniqueId: TechniqueId.OITClearTranslucent }, @@ -915,7 +917,7 @@ export class Techniques implements WebGLDisposable { this._list[TechniqueId.Polyline] = new PolylineTechnique(gl); this._list[TechniqueId.PointString] = new PointStringTechnique(gl); this._list[TechniqueId.PointCloud] = new PointCloudTechnique(gl); - this._list[TechniqueId.TerrainMesh] = new TerrainMeshTechnique(gl); + this._list[TechniqueId.RealityMesh] = new RealityMeshTechnique(gl); if (System.instance.capabilities.supportsFragDepth) this._list[TechniqueId.VolClassCopyZ] = new SingularTechnique(createVolClassCopyZProgram(gl)); else diff --git a/core/frontend/src/render/webgl/TechniqueId.ts b/core/frontend/src/render/webgl/TechniqueId.ts index 83b137d59e9..3472ad5252c 100644 --- a/core/frontend/src/render/webgl/TechniqueId.ts +++ b/core/frontend/src/render/webgl/TechniqueId.ts @@ -23,7 +23,7 @@ export const enum TechniqueId { PointString, Edge, SilhouetteEdge, - TerrainMesh, + RealityMesh, // Techniques with a single associated shader that operates on the entire image CompositeHilite, diff --git a/core/frontend/src/render/webgl/glsl/Color.ts b/core/frontend/src/render/webgl/glsl/Color.ts index 1b76e24131d..74143939d35 100644 --- a/core/frontend/src/render/webgl/glsl/Color.ts +++ b/core/frontend/src/render/webgl/glsl/Color.ts @@ -55,8 +55,12 @@ export function addColor(builder: ProgramBuilder) { } }); }); + addVaryingColor(builder, getComputeColor(builder.vert)); +} +/** @internal */ +export function addVaryingColor(builder: ProgramBuilder, computeVertexBase: string) { builder.addVarying("v_color", VariableType.Vec4); - builder.vert.set(VertexShaderComponent.ComputeBaseColor, getComputeColor(builder.vert)); + builder.vert.set(VertexShaderComponent.ComputeBaseColor, computeVertexBase); builder.frag.set(FragmentShaderComponent.ComputeBaseColor, computeBaseColor); } diff --git a/core/frontend/src/render/webgl/glsl/Common.ts b/core/frontend/src/render/webgl/glsl/Common.ts index 34ec718d8e8..37e1e612fac 100644 --- a/core/frontend/src/render/webgl/glsl/Common.ts +++ b/core/frontend/src/render/webgl/glsl/Common.ts @@ -88,7 +88,7 @@ function setShaderFlags(uniform: UniformHandle, params: DrawParams) { const geom = params.geometry.asLUT; if (undefined !== geom) { - // Could also be TerrainMeshGeometry, so only detect non-uniform color if explicitly LUTGeometry. + // Could also be RealityMeshGeometry, so only detect non-uniform color if explicitly LUTGeometry. const color = geom.getColor(params.target); if (color.isNonUniform) shaderFlagArray[kShaderBitNonUniformColor] = 1; diff --git a/core/frontend/src/render/webgl/glsl/Edge.ts b/core/frontend/src/render/webgl/glsl/Edge.ts index 24c447f5d82..f4d6b71eb70 100644 --- a/core/frontend/src/render/webgl/glsl/Edge.ts +++ b/core/frontend/src/render/webgl/glsl/Edge.ts @@ -16,7 +16,7 @@ import { addShaderFlags } from "./Common"; import { addWhiteOnWhiteReversal } from "./Fragment"; import { addAdjustWidth, addLineCode } from "./Polyline"; import { octDecodeNormal } from "./Surface"; -import { addLineWeight, addModelViewMatrix, addNormalMatrix, addProjectionMatrix } from "./Vertex"; +import { addLineWeight, addModelViewMatrix, addModelViewProjectionMatrix, addNormalMatrix, addProjectionMatrix } from "./Vertex"; import { addModelToWindowCoordinates, addViewport } from "./Viewport"; const decodeEndPointAndQuadIndices = ` @@ -62,16 +62,15 @@ const computePosition = ` vec4 other = g_otherPos; float miterAdjust = 0.0; float weight = computeLineWeight(); - float clipDist; - g_windowPos = modelToWindowCoordinates(rawPos, other, clipDist); + vec4 pos; + g_windowPos = modelToWindowCoordinates(rawPos, other, pos, v_eyeSpace); if (g_windowPos.w == 0.0) // Clipped out. return g_windowPos; - vec4 clipPos = rawPos + clipDist * (other - rawPos); - vec4 pos = MAT_MVP * clipPos; - - vec4 projOther = modelToWindowCoordinates(other, rawPos, clipDist); + vec4 otherPos; + vec3 otherMvPos; + vec4 projOther = modelToWindowCoordinates(other, rawPos, otherPos, otherMvPos); g_windowDir = projOther.xy - g_windowPos.xy; @@ -117,9 +116,10 @@ function createBase(isSilhouette: boolean, instanced: IsInstanced, isAnimated: I vert.addGlobal("lineCodeEyePos", VariableType.Vec4); vert.addGlobal("lineCodeDist", VariableType.Float, "0.0"); - addModelToWindowCoordinates(vert); // adds u_mvp, u_viewportTransformation + addModelToWindowCoordinates(vert); // adds u_mvp, u_viewportTransformation, and sets g_eyeSpace addProjectionMatrix(vert); addLineCode(builder, lineCodeArgs); + builder.addVarying("v_eyeSpace", VariableType.Vec3); vert.set(VertexShaderComponent.ComputePosition, computePosition); builder.addVarying("v_lnInfo", VariableType.Vec4); addAdjustWidth(vert); @@ -131,6 +131,7 @@ function createBase(isSilhouette: boolean, instanced: IsInstanced, isAnimated: I if (isSilhouette) { addNormalMatrix(vert); + addModelViewProjectionMatrix(vert); vert.set(VertexShaderComponent.CheckForEarlyDiscard, checkForSilhouetteDiscard); vert.addFunction(octDecodeNormal); } diff --git a/core/frontend/src/render/webgl/glsl/FeatureSymbology.ts b/core/frontend/src/render/webgl/glsl/FeatureSymbology.ts index e403c2515b3..25c599dd09e 100644 --- a/core/frontend/src/render/webgl/glsl/FeatureSymbology.ts +++ b/core/frontend/src/render/webgl/glsl/FeatureSymbology.ts @@ -15,7 +15,7 @@ import { import { System } from "../System"; import { FeatureMode, TechniqueFlags } from "../TechniqueFlags"; import { addExtractNthBit, addEyeSpace, addUInt32s } from "./Common"; -import { decodeDepthRgb } from "./Decode"; +import { decodeDepthRgb, decodeUint24 } from "./Decode"; import { addWindowToTexCoords, assignFragColor, computeLinearDepth } from "./Fragment"; import { addLookupTable } from "./LookupTable"; import { addRenderPass } from "./RenderPass"; @@ -56,13 +56,17 @@ export function addOvrFlagConstants(builder: ShaderBuilder): void { const computeLUTFeatureIndex = `g_featureAndMaterialIndex.xyz`; const computeInstanceFeatureIndex = `a_featureId`; -function computeFeatureIndex(instanced: boolean): string { - return `g_featureIndex = ${instanced ? computeInstanceFeatureIndex : computeLUTFeatureIndex};`; -} -function getFeatureIndex(instanced: boolean): string { +function computeFeatureIndex(vertex: VertexShaderBuilder): string { + if (vertex.usesInstancedGeometry) + return `g_featureIndex = ${computeInstanceFeatureIndex};`; + else if (vertex.usesVertexTable) + return `g_featureIndex = ${computeLUTFeatureIndex};`; + else return ""; +} +function getFeatureIndex(vertex: VertexShaderBuilder): string { return ` float getFeatureIndex() { - ${computeFeatureIndex(instanced)}; + ${computeFeatureIndex(vertex)} return decodeUInt24(g_featureIndex); } `; @@ -125,9 +129,11 @@ float computeLineCode() { function addFeatureIndex(vert: VertexShaderBuilder): void { vert.addGlobal("g_featureIndex", VariableType.Vec3); - vert.addFunction(getFeatureIndex(vert.usesInstancedGeometry)); - if (!vert.usesInstancedGeometry) + vert.addFunction(decodeUint24); + vert.addFunction(getFeatureIndex(vert)); + + if (vert.usesVertexTable && !vert.usesInstancedGeometry) addFeatureAndMaterialLookup(vert); } @@ -165,7 +171,7 @@ function addCommon(builder: ProgramBuilder, mode: FeatureMode, opts: FeatureSymb if (!haveOverrides) { // For pick output we must compute g_featureIndex... if (FeatureMode.Pick === mode) - vert.set(VertexShaderComponent.ComputeFeatureOverrides, computeFeatureIndex(vert.usesInstancedGeometry)); + vert.set(VertexShaderComponent.ComputeFeatureOverrides, computeFeatureIndex(vert)); return true; } diff --git a/core/frontend/src/render/webgl/glsl/Polyline.ts b/core/frontend/src/render/webgl/glsl/Polyline.ts index 0bf4be8cc28..a52471ecd61 100644 --- a/core/frontend/src/render/webgl/glsl/Polyline.ts +++ b/core/frontend/src/render/webgl/glsl/Polyline.ts @@ -196,6 +196,7 @@ function addCommon(prog: ProgramBuilder) { addLineWeight(vert); vert.addGlobal("miterAdjust", VariableType.Float, "0.0"); + prog.addVarying("v_eyeSpace", VariableType.Vec3); vert.set(VertexShaderComponent.ComputePosition, computePosition); prog.addVarying("v_lnInfo", VariableType.Vec4); addAdjustWidth(vert); @@ -232,14 +233,11 @@ const computePosition = ` v_lnInfo = vec4(0.0, 0.0, 0.0, 0.0); // init and set flag to false vec4 next = g_nextPos; - float clipDist; - g_windowPos = modelToWindowCoordinates(rawPos, next, clipDist); + vec4 pos; + g_windowPos = modelToWindowCoordinates(rawPos, next, pos, v_eyeSpace); if (g_windowPos.w == 0.0) return g_windowPos; - vec4 clipPos = rawPos + clipDist * (next - rawPos); - vec4 pos = MAT_MVP * clipPos; - float param = a_param; float weight = computeLineWeight(); float scale = 1.0, directionScale = 1.0; @@ -257,7 +255,9 @@ const computePosition = ` param -= kNegatePerp; } - vec4 projNext = modelToWindowCoordinates(next, rawPos, clipDist); + vec4 otherPos; + vec3 otherMvPos; + vec4 projNext = modelToWindowCoordinates(next, rawPos, otherPos, otherMvPos); g_windowDir = projNext.xy - g_windowPos.xy; if (param < kJointBase) { @@ -269,7 +269,7 @@ const computePosition = ` if (kNone != param) { vec2 delta = vec2(0.0); vec4 prev = g_prevPos; - vec4 projPrev = modelToWindowCoordinates(prev, rawPos, clipDist); + vec4 projPrev = modelToWindowCoordinates(prev, rawPos, otherPos, otherMvPos); vec2 prevDir = g_windowPos.xy - projPrev.xy; float thisLength = sqrt(g_windowDir.x * g_windowDir.x + g_windowDir.y * g_windowDir.y); const float s_minNormalizeLength = 1.0E-5; // avoid normalizing zero length vectors. diff --git a/core/frontend/src/render/webgl/glsl/RealityMesh.ts b/core/frontend/src/render/webgl/glsl/RealityMesh.ts new file mode 100644 index 00000000000..38ca8bb3186 --- /dev/null +++ b/core/frontend/src/render/webgl/glsl/RealityMesh.ts @@ -0,0 +1,250 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Bentley Systems, Incorporated. All rights reserved. +* See LICENSE.md in the project root for license terms and full copyright notice. +*--------------------------------------------------------------------------------------------*/ +/** @packageDocumentation + * @module WebGL + */ + +import { assert } from "@bentley/bentleyjs-core"; +import { ColorDef } from "@bentley/imodeljs-common"; +import { AttributeMap } from "../AttributeMap"; +import { TextureUnit } from "../RenderFlags"; +import { FragmentShaderBuilder, FragmentShaderComponent, ProgramBuilder, VariableType, VertexShaderComponent } from "../ShaderBuilder"; +import { System } from "../System"; +import { FeatureMode, IsShadowable, IsThematic, TechniqueFlags } from "../TechniqueFlags"; +import { TechniqueId } from "../TechniqueId"; +import { Texture } from "../Texture"; +import { addVaryingColor } from "./Color"; +import { addShaderFlags, addUInt32s } from "./Common"; +import { decodeDepthRgb, unquantize2d } from "./Decode"; +import { addFeatureSymbology, FeatureSymbologyOptions } from "./FeatureSymbology"; +import { addAltPickBufferOutputs, addPickBufferOutputs, assignFragColor } from "./Fragment"; +import { addColorPlanarClassifier, addFeaturePlanarClassifier, addHilitePlanarClassifier } from "./PlanarClassification"; +import { addSolarShadowMap } from "./SolarShadowMapping"; +import { addClassificationTranslucencyDiscard, octDecodeNormal } from "./Surface"; +import { addThematicDisplay, getComputeThematicIndex } from "./Thematic"; +import { addModelViewProjectionMatrix, addNormalMatrix } from "./Vertex"; + +const computePosition = "gl_PointSize = 1.0; return MAT_MVP * rawPos;"; +const computeNormal = ` + vec3 normal = octDecodeNormal(a_norm); // normal coming in for is already in world space + g_hillshadeIndex = normal.z; // save off world Z for thematic hill shade mode index + return normalize(u_worldToViewN * normal); +`; + +const applyTexture = ` +bool applyTexture(inout vec4 col, sampler2D sampler, mat4 params) { + vec4 texTransform = params[0].xyzw; + vec4 texClip = params[1].xyzw; + float layerAlpha = params[2].x; + vec2 uv = vec2(texTransform[0] + texTransform[2] * v_texCoord.x, texTransform[1] + texTransform[3] * v_texCoord.y); + if (uv.x >= texClip[0] && uv.x <= texClip[2] && uv.y >= texClip[1] && uv.y <= texClip[3]) { + uv.y = 1.0 - uv.y; + vec4 texCol = TEXTURE(sampler, uv); + float alpha = layerAlpha * texCol.a; + if (alpha > 0.05) { + col.rgb = (1.0 - alpha) * col.rgb + alpha * texCol.rgb; + if (texCol.a > 0.1) + featureIncrement = params[2].y; + if (alpha > col.a) + col.a = alpha; + } + return true; + } + + return false; +} +`; + +const computeTexCoord = "return unquantize2d(a_uvParam, u_qTexCoordParams);"; +const overrideFeatureId = "return addUInt32s(feature_id * 255.0, vec4(featureIncrement, 0.0, 0.0, 0.0)) / 255.0;"; + +function addTextures(builder: ProgramBuilder, maxTexturesPerMesh: number) { + builder.vert.addFunction(unquantize2d); + builder.addFunctionComputedVarying("v_texCoord", VariableType.Vec2, "computeTexCoord", computeTexCoord); + builder.vert.addUniform("u_qTexCoordParams", VariableType.Vec4, (prog) => { + prog.addGraphicUniform("u_qTexCoordParams", (uniform, params) => { + const realityMesh = params.geometry.asRealityMesh!; + if (undefined !== realityMesh.uvQParams) { + uniform.setUniform4fv(realityMesh.uvQParams); + } + }); + }); + + builder.frag.addUniform("u_texturesPresent", VariableType.Boolean, (program) => { + program.addGraphicUniform("u_texturesPresent", (uniform, params) => { + const textureCount = params.geometry.asRealityMesh!.textureParams?.textures.length; + uniform.setUniform1i(textureCount ? 1 : 0); + }); + }); + + for (let i = 0; i < maxTexturesPerMesh; i++) { + const textureLabel = `s_texture${i}`; + builder.frag.addUniform(textureLabel, VariableType.Sampler2D, (prog) => { + prog.addGraphicUniform(textureLabel, (uniform, params) => { + const textureUnits = [TextureUnit.RealityMesh0, TextureUnit.RealityMesh1, params.target.drawForReadPixels ? TextureUnit.ShadowMap : TextureUnit.PickDepthAndOrder, TextureUnit.RealityMesh3, TextureUnit.RealityMesh4, TextureUnit.RealityMesh5]; + const realityMesh = params.geometry.asRealityMesh!; + const realityTexture = realityMesh.textureParams ? realityMesh.textureParams.textures[i] : undefined; + if (realityTexture !== undefined) { + const texture = realityTexture as Texture; + texture.texture.bindSampler(uniform, textureUnits[i]); + } else { + // assert(false, "Terrain Mesh texture not defined when beginning texture."); + System.instance.ensureSamplerBound(uniform, textureUnits[i]); + } + }); + }); + const paramsLabel = `u_texTransform${i}`; + builder.frag.addUniform(paramsLabel, VariableType.Mat4, (prog) => { + prog.addGraphicUniform(paramsLabel, (uniform, params) => { + const realityMesh = params.geometry.asRealityMesh!; + const textureParams = realityMesh.textureParams; + assert(undefined !== textureParams); + if (undefined !== textureParams) { + uniform.setMatrix4(textureParams.matrices[i]); + } + }); + }); + } +} +function baseColorFromTextures(textureCount: number, applyFeatureColor: string) { + const applyTextureStrings = []; + + for (let i = 0; i < textureCount; i++) + applyTextureStrings.push(`if (applyTexture(col, s_texture${i}, u_texTransform${i})) doDiscard = false; `); + + return ` + if (!u_texturesPresent) + return u_baseColor; + + bool doDiscard = true; + vec4 col = u_baseColor; + ${applyTextureStrings.join("\n ")} + if (doDiscard) + discard; + + ${applyFeatureColor} + + return col; +`; +} + +// feature_rgb.r = -1.0 if rgb color not overridden for feature. +// feature_alpha = -1.0 if alpha not overridden for feature. +const mixFeatureColor = ` + col.rgb = mix(col.rgb, mix(col.rgb, v_color.rgb, u_overrideColorMix), step(0.0, v_color.r)); + col.a = mix(col.a, v_color.a, step(0.0, v_color.a)); + `; + +function addThematicToRealityMesh(builder: ProgramBuilder, gradientTextureUnit: TextureUnit) { + addNormalMatrix(builder.vert); + builder.vert.addFunction(octDecodeNormal); + builder.vert.addGlobal("g_hillshadeIndex", VariableType.Float); + builder.addFunctionComputedVarying("v_n", VariableType.Vec3, "computeLightingNormal", computeNormal); + addThematicDisplay(builder, false, true); + builder.addInlineComputedVarying("v_thematicIndex", VariableType.Float, getComputeThematicIndex(builder.vert.usesInstancedGeometry, false, false)); + builder.vert.addUniform("u_worldToViewN", VariableType.Mat3, (prog) => { + prog.addGraphicUniform("u_worldToViewN", (uniform, params) => { + params.target.uniforms.branch.bindWorldToViewNTransform(uniform, params.geometry, false); + }); + }); + builder.frag.addUniform("s_texture", VariableType.Sampler2D, (prog) => { + prog.addGraphicUniform("s_texture", (uniform, params) => { + params.target.uniforms.thematic.bindTexture(uniform, gradientTextureUnit >= 0 ? gradientTextureUnit : (params.target.drawForReadPixels ? TextureUnit.ShadowMap : TextureUnit.PickDepthAndOrder)); + }); + }); +} + +function addColorOverrideMix(frag: FragmentShaderBuilder) { + frag.addUniform("u_overrideColorMix", VariableType.Float, (prog) => { + prog.addGraphicUniform("u_overrideColorMix", (uniform, params) => { + uniform.setUniform1f(params.geometry.asRealityMesh!.overrideColorMix); + }); + }); + +} + +/** @internal */ +export function createClassifierRealityMeshHiliter(): ProgramBuilder { + const builder = new ProgramBuilder(AttributeMap.findAttributeMap(TechniqueId.RealityMesh, false)); + addHilitePlanarClassifier(builder, false); + const vert = builder.vert; + vert.set(VertexShaderComponent.ComputePosition, computePosition); + addModelViewProjectionMatrix(vert); + builder.frag.set(FragmentShaderComponent.AssignFragData, assignFragColor); + return builder; +} + +/** @internal */ +export default function createRealityMeshBuilder(flags: TechniqueFlags): ProgramBuilder { + const builder = new ProgramBuilder(AttributeMap.findAttributeMap(TechniqueId.RealityMesh, false)); + const vert = builder.vert; + vert.set(VertexShaderComponent.ComputePosition, computePosition); + addModelViewProjectionMatrix(vert); + + if (flags.isShadowable === IsShadowable.Yes) + addSolarShadowMap(builder, true); + + const frag = builder.frag; + frag.addGlobal("featureIncrement", VariableType.Float, "0.0"); + frag.set(FragmentShaderComponent.OverrideFeatureId, overrideFeatureId); + let textureCount = System.instance.maxRealityImageryLayers; + let gradientTextureUnit = TextureUnit.RealityMeshThematicGradient; + const caps = System.instance.capabilities; + if (Math.min(caps.maxFragTextureUnits, caps.maxVertTextureUnits) < 16 && IsThematic.Yes === flags.isThematic) { + textureCount--; // steal the last bg map layer texture for thematic gradient (just when thematic display is applied) + gradientTextureUnit = -1; // is dependent on drawing mode so will set later + } + + const feat = flags.featureMode; + let opts = FeatureMode.Overrides === feat ? FeatureSymbologyOptions.Surface : FeatureSymbologyOptions.None; + let applyFragmentFeatureColor = ""; + + if (flags.isClassified) { + opts &= ~FeatureSymbologyOptions.Alpha; + addColorPlanarClassifier(builder, flags.isTranslucent, flags.isThematic); + addClassificationTranslucencyDiscard(builder); + } + + addFeatureSymbology(builder, feat, opts); + if (feat === FeatureMode.Overrides) { + addShaderFlags(builder); + addVaryingColor(builder, "return vec4(-1.0, -1.0, -1.0, -1.0);" ); + applyFragmentFeatureColor = mixFeatureColor; + addColorOverrideMix(builder.frag); + } + const computeFragmentBaseColor = baseColorFromTextures(textureCount, applyFragmentFeatureColor); + + frag.addFunction(applyTexture); + frag.set(FragmentShaderComponent.ComputeBaseColor, computeFragmentBaseColor); + frag.addFunction(addUInt32s); + builder.frag.addUniform("u_baseColor", VariableType.Vec4, (prog) => { + prog.addGraphicUniform("u_baseColor", (uniform, params) => { + const realityMesh = params.geometry.asRealityMesh!; + const baseColor = (realityMesh.baseColor ? realityMesh.baseColor : ColorDef.create(0xff000000)).colors; + uniform.setUniform4fv([baseColor.r / 255, baseColor.g / 255, baseColor.b / 255, 1 - baseColor.t / 255]); + }); + }); + builder.frag.set(FragmentShaderComponent.ComputeBaseColor, computeFragmentBaseColor); + if (!flags.isTranslucent) { + if (FeatureMode.None !== feat) { + if (flags.isClassified) + addFeaturePlanarClassifier(builder); + + builder.frag.addFunction(decodeDepthRgb); + if (flags.isClassified) + addPickBufferOutputs(builder.frag); + else + addAltPickBufferOutputs(builder.frag); + } + } + + addTextures(builder, textureCount); + + if (IsThematic.Yes === flags.isThematic) + addThematicToRealityMesh(builder, gradientTextureUnit); + + return builder; +} + diff --git a/core/frontend/src/render/webgl/glsl/TerrainMesh.ts b/core/frontend/src/render/webgl/glsl/TerrainMesh.ts deleted file mode 100644 index 88d52968903..00000000000 --- a/core/frontend/src/render/webgl/glsl/TerrainMesh.ts +++ /dev/null @@ -1,197 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Bentley Systems, Incorporated. All rights reserved. -* See LICENSE.md in the project root for license terms and full copyright notice. -*--------------------------------------------------------------------------------------------*/ -/** @packageDocumentation - * @module WebGL - */ - -import { assert } from "@bentley/bentleyjs-core"; -import { ColorDef } from "@bentley/imodeljs-common"; -import { AttributeMap } from "../AttributeMap"; -import { TextureUnit } from "../RenderFlags"; -import { FragmentShaderComponent, ProgramBuilder, VariableType, VertexShaderComponent } from "../ShaderBuilder"; -import { System } from "../System"; -import { FeatureMode, IsShadowable, IsThematic, TechniqueFlags } from "../TechniqueFlags"; -import { TechniqueId } from "../TechniqueId"; -import { Texture } from "../Texture"; -import { addUInt32s } from "./Common"; -import { unquantize2d } from "./Decode"; -import { addColorPlanarClassifier } from "./PlanarClassification"; -import { addSolarShadowMap } from "./SolarShadowMapping"; -import { addClassificationTranslucencyDiscard, octDecodeNormal } from "./Surface"; -import { addThematicDisplay, getComputeThematicIndex } from "./Thematic"; -import { addModelViewProjectionMatrix, addNormalMatrix } from "./Vertex"; - -const computePosition = "gl_PointSize = 1.0; return MAT_MVP * rawPos;"; -const computeNormal = ` - vec3 normal = octDecodeNormal(a_norm); // normal coming in for terrain is already in world space - g_hillshadeIndex = normal.z; // save off world Z for thematic hill shade mode index - return normalize(u_worldToViewN * normal); -`; - -const applyTexture = ` -bool applyTexture(inout vec4 col, sampler2D sampler, mat4 params) { - vec4 texTransform = params[0].xyzw; - vec4 texClip = params[1].xyzw; - float layerAlpha = params[2].x; - vec2 uv = vec2(texTransform[0] + texTransform[2] * v_texCoord.x, texTransform[1] + texTransform[3] * v_texCoord.y); - if (uv.x >= texClip[0] && uv.x <= texClip[2] && uv.y >= texClip[1] && uv.y <= texClip[3]) { - uv.y = 1.0 - uv.y; - vec4 texCol = TEXTURE(sampler, uv); - float alpha = layerAlpha * texCol.a; - if (alpha > 0.05) { - col.rgb = (1.0 - alpha) * col.rgb + alpha * texCol.rgb; - if (texCol.a > 0.1) - featureIncrement = params[2].y; - if (alpha > col.a) - col.a = alpha; - } - return true; - } - - return false; -} -`; - -const computeTexCoord = "return unquantize2d(a_uvParam, u_qTexCoordParams);"; -const overrideFeatureId = "return addUInt32s(feature_id * 255.0, vec4(featureIncrement, 0.0, 0.0, 0.0)) / 255.0;"; - -function createBuilder(shadowable: IsShadowable): ProgramBuilder { - const builder = new ProgramBuilder(AttributeMap.findAttributeMap(TechniqueId.TerrainMesh, false)); - const vert = builder.vert; - vert.set(VertexShaderComponent.ComputePosition, computePosition); - addModelViewProjectionMatrix(vert); - - if (shadowable === IsShadowable.Yes) - addSolarShadowMap(builder, true); - - const frag = builder.frag; - frag.addGlobal("featureIncrement", VariableType.Float, "0.0"); - frag.set(FragmentShaderComponent.OverrideFeatureId, overrideFeatureId); - return builder; -} - -function addTextures(builder: ProgramBuilder, maxTexturesPerMesh: number) { - builder.vert.addFunction(unquantize2d); - builder.addFunctionComputedVarying("v_texCoord", VariableType.Vec2, "computeTexCoord", computeTexCoord); - builder.vert.addUniform("u_qTexCoordParams", VariableType.Vec4, (prog) => { - prog.addGraphicUniform("u_qTexCoordParams", (uniform, params) => { - const terrainMesh = params.geometry.asTerrainMesh!; - if (undefined !== terrainMesh.uvQParams) { - uniform.setUniform4fv(terrainMesh.uvQParams); - } - }); - }); - - builder.frag.addUniform("u_texturesPresent", VariableType.Boolean, (program) => { - program.addGraphicUniform("u_texturesPresent", (uniform, params) => { - const textureCount = params.geometry.asTerrainMesh!.textureParams?.textures.length; - uniform.setUniform1i(textureCount ? 1 : 0); - }); - }); - - for (let i = 0; i < maxTexturesPerMesh; i++) { - const textureLabel = `s_texture${i}`; - builder.frag.addUniform(textureLabel, VariableType.Sampler2D, (prog) => { - prog.addGraphicUniform(textureLabel, (uniform, params) => { - const textureUnits = [TextureUnit.TerrainMesh0, TextureUnit.TerrainMesh1, params.target.drawForReadPixels ? TextureUnit.ShadowMap : TextureUnit.PickDepthAndOrder, TextureUnit.TerrainMesh3, TextureUnit.TerrainMesh4, TextureUnit.TerrainMesh5]; - const terrainMesh = params.geometry.asTerrainMesh!; - const terrainTexture = terrainMesh.textureParams ? terrainMesh.textureParams.textures[i] : undefined; - if (terrainTexture !== undefined) { - const texture = terrainTexture as Texture; - texture.texture.bindSampler(uniform, textureUnits[i]); - } else { - // assert(false, "Terrain Mesh texture not defined when beginning texture."); - System.instance.ensureSamplerBound(uniform, textureUnits[i]); - } - }); - }); - const paramsLabel = `u_texTransform${i}`; - builder.frag.addUniform(paramsLabel, VariableType.Mat4, (prog) => { - prog.addGraphicUniform(paramsLabel, (uniform, params) => { - const terrainMesh = params.geometry.asTerrainMesh!; - const textureParams = terrainMesh.textureParams; - assert(undefined !== textureParams); - if (undefined !== textureParams) { - uniform.setMatrix4(textureParams.matrices[i]); - } - }); - }); - } - -} - -/** @internal */ -export default function createTerrainMeshBuilder(flags: TechniqueFlags, _featureMode: FeatureMode, thematic: IsThematic): ProgramBuilder { - const builder = createBuilder(flags.isShadowable); - const frag = builder.frag; - const applyTextureStrings = []; - let textureCount = System.instance.maxTerrainImageryLayers; - let gradientTextureUnit = TextureUnit.TerrainThematicGradient; - const caps = System.instance.capabilities; - if (Math.min(caps.maxFragTextureUnits, caps.maxVertTextureUnits) < 16 && IsThematic.Yes === thematic) { - textureCount--; // steal the last bg map layer texture for thematic gradient (just when thematic display is applied) - gradientTextureUnit = -1; // is dependent on drawing mode so will set later - } - - for (let i = 0; i < textureCount; i++) - applyTextureStrings.push(`if (applyTexture(col, s_texture${i}, u_texTransform${i})) doDiscard = false; `); - - const computeBaseColor = ` - if (!u_texturesPresent) - return u_terrainColor; - - bool doDiscard = true; - vec4 col = u_terrainColor; - ${applyTextureStrings.join("\n ")} - if (doDiscard) - discard; - - col.a *= u_terrainTransparency; - return col; -`; - - frag.addFunction(applyTexture); - frag.set(FragmentShaderComponent.ComputeBaseColor, computeBaseColor); - frag.addFunction(addUInt32s); - builder.frag.addUniform("u_terrainColor", VariableType.Vec4, (prog) => { - prog.addGraphicUniform("u_terrainColor", (uniform, params) => { - const terrainMesh = params.geometry.asTerrainMesh!; - const baseColor = (terrainMesh.baseColor ? terrainMesh.baseColor : ColorDef.create(0xff000000)).colors; - uniform.setUniform4fv([baseColor.r / 255, baseColor.g / 255, baseColor.b / 255, 1 - baseColor.t / 255]); - }); - }); - builder.frag.set(FragmentShaderComponent.ComputeBaseColor, computeBaseColor); - builder.frag.addUniform("u_terrainTransparency", VariableType.Float, (prog) => { - prog.addProgramUniform("u_terrainTransparency", (uniform, params) => { - uniform.setUniform1f(1.0 - params.target.terrainTransparency); - }); - }); - addTextures(builder, textureCount); - if (flags.isClassified) { - addColorPlanarClassifier(builder, true /* Transparency? */, thematic); - addClassificationTranslucencyDiscard(builder); - } - - if (IsThematic.Yes === thematic) { - addNormalMatrix(builder.vert); - builder.vert.addFunction(octDecodeNormal); - builder.vert.addGlobal("g_hillshadeIndex", VariableType.Float); - builder.addFunctionComputedVarying("v_n", VariableType.Vec3, "computeLightingNormal", computeNormal); - addThematicDisplay(builder, false, true); - builder.addInlineComputedVarying("v_thematicIndex", VariableType.Float, getComputeThematicIndex(builder.vert.usesInstancedGeometry, false, false)); - builder.vert.addUniform("u_worldToViewN", VariableType.Mat3, (prog) => { - prog.addGraphicUniform("u_worldToViewN", (uniform, params) => { - params.target.uniforms.branch.bindWorldToViewNTransform(uniform, params.geometry, false); - }); - }); - builder.frag.addUniform("s_texture", VariableType.Sampler2D, (prog) => { - prog.addGraphicUniform("s_texture", (uniform, params) => { - params.target.uniforms.thematic.bindTexture(uniform, gradientTextureUnit >= 0 ? gradientTextureUnit : (params.target.drawForReadPixels ? TextureUnit.ShadowMap : TextureUnit.PickDepthAndOrder)); - }); - }); - } - - return builder; -} diff --git a/core/frontend/src/render/webgl/glsl/Vertex.ts b/core/frontend/src/render/webgl/glsl/Vertex.ts index 48d059704a7..63d6c14ff80 100644 --- a/core/frontend/src/render/webgl/glsl/Vertex.ts +++ b/core/frontend/src/render/webgl/glsl/Vertex.ts @@ -12,6 +12,7 @@ import { UniformHandle } from "../UniformHandle"; import { Matrix4 } from "../Matrix"; import { RenderPass, TextureUnit } from "../RenderFlags"; import { VariableType, VertexShaderBuilder } from "../ShaderBuilder"; +import { System } from "../System"; import { decodeUint16, decodeUint24 } from "./Decode"; import { addInstanceOverrides } from "./Instancing"; import { addLookupTable } from "./LookupTable"; @@ -113,7 +114,13 @@ export function addModelViewMatrix(vert: VertexShaderBuilder): void { } const computeNormalMatrix = ` - g_nmx = mat3(MAT_MV); + g_nmx = mat3(u_modelViewN); + g_nmx[0][0] *= u_frustumScale.x; + g_nmx[1][1] *= u_frustumScale.y; +`; + +const computeNormalMatrix2 = ` + g_nmx = transpose(inverse(mat3(MAT_MV))); g_nmx[0][0] *= u_frustumScale.x; g_nmx[1][1] *= u_frustumScale.y; `; @@ -128,7 +135,16 @@ export function addNormalMatrix(vert: VertexShaderBuilder) { }); }); - vert.addInitializer(computeNormalMatrix); + if (System.instance.capabilities.isWebGL2) + vert.addInitializer(computeNormalMatrix2); + else { + vert.addUniform("u_modelViewN", VariableType.Mat3, (prog) => { + prog.addGraphicUniform("u_modelViewN", (uniform, params) => { + params.target.uniforms.branch.bindModelViewNTransform(uniform, params.geometry, false); + }); + }); + vert.addInitializer(computeNormalMatrix); + } } const scratchLutParams = new Float32Array(4); diff --git a/core/frontend/src/render/webgl/glsl/Viewport.ts b/core/frontend/src/render/webgl/glsl/Viewport.ts index c00f4bdc789..39a5b04ae3d 100644 --- a/core/frontend/src/render/webgl/glsl/Viewport.ts +++ b/core/frontend/src/render/webgl/glsl/Viewport.ts @@ -8,7 +8,7 @@ import { ShaderBuilder, VariableType, VertexShaderBuilder } from "../ShaderBuilder"; import { addRenderPass } from "./RenderPass"; -import { addModelViewProjectionMatrix } from "./Vertex"; +import { addModelViewMatrix, addProjectionMatrix } from "./Vertex"; /** @internal */ export function addViewport(shader: ShaderBuilder) { @@ -29,10 +29,12 @@ export function addViewportTransformation(shader: ShaderBuilder) { } const modelToWindowCoordinates = ` -vec4 modelToWindowCoordinates(vec4 position, vec4 next, out float clipDist) { - clipDist = 0.0; +vec4 modelToWindowCoordinates(vec4 position, vec4 next, out vec4 clippedMvpPos, out vec3 clippedMvPos) { if (kRenderPass_ViewOverlay == u_renderPass || kRenderPass_Background == u_renderPass) { - vec4 q = MAT_MVP * position; + vec4 q = MAT_MV * position; + clippedMvPos = q.xyz; + q = u_proj * q; + clippedMvpPos = q; q.xyz /= q.w; q.xyz = (u_viewportTransformation * vec4(q.xyz, 1.0)).xyz; return q; @@ -40,21 +42,23 @@ vec4 modelToWindowCoordinates(vec4 position, vec4 next, out float clipDist) { // Negative values are in front of the camera (visible). float s_maxZ = -u_frustum.x; // use -near (front) plane for segment drop test since u_frustum's near & far are pos. - vec4 q = MAT_MV * position; // eye coordinates. + vec4 q = MAT_MV * position; // eye coordinates. vec4 n = MAT_MV * next; if (q.z > s_maxZ) { if (n.z > s_maxZ) - return vec4(0.0, 0.0, 1.0, 0.0); // Entire segment behind eye. + return vec4(0.0, 0.0, 1.0, 0.0); // Entire segment behind front clip plane. - clipDist = (s_maxZ - q.z) / (n.z - q.z); + float t = (s_maxZ - q.z) / (n.z - q.z); - q.x += clipDist * (n.x - q.x); - q.y += clipDist * (n.y - q.y); - q.z = s_maxZ; // q.z + (s_maxZ - q.z) * (s_maxZ - q.z) / n.z - q.z + q.x += t * (n.x - q.x); + q.y += t * (n.y - q.y); + q.z = s_maxZ; // q.z + (s_maxZ - q.z) / (n.z - q.z) * (n.z - q.z) = s_maxZ } + clippedMvPos = q.xyz; q = u_proj * q; + clippedMvpPos = q; q.xyz /= q.w; // normalized device coords q.xyz = (u_viewportTransformation * vec4(q.xyz, 1.0)).xyz; // window coords return q; @@ -63,7 +67,8 @@ vec4 modelToWindowCoordinates(vec4 position, vec4 next, out float clipDist) { /** @internal */ export function addModelToWindowCoordinates(vert: VertexShaderBuilder) { - addModelViewProjectionMatrix(vert); + addModelViewMatrix(vert); + addProjectionMatrix(vert); addViewportTransformation(vert); addRenderPass(vert); vert.addFunction(modelToWindowCoordinates); diff --git a/core/frontend/src/tile/GltfReader.ts b/core/frontend/src/tile/GltfReader.ts index 0126cd5f963..2a97c13aa60 100644 --- a/core/frontend/src/tile/GltfReader.ts +++ b/core/frontend/src/tile/GltfReader.ts @@ -7,10 +7,11 @@ */ import { assert, ByteStream, Id64String, JsonUtils, utf8ToString } from "@bentley/bentleyjs-core"; -import { Angle, Matrix3d, Point2d, Point3d, Range3d, Transform, Vector3d } from "@bentley/geometry-core"; +import { Angle, Matrix3d, Point2d, Point3d, Range2d, Range3d, Transform, Vector3d } from "@bentley/geometry-core"; import { BatchType, ColorDef, ElementAlignedBox3d, FeatureTable, FillFlags, GltfBufferData, GltfBufferView, GltfDataType, GltfHeader, GltfMeshMode, - ImageSource, ImageSourceFormat, LinePixels, MeshEdge, MeshEdges, MeshPolyline, MeshPolylineList, OctEncodedNormal, PackedFeatureTable, QParams3d, QPoint3d, QPoint3dList, + ImageSource, ImageSourceFormat, LinePixels, MeshEdge, MeshEdges, MeshPolyline, MeshPolylineList, OctEncodedNormal, PackedFeatureTable, QParams2d, QParams3d, QPoint2dList, QPoint3dList, + Quantization, RenderTexture, TextureMapping, TileReadStatus, } from "@bentley/imodeljs-common"; import { getImageSourceFormatForMimeType, imageElementFromImageSource } from "../ImageUtil"; @@ -18,12 +19,14 @@ import { IModelConnection } from "../IModelConnection"; import { GraphicBranch } from "../render/GraphicBranch"; import { InstancedGraphicParams } from "../render/InstancedGraphicParams"; import { DisplayParams } from "../render/primitives/DisplayParams"; -import { Mesh, MeshGraphicArgs, MeshList } from "../render/primitives/mesh/MeshPrimitives"; -import { Triangle } from "../render/primitives/Primitives"; +import { Mesh, MeshGraphicArgs } from "../render/primitives/mesh/MeshPrimitives"; +import { RealityMeshPrimitive } from "../render/primitives/mesh/RealityMeshPrimitive"; import { RenderGraphic } from "../render/RenderGraphic"; import { RenderSystem } from "../render/RenderSystem"; import { TileContent } from "./internal"; +// eslint-disable-next-line prefer-const +let forceLUT = false; /* eslint-disable no-restricted-syntax */ /* ----------------------------------- @@ -89,6 +92,28 @@ export class GltfReaderProps { } } +/** The GltfMeshData contains the raw GLTF mesh data. If the data is suitable to create a [[RealityMesh]] directly, basically in the quantized format produced by + * ContextCapture, then a RealityMesh is created directly from this data. Otherwise, the mesh primitive is populated from the raw data and a MeshPrimitive + * is generated. The MeshPrimitve path is much less efficient but should be rarely used. + * + * @internal + */ +export class GltfMeshData { + public primitive: Mesh; // Populated with vertex and indices only if the mesh cannot be represented as [[RealityMesh]] + public pointQParams?: QParams3d; + public points?: Uint16Array; + public pointRange?: Range3d; + public normals?: Uint16Array; + public uvQParams?: QParams2d; + public uvs?: Uint16Array; + public uvRange?: Range2d; + public indices?: Uint16Array | Uint32Array; + + public constructor(props: Mesh) { + this.primitive = props; + } +} + /** A function that returns true if deserialization of the data supplied by the reader should abort. * @internal */ @@ -214,6 +239,35 @@ export abstract class GltfReader { }; } + private graphicFromMeshData(gltfMesh: GltfMeshData, meshGraphicArgs: MeshGraphicArgs, instances?: InstancedGraphicParams) { + if (!gltfMesh.points || !gltfMesh.pointRange) { + assert(false); + return; + } + const realityMeshPrimitive = (forceLUT || instances) ? undefined : RealityMeshPrimitive.createFromGltfMesh(gltfMesh); + if (realityMeshPrimitive) { + const realityMesh = this._system.createRealityMesh(realityMeshPrimitive); + if (realityMesh) + return realityMesh; + } + const mesh = gltfMesh.primitive; + const pointCount = gltfMesh.points.length / 3; + mesh.points.fromTypedArray(gltfMesh.pointRange, gltfMesh.points); + if (mesh.triangles && gltfMesh.indices) + mesh.triangles.addFromTypedArray(gltfMesh.indices); + + if (gltfMesh.uvs && gltfMesh.uvRange && gltfMesh.uvQParams) { + /** This is ugly and inefficient... unnecessary if Mesh stored uvs as QPoint2dList */ + for (let i = 0, j = 0; i < pointCount; i++) + mesh.uvParams.push(gltfMesh.uvQParams.unquantize(gltfMesh.uvs[j++], gltfMesh.uvs[j++])); + } + if (gltfMesh.normals) + for (const normal of gltfMesh.normals) + mesh.normals.push(new OctEncodedNormal(normal)); + + return mesh.getGraphics(meshGraphicArgs, this._system, instances); + } + private readNodeAndCreateGraphics(renderGraphicList: RenderGraphic[], node: any, featureTable: FeatureTable, parentTransform: Transform | undefined, instances?: InstancedGraphicParams, pseudoRtcBias?: Vector3d): TileReadStatus { if (undefined === node) return TileReadStatus.InvalidTileData; @@ -241,7 +295,7 @@ export abstract class GltfReader { const nodeMesh = this._meshes[meshKey]; if (nodeMesh) { const meshGraphicArgs = new MeshGraphicArgs(); - const meshes = new MeshList(featureTable); + const meshes = []; for (const primitive of nodeMesh.primitives) { const geometry = this.readMeshPrimitive(primitive, featureTable, thisBias); if (undefined !== geometry) @@ -251,11 +305,11 @@ export abstract class GltfReader { let renderGraphic: RenderGraphic | undefined; if (0 !== meshes.length) { if (1 === meshes.length) { - renderGraphic = meshes[0].getGraphics(meshGraphicArgs, this._system, instances); + renderGraphic = this.graphicFromMeshData(meshes[0], meshGraphicArgs, instances); } else { const thisList: RenderGraphic[] = []; for (const mesh of meshes) { - renderGraphic = mesh.getGraphics(meshGraphicArgs, this._system, instances); + renderGraphic = this.graphicFromMeshData(mesh, meshGraphicArgs, instances); if (undefined !== renderGraphic) thisList.push(renderGraphic); } @@ -436,7 +490,7 @@ export abstract class GltfReader { return (rtc[0] === 0.0 && rtc[1] === 0.0 && rtc[2] === 0.0) ? undefined : rtc; } - protected readMeshPrimitive(primitive: any, featureTable?: FeatureTable, pseudoRtcBias?: Vector3d): Mesh | undefined { + protected readMeshPrimitive(primitive: any, featureTable?: FeatureTable, pseudoRtcBias?: Vector3d): GltfMeshData | undefined { const materialName = JsonUtils.asString(primitive.material); const hasBakedLighting = undefined === primitive.attributes.NORMAL; const materialValue = 0 < materialName.length ? JsonUtils.asObject(this._materialValues[materialName]) : undefined; @@ -466,7 +520,7 @@ export abstract class GltfReader { const isPlanar = JsonUtils.asBool(primitive.isPlanar); const isVolumeClassifier = this._isVolumeClassifier; - const mesh = Mesh.create({ + const meshPrimitive = Mesh.create({ displayParams, features: undefined !== featureTable ? new Mesh.Features(featureTable) : undefined, type: primitiveType, @@ -476,10 +530,12 @@ export abstract class GltfReader { hasBakedLighting, isVolumeClassifier, }); + const mesh = new GltfMeshData(meshPrimitive); + // We don't have real colormap - just load material color. This will be used if non-Bentley // tile or fit the color table is uniform. For a non-Bentley, non-Uniform, we'll set the // uv parameters to pick the colors out of the color map texture. - mesh.colorMap.insert(displayParams.fillColor.tbgr); // White... + meshPrimitive.colorMap.insert(displayParams.fillColor.tbgr); // White... const colorIndices = this.readBufferData16(primitive.attributes, "_COLORINDEX"); if (undefined !== colorIndices) { @@ -489,9 +545,15 @@ export abstract class GltfReader { else if (materialValue.extensions && materialValue.extensions.KHR_techniques_webgl && materialValue.extensions.KHR_techniques_webgl.values && Array.isArray(materialValue.extensions.KHR_techniques_webgl.values.u_texStep)) texStep = materialValue.extensions.KHR_techniques_webgl.values.u_texStep; - if (texStep) + if (texStep) { + const uvParams = []; for (let i = 0; i < colorIndices.count; i++) - mesh.uvParams.push(new Point2d(texStep[1] + texStep[0] * colorIndices.buffer[i], .5)); + uvParams.push(new Point2d(texStep[1] + texStep[0] * colorIndices.buffer[i], .5)); + + const paramList = QPoint2dList.fromPoints(uvParams); + mesh.uvs = paramList.toTypedArray(); + mesh.uvQParams = paramList.params; + } } if (primitive.extensions?.KHR_draco_mesh_compression) { @@ -505,9 +567,9 @@ export abstract class GltfReader { return DracoDecoder.readDracoMesh(mesh, primitive, bufferData, dracoExtension.attributes); */ } - this.readBatchTable(mesh, primitive); + this.readBatchTable(mesh.primitive, primitive); - if (!this.readVertices(mesh.points, primitive, pseudoRtcBias)) + if (!this.readVertices(mesh, primitive, pseudoRtcBias)) return undefined; switch (primitiveType) { @@ -515,17 +577,17 @@ export abstract class GltfReader { if (!this.readMeshIndices(mesh, primitive)) return undefined; - if (!displayParams.ignoreLighting && !this.readNormals(mesh.normals, primitive.attributes, "NORMAL")) + if (!displayParams.ignoreLighting && !this.readNormals(mesh, primitive.attributes, "NORMAL")) return undefined; - if (0 === mesh.uvParams.length) - this.readUVParams(mesh.uvParams, primitive.attributes, "TEXCOORD_0"); + if (!mesh.uvs) + this.readUVParams(mesh, primitive.attributes, "TEXCOORD_0"); break; } case Mesh.PrimitiveType.Polyline: case Mesh.PrimitiveType.Point: { - if (undefined !== mesh.polylines && !this.readPolylines(mesh.polylines, primitive, "indices", Mesh.PrimitiveType.Point === primitiveType)) + if (undefined !== mesh.primitive.polylines && !this.readPolylines(mesh.primitive.polylines, primitive, "indices", Mesh.PrimitiveType.Point === primitiveType)) return undefined; break; } @@ -534,16 +596,16 @@ export abstract class GltfReader { return undefined; } } - if (displayParams.textureMapping && 0 === mesh.uvParams.length) + if (displayParams.textureMapping && !mesh.uvs) return undefined; if (primitive.extensions?.CESIUM_primitive_outline) { const data = this.readBufferData32(primitive.extensions.CESIUM_primitive_outline, "indices"); if (data !== undefined) { assert(0 === data.count % 2); - mesh.edges = new MeshEdges(); + mesh.primitive.edges = new MeshEdges(); for (let i = 0; i < data.count;) - mesh.edges.visible.push(new MeshEdge(data.buffer[i++], data.buffer[i++])); + mesh.primitive.edges.visible.push(new MeshEdge(data.buffer[i++], data.buffer[i++])); } } @@ -558,7 +620,7 @@ export abstract class GltfReader { * context capture which have a large offset from the tileset origin that exceeds the * capacity of 32 bit integers. This is essentially an ad hoc RTC applied at read time. */ - protected readVertices(positions: QPoint3dList, primitive: any, pseudoRtcBias?: Vector3d): boolean { + private readVertices(mesh: GltfMeshData, primitive: any, pseudoRtcBias?: Vector3d): boolean { const view = this.getBufferView(primitive.attributes, "POSITION"); if (undefined === view) return false; @@ -569,11 +631,11 @@ export abstract class GltfReader { return false; const strideSkip = view.stride - 3; - const range = Range3d.createNull(); + mesh.pointRange = Range3d.createNull(); for (let i = 0; i < buffer.buffer.length; i += strideSkip) - range.extendXYZ(buffer.buffer[i++], buffer.buffer[i++], buffer.buffer[i++]); + mesh.pointRange.extendXYZ(buffer.buffer[i++], buffer.buffer[i++], buffer.buffer[i++]); - positions.reset(QParams3d.fromRange(range)); + const positions = new QPoint3dList(QParams3d.fromRange(mesh.pointRange)); const scratchPoint = new Point3d(); for (let i = 0, j = 0; i < buffer.count; i++, j += strideSkip) { scratchPoint.set(buffer.buffer[j++], buffer.buffer[j++], buffer.buffer[j++]); @@ -582,6 +644,8 @@ export abstract class GltfReader { positions.add(scratchPoint); } + mesh.pointQParams = positions.params; + mesh.points = positions.toTypedArray(); } else { if (GltfDataType.UnsignedShort !== view.type) return false; @@ -597,20 +661,26 @@ export abstract class GltfReader { return false; const buffer = view.toBufferData(GltfDataType.UnsignedShort); - if (undefined === buffer) + if (undefined === buffer || ! (buffer.buffer instanceof Uint16Array)) return false; - const qpt = QPoint3d.fromScalars(0, 0, 0); - const qRange = Range3d.create(Point3d.create(rangeMin[0], rangeMin[1], rangeMin[2]), Point3d.create(rangeMax[0], rangeMax[1], rangeMax[2])); + assert (buffer.buffer instanceof Uint16Array); + mesh.pointRange = Range3d.createXYZXYZ(rangeMin[0], rangeMin[1], rangeMin[2], rangeMax[0], rangeMax[1], rangeMax[2]); if (undefined !== pseudoRtcBias) { - qRange.low.subtractInPlace(pseudoRtcBias); - qRange.high.subtractInPlace(pseudoRtcBias); + mesh.pointRange.low.subtractInPlace(pseudoRtcBias); + mesh.pointRange.high.subtractInPlace(pseudoRtcBias); } - positions.reset(QParams3d.fromRange(qRange)); - for (let i = 0; i < view.count; i++) { - const index = i * view.stride; - qpt.setFromScalars(buffer.buffer[index], buffer.buffer[index + 1], buffer.buffer[index + 2]); - positions.push(qpt); + mesh.pointQParams = QParams3d.fromRange( mesh.pointRange); + if (3 === view.stride) { + mesh.points = buffer.buffer; + } else { + mesh.points = new Uint16Array(3 * view.count); + for (let i = 0, j = 0; i < view.count; i++) { + const index = i * view.stride; + mesh.points[j++] = buffer.buffer[index]; + mesh.points[j++] = buffer.buffer[index + 1]; + mesh.points[j++] = buffer.buffer[index + 2]; + } } } @@ -632,24 +702,17 @@ export abstract class GltfReader { protected readBatchTable(_mesh: Mesh, _json: any) { } - protected readMeshIndices(mesh: Mesh, json: any): boolean { - const data = this.readBufferData32(json, "indices"); - if (undefined === data) + protected readMeshIndices(mesh: GltfMeshData, json: any): boolean { + const data = this.readBufferData16(json, "indices") || this.readBufferData32(json, "indices"); + if (undefined === data || (!(data.buffer instanceof(Uint16Array)) && ! (data.buffer instanceof(Uint32Array)))) return false; - assert(0 === data.count % 3); - - const triangle = new Triangle(false); - - for (let i = 0; i < data.count; i += 3) { - triangle.setIndices(data.buffer[i], data.buffer[i + 1], data.buffer[i + 2]); - mesh.addTriangle(triangle); - } + mesh.indices = data.buffer; return true; } - protected readNormals(normals: OctEncodedNormal[], json: any, accessorName: string): boolean { + protected readNormals(mesh: GltfMeshData, json: any, accessorName: string): boolean { const view = this.getBufferView(json, accessorName); if (undefined === view) return false; @@ -660,11 +723,12 @@ export abstract class GltfReader { if (undefined === data) return false; + mesh.normals = new Uint16Array(data.count); const scratchNormal = new Vector3d(); const strideSkip = view.stride - 3; for (let i = 0, j = 0; i < data.count; i++, j += strideSkip) { scratchNormal.set(data.buffer[j++], data.buffer[j++], data.buffer[j++]); - normals.push(OctEncodedNormal.fromVector(scratchNormal)); + mesh.normals[i] = OctEncodedNormal.encode(scratchNormal); } return true; } @@ -675,11 +739,12 @@ export abstract class GltfReader { return false; // ###TODO: we shouldn't have to allocate OctEncodedNormal objects...just use uint16s / numbers... + mesh.normals = new Uint16Array(data.count); for (let i = 0; i < data.count; i++) { // ###TODO? not clear why ray writes these as pairs of uint8... const index = i * view.stride; const normal = data.buffer[index] | (data.buffer[index + 1] << 8); - normals.push(new OctEncodedNormal(normal)); + mesh.normals[i] = normal; } return true; } @@ -688,41 +753,65 @@ export abstract class GltfReader { } } - protected readUVParams(params: Point2d[], json: any, accessorName: string): boolean { + private readUVParams(mesh: GltfMeshData, json: any, accessorName: string): boolean { const view = this.getBufferView(json, accessorName); let data: any; - if (view === undefined) { return false; } + if (view === undefined) + return false; + switch (view.type) { case GltfDataType.Float: { data = this.readBufferDataFloat(json, accessorName); + mesh.uvRange = Range2d.createNull(); for (let i = 0; i < data.count; i++) { const index = view.stride * i; // 2 float per param... - params.push(new Point2d(data.buffer[index], data.buffer[index + 1])); + mesh.uvRange.extendXY(data.buffer[index], data.buffer[index + 1]); } - break; + mesh.uvQParams = QParams2d.fromRange(mesh.uvRange); + mesh.uvs = new Uint16Array(data.count * 2); + for (let i = 0, j = 0; i < data.count; i++) { + const index = view.stride * i; // 2 float per param... + mesh.uvs[j++] = Quantization.quantize(data.buffer[index], mesh.uvQParams.origin.x, mesh.uvQParams.scale.x); + mesh.uvs[j++] = Quantization.quantize(data.buffer[index + 1], mesh.uvQParams.origin.y, mesh.uvQParams.scale.y); + } + return true; } case GltfDataType.UnsignedShort: { - // TBD. Support quantized UVParams in shaders rather than expanding here. const extensions = JsonUtils.asObject(view.accessor.extensions); const quantized = undefined !== extensions ? JsonUtils.asObject(extensions.WEB3D_quantized_attributes) : undefined; if (undefined === quantized) return false; - const decodeMatrix = JsonUtils.asArray(quantized.decodeMatrix); - if (undefined === decodeMatrix) { return false; } + const rangeMin = JsonUtils.asArray(quantized.decodedMin); + const rangeMax = JsonUtils.asArray(quantized.decodedMax); + if (undefined === rangeMin || undefined === rangeMax) + return false; const qData = view.toBufferData(GltfDataType.UnsignedShort); - if (undefined === qData) { return false; } + if (undefined === qData || ! (qData.buffer instanceof Uint16Array)) + return false; - for (let i = 0; i < view.count; i++) { - const index = view.stride * i; // 3 uint16 per QPoint3d... - params.push(new Point2d(qData.buffer[index] * decodeMatrix[0] + decodeMatrix[6], qData.buffer[index + 1] * decodeMatrix[4] + decodeMatrix[7])); + mesh.uvRange = Range2d.createXYXY(rangeMin[0], rangeMin[1], rangeMax[0], rangeMax[1]); + mesh.uvQParams = QParams2d.fromRange( mesh.uvRange); + if (2 === view.stride) { + mesh.uvs = qData.buffer; + } else { + mesh.uvs = new Uint16Array(2 * view.count); + for (let i = 0, j = 0; i < view.count; i++) { + const index = i * view.stride; + mesh.uvs[j++] = qData.buffer[index]; + mesh.uvs[j++] = qData.buffer[index + 1]; + } } - break; + return true; } + default: + assert (false); + return false; + } return true; diff --git a/core/frontend/src/tile/OrbitGtTileTree.ts b/core/frontend/src/tile/OrbitGtTileTree.ts index 74abcbbd53d..b0c41eaadb6 100644 --- a/core/frontend/src/tile/OrbitGtTileTree.ts +++ b/core/frontend/src/tile/OrbitGtTileTree.ts @@ -9,8 +9,8 @@ import { BeTimePoint, compareStrings, compareStringsOrUndefined, Id64String } from "@bentley/bentleyjs-core"; import { Point3d, Range3d, Transform, TransformProps, Vector3d } from "@bentley/geometry-core"; import { - BatchType, ColorDef, Feature, - FeatureTable, Frustum, FrustumPlanes, OrbitGtBlobProps, PackedFeatureTable, QParams3d, Quantization, + BatchType, Cartographic, ColorDef, Feature, + FeatureTable, Frustum, FrustumPlanes, GeoCoordStatus, OrbitGtBlobProps, PackedFeatureTable, QParams3d, Quantization, ViewFlagOverrides, } from "@bentley/imodeljs-common"; import { @@ -18,6 +18,7 @@ import { OrbitGtDataManager, OrbitGtFrameData, OrbitGtIProjectToViewForSort, OrbitGtIViewRequest, OrbitGtLevel, OrbitGtTileIndex, OrbitGtTileLoadSorter, OrbitGtTransform, PageCachedFile, PointDataRaw, UrlFS, } from "@bentley/orbitgt-core"; +import { calculateEcefToDbTransformAtLocation } from "../BackgroundMapGeometry"; import { DisplayStyleState } from "../DisplayStyleState"; import { HitDetail } from "../HitDetail"; import { IModelApp } from "../IModelApp"; @@ -371,11 +372,34 @@ export namespace OrbitGtTileTree { await CRSManager.ENGINE.prepareForArea(pointCloudCRS, pointCloudBounds); const wgs84CRS = "4978"; await CRSManager.ENGINE.prepareForArea(wgs84CRS, new OrbitGtBounds()); - const pointCloudToEcef = transformFromOrbitGt(CRSManager.createTransform(pointCloudCRS, pointCloudBounds.min, wgs84CRS)); + const pointCloudToEcef = transformFromOrbitGt(CRSManager.createTransform(pointCloudCRS, new OrbitGtCoordinate(pointCloudCenter.x, pointCloudCenter.y, pointCloudCenter.z), wgs84CRS)); const pointCloudCenterToEcef = pointCloudToEcef.multiplyTransformTransform(addCloudCenter); - const ecefLocation = iModel.ecefLocation; - if (ecefLocation === undefined) return undefined; - const ecefToDb = ecefLocation.getTransform().inverse()!; + + let ecefToDb = iModel.backgroundMapLocation.getMapEcefToDb(0); + // In initial publishing version the iModel ecef Transform was used to locate the reality model. + // This would work well only for tilesets published from that iModel but for iModels the ecef transform is calculated + // at the center of the project extents and the reality model location may differ greatly, and the curvature of the earth + // could introduce significant errors. + // The publishing was modified to calculate the ecef transform at the reality model range center and at the same time the "iModelPublishVersion" + // member was added to the root object. + const ecefOrigin = pointCloudCenterToEcef.getOrigin(); + const dbOrigin = ecefToDb.multiplyPoint3d(ecefOrigin); + const realityOriginToProjectDistance = iModel.projectExtents.distanceToPoint(dbOrigin); + const maxProjectDistance = 1E5; // Only use the project GCS projection if within 100KM of the project. Don't attempt to use GCS if global reality model or in another locale - Results will be unreliable. + if (realityOriginToProjectDistance < maxProjectDistance) { + const cartographicOrigin = Cartographic.fromEcef(ecefOrigin); + const geoConverter = iModel.noGcsDefined ? undefined : iModel.geoServices.getConverter("WGS84"); + if (cartographicOrigin !== undefined && geoConverter !== undefined) { + const geoOrigin = Point3d.create(cartographicOrigin.longitudeDegrees, cartographicOrigin.latitudeDegrees, cartographicOrigin.height); + const response = await geoConverter.getIModelCoordinatesFromGeoCoordinates([geoOrigin]); + if (response.iModelCoords[0].s === GeoCoordStatus.Success) { + const ecefToDbOrigin = await calculateEcefToDbTransformAtLocation(Point3d.fromJSON(response.iModelCoords[0].p), iModel); + if (ecefToDbOrigin) + ecefToDb = ecefToDbOrigin; + } + } + } + pointCloudCenterToDb = ecefToDb.multiplyTransformTransform(pointCloudCenterToEcef); } const params = new OrbitGtTileTreeParams(props, iModel, modelId, pointCloudCenterToDb); diff --git a/core/frontend/src/tile/PrimaryTileTree.ts b/core/frontend/src/tile/PrimaryTileTree.ts index a705f83ded5..5e912ea0c4a 100644 --- a/core/frontend/src/tile/PrimaryTileTree.ts +++ b/core/frontend/src/tile/PrimaryTileTree.ts @@ -6,11 +6,10 @@ * @module Tiles */ -import { assert, compareStrings, Id64String } from "@bentley/bentleyjs-core"; -import { Range3d, StringifiedClipVector, Transform } from "@bentley/geometry-core"; +import { assert, compareBooleans, compareStrings, Id64String } from "@bentley/bentleyjs-core"; +import { Geometry, Range3d, StringifiedClipVector, Transform } from "@bentley/geometry-core"; import { - BatchType, compareIModelTileTreeIds, FeatureAppearance, FeatureAppearanceProvider, HiddenLine, iModelTileTreeIdToString, PrimaryTileTreeId, - ViewFlagOverrides, + BatchType, compareIModelTileTreeIds, FeatureAppearance, FeatureAppearanceProvider, HiddenLine, iModelTileTreeIdToString, PrimaryTileTreeId, ViewFlagOverrides, } from "@bentley/imodeljs-common"; import { IModelApp } from "../IModelApp"; import { IModelConnection } from "../IModelConnection"; @@ -20,7 +19,7 @@ import { RenderClipVolume } from "../render/RenderClipVolume"; import { RenderScheduleState } from "../RenderScheduleState"; import { SpatialViewState } from "../SpatialViewState"; import { SceneContext } from "../ViewContext"; -import { ViewState, ViewState3d } from "../ViewState"; +import { ModelDisplayTransformProvider, ViewState, ViewState3d } from "../ViewState"; import { IModelTileTree, IModelTileTreeParams, iModelTileTreeParamsFromJSON, TileDrawArgs, TileGraphicType, TileTree, TileTreeOwner, TileTreeReference, TileTreeSupplier, @@ -31,6 +30,7 @@ interface PrimaryTreeId { readonly modelId: Id64String; readonly is3d: boolean; readonly isPlanProjection: boolean; + readonly forceNoInstancing: boolean; } class PlanProjectionTileTree extends IModelTileTree { @@ -57,6 +57,8 @@ class PrimaryTreeSupplier implements TileTreeSupplier { let cmp = compareStrings(lhs.modelId, rhs.modelId); if (0 === cmp) { cmp = compareIModelTileTreeIds(lhs.treeId, rhs.treeId); + if (0 === cmp) + cmp = compareBooleans(lhs.forceNoInstancing, rhs.forceNoInstancing); } return cmp; @@ -69,7 +71,7 @@ class PrimaryTreeSupplier implements TileTreeSupplier { const options = { edgesRequired: treeId.edgesRequired, - allowInstancing: undefined === treeId.animationId && !treeId.enforceDisplayPriority && !treeId.sectionCut, + allowInstancing: undefined === treeId.animationId && !treeId.enforceDisplayPriority && !treeId.sectionCut && !id.forceNoInstancing, is3d: id.is3d, batchType: BatchType.Primary, }; @@ -126,6 +128,7 @@ class PrimaryTreeReference extends TileTreeReference { private _owner: TileTreeOwner; private readonly _sectionClip?: StringifiedClipVector; private readonly _sectionCutAppearanceProvider?: FeatureAppearanceProvider; + private _forceNoInstancing: boolean; public constructor(view: ViewState, model: GeometricModelState, planProjection: boolean, transformNodeId?: number, sectionClip?: StringifiedClipVector) { super(); @@ -143,16 +146,36 @@ class PrimaryTreeReference extends TileTreeReference { }); } + this._forceNoInstancing = false; + if (!IModelApp.renderSystem.supportsNonuniformScaledInstancing) { + this.checkForceNoInstancing(view.modelDisplayTransformProvider); + view.onModelDisplayTransformProviderChanged.addListener((provider: ModelDisplayTransformProvider | undefined) => this.checkForceNoInstancing(provider)); + } + this._id = { modelId: model.id, is3d: model.is3d, treeId: this.createTreeId(view, model.id, transformNodeId), isPlanProjection: planProjection, + forceNoInstancing: this._forceNoInstancing, }; this._owner = primaryTreeSupplier.getOwner(this._id, model.iModel); } + private checkForceNoInstancing(provider: ModelDisplayTransformProvider | undefined) { + this._forceNoInstancing = false; + // If this model has a display transform with a non-uniform scale then instancing needs to be forced off when using WebGL1. + if (undefined !== provider) { + const tf = provider.getModelDisplayTransform(this.model.id, Transform.createIdentity()); + const sx = tf.matrix.getColumn(0).magnitudeSquared(); + const sy = tf.matrix.getColumn(1).magnitudeSquared(); + const sz = tf.matrix.getColumn(2).magnitudeSquared(); + if (Math.abs(sx - sy) > Geometry.smallMetricDistance || Math.abs(sx - sz) > Geometry.smallMetricDistance) + this._forceNoInstancing = true; + } + } + protected getViewFlagOverrides(_tree: TileTree) { return this._viewFlagOverrides; } @@ -191,12 +214,13 @@ class PrimaryTreeReference extends TileTreeReference { public get treeOwner(): TileTreeOwner { const newId = this.createTreeId(this.view, this._id.modelId, this._id.treeId.animationTransformNodeId); - if (0 !== compareIModelTileTreeIds(newId, this._id.treeId)) { + if (0 !== compareIModelTileTreeIds(newId, this._id.treeId) || this._forceNoInstancing !== this._id.forceNoInstancing) { this._id = { modelId: this._id.modelId, is3d: this._id.is3d, treeId: newId, isPlanProjection: this._id.isPlanProjection, + forceNoInstancing: this._forceNoInstancing, }; this._owner = primaryTreeSupplier.getOwner(this._id, this.model.iModel); @@ -358,14 +382,14 @@ class MaskTreeReference extends TileTreeReference { public constructor(model: GeometricModelState) { super(); this.model = model; - this._id = { modelId: model.id, is3d: model.is3d, treeId: this.createTreeId(), isPlanProjection: false }; + this._id = { modelId: model.id, is3d: model.is3d, treeId: this.createTreeId(), isPlanProjection: false, forceNoInstancing: false }; this._owner = primaryTreeSupplier.getOwner(this._id, model.iModel); } public get treeOwner(): TileTreeOwner { const newId = this.createTreeId(); if (0 !== compareIModelTileTreeIds(newId, this._id.treeId)) { - this._id = { modelId: this._id.modelId, is3d: this._id.is3d, treeId: newId, isPlanProjection: false }; + this._id = { modelId: this._id.modelId, is3d: this._id.is3d, treeId: newId, isPlanProjection: false, forceNoInstancing: false }; this._owner = primaryTreeSupplier.getOwner(this._id, this.model.iModel); } diff --git a/core/frontend/src/tile/RealityModelTileTree.ts b/core/frontend/src/tile/RealityModelTileTree.ts index 912e37dde0f..9e013b46d7c 100644 --- a/core/frontend/src/tile/RealityModelTileTree.ts +++ b/core/frontend/src/tile/RealityModelTileTree.ts @@ -112,6 +112,7 @@ export function createRealityTileTreeReference(props: RealityModelTileTree.Refer const zeroPoint = Point3d.createZero(); const earthEllipsoid = Ellipsoid.createCenterMatrixRadii(zeroPoint, undefined, Constant.earthRadiusWGS84.equator, Constant.earthRadiusWGS84.equator, Constant.earthRadiusWGS84.polar); const scratchRay = Ray3d.createXAxis(); + /** @internal */ export class RealityModelTileUtils { public static rangeFromBoundingVolume(boundingVolume: any): { range: Range3d, corners?: Point3d[] } | undefined { diff --git a/core/frontend/src/tile/Tile.ts b/core/frontend/src/tile/Tile.ts index b11b35e7f11..17dbc1cd485 100644 --- a/core/frontend/src/tile/Tile.ts +++ b/core/frontend/src/tile/Tile.ts @@ -418,7 +418,7 @@ export abstract class Tile { return TileVisibility.Visible; } - const pixelSize = args.getPixelSize(this); + const pixelSize = args.getPixelSize(this) * args.pixelSizeScaleFactor; const maxSize = this.maximumSize * args.tileSizeModifier; return pixelSize > maxSize ? TileVisibility.TooCoarse : TileVisibility.Visible; diff --git a/core/frontend/src/tile/TileDrawArgs.ts b/core/frontend/src/tile/TileDrawArgs.ts index d03857bf076..e5919d478ec 100644 --- a/core/frontend/src/tile/TileDrawArgs.ts +++ b/core/frontend/src/tile/TileDrawArgs.ts @@ -7,7 +7,7 @@ */ import { BeTimePoint } from "@bentley/bentleyjs-core"; -import { ClipVector, Map4d, Matrix4d, Point3d, Point4d, Range1d, Range3d, Transform, Vector3d } from "@bentley/geometry-core"; +import { ClipVector, Geometry, Map4d, Matrix4d, Point3d, Point4d, Range1d, Range3d, Transform, Vector3d } from "@bentley/geometry-core"; import { FeatureAppearanceProvider, FrustumPlanes, HiddenLine, ViewFlagOverrides } from "@bentley/imodeljs-common"; import { FeatureSymbology } from "../render/FeatureSymbology"; import { GraphicBranch } from "../render/GraphicBranch"; @@ -88,6 +88,8 @@ export class TileDrawArgs { public get symbologyOverrides(): FeatureSymbology.Overrides | undefined { return this.graphics.symbologyOverrides; } /** If defined, tiles will be culled if they do not intersect the clip vector. */ public intersectionClip?: ClipVector; + /** @internal */ + public readonly pixelSizeScaleFactor; /** Compute the size in pixels of the specified tile at the point on its bounding sphere closest to the camera. */ public getPixelSize(tile: Tile): number { @@ -192,6 +194,34 @@ export class TileDrawArgs { return this.viewingSpace.worldToViewMap; } + private computePixelSizeScaleFactor(): number { + // Check to see if a model display transform with non-uniform scaling is being used. + const tf = this.context.viewport.view.getModelDisplayTransform(this.tree.modelId, Transform.createIdentity()); + const scale = []; + scale[0] = tf.matrix.getColumn(0).magnitude(); + scale[1] = tf.matrix.getColumn(1).magnitude(); + scale[2] = tf.matrix.getColumn(2).magnitude(); + if (Math.abs(scale[0] - scale[1]) <= Geometry.smallMetricDistance && Math.abs(scale[0] - scale[2]) <= Geometry.smallMetricDistance) + return 1; + // If the component with the largest scale is not the same as the component with the largest tile range use it to adjust the pixel size. + const rangeDiag = this.tree.range.diagonal(); + let maxS = 0; + let maxR = 0; + if (scale[0] > scale[1]) { + maxS = (scale[0] > scale[2] ? 0 : 2); + } else { + maxS = (scale[1] > scale[2] ? 1 : 2); + } + if (rangeDiag.x > rangeDiag.y) { + maxR = (rangeDiag.x > rangeDiag.z ? 0 : 2); + } else { + maxR = (rangeDiag.y > rangeDiag.z ? 1 : 2); + } + if (maxS !== maxR) + return scale[maxS]; + return 1; + } + /** Constructor */ public constructor(params: TileDrawArgParams) { const { location, tree, context, now, viewFlagOverrides, clipVolume, parentsAndChildrenExclusive, symbologyOverrides } = params; @@ -222,6 +252,8 @@ export class TileDrawArgs { this.parentsAndChildrenExclusive = parentsAndChildrenExclusive; if (context.viewport.view.isCameraEnabled()) this._nearFrontCenter = context.viewport.getFrustum(CoordSystem.World).frontCenter; + + this.pixelSizeScaleFactor = this.computePixelSizeScaleFactor(); } /** A multiplier applied to a [[Tile]]'s `maximumSize` property to adjust level of detail. diff --git a/core/frontend/src/tile/map/CesiumTerrainProvider.ts b/core/frontend/src/tile/map/CesiumTerrainProvider.ts index b869e0c6562..04703f7921c 100644 --- a/core/frontend/src/tile/map/CesiumTerrainProvider.ts +++ b/core/frontend/src/tile/map/CesiumTerrainProvider.ts @@ -8,7 +8,7 @@ */ import { assert, BeDuration, BeTimePoint, ByteStream, ClientRequestContext, Id64String, JsonUtils, utf8ToString } from "@bentley/bentleyjs-core"; import { Point2d, Point3d, Range1d, Vector3d } from "@bentley/geometry-core"; -import { nextPoint3d64FromByteStream, OctEncodedNormal, QPoint2d } from "@bentley/imodeljs-common"; +import { nextPoint3d64FromByteStream, OctEncodedNormal, QParams3d, QPoint2d } from "@bentley/imodeljs-common"; import { request, RequestOptions, Response } from "@bentley/itwin-client"; import { ApproximateTerrainHeights } from "../../ApproximateTerrainHeights"; import { IModelApp } from "../../IModelApp"; @@ -206,20 +206,20 @@ class CesiumTerrainProvider extends TerrainMeshProvider { terrainTile.adjustHeights(minHeight, maxHeight); if (undefined === center || undefined === boundCenter || undefined === boundRadius || undefined === horizonOcclusion) { } - const vertexCount = streamBuffer.nextUint32; - const encodedVertexBuffer = new Uint16Array(blob.buffer, streamBuffer.curPos, vertexCount * 3); - streamBuffer.advance(vertexCount * 6); + const pointCount = streamBuffer.nextUint32; + const encodedVertexBuffer = new Uint16Array(blob.buffer, streamBuffer.curPos, pointCount * 3); + streamBuffer.advance(pointCount * 6); - const uBuffer = encodedVertexBuffer.subarray(0, vertexCount); - const vBuffer = encodedVertexBuffer.subarray(vertexCount, 2 * vertexCount); - const heightBuffer = encodedVertexBuffer.subarray(vertexCount * 2, 3 * vertexCount); + const uBuffer = encodedVertexBuffer.subarray(0, pointCount); + const vBuffer = encodedVertexBuffer.subarray(pointCount, 2 * pointCount); + const heightBuffer = encodedVertexBuffer.subarray(pointCount * 2, 3 * pointCount); zigZagDeltaDecode(uBuffer, vBuffer, heightBuffer); let bytesPerIndex = Uint16Array.BYTES_PER_ELEMENT; const triangleElements = 3; - if (vertexCount > 64 * 1024) { + if (pointCount > 64 * 1024) { // More than 64k vertices, so indices are 32-bit. bytesPerIndex = Uint32Array.BYTES_PER_ELEMENT; } @@ -231,7 +231,7 @@ class CesiumTerrainProvider extends TerrainMeshProvider { const triangleCount = streamBuffer.nextUint32; const indexCount = triangleCount * triangleElements; - const indices = getIndexArray(vertexCount, streamBuffer, indexCount); + const indices = getIndexArray(pointCount, streamBuffer, indexCount); // High water mark decoding based on decompressIndices_ in webgl-loader's loader.js. // https://code.google.com/p/webgl-loader/source/browse/trunk/samples/loader.js?r=99#55 // Copyright 2012 Google Inc., Apache 2.0 license. @@ -248,44 +248,35 @@ class CesiumTerrainProvider extends TerrainMeshProvider { CesiumTerrainProvider._scratchHeightRange.low = minHeight - skirtHeight; CesiumTerrainProvider._scratchHeightRange.high = maxHeight; const projection = terrainTile.getProjection(CesiumTerrainProvider._scratchHeightRange); - - const mesh = TerrainMeshPrimitive.create({ range: projection.localRange }); - for (let i = 0; i < indexCount;) - this.addTriangle(mesh, indices[i++], indices[i++], indices[i++]); + const pointQParams = QParams3d.fromRange(projection.localRange); const uvScale = 1.0 / 32767.0; const heightScale = uvScale * (maxHeight - minHeight); - const westVertexCount = streamBuffer.nextUint32; - const westIndices = getIndexArray(vertexCount, streamBuffer, westVertexCount); + const westCount = streamBuffer.nextUint32; + const westIndices = getIndexArray(pointCount, streamBuffer, westCount); - const southVertexCount = streamBuffer.nextUint32; - const southIndices = getIndexArray(vertexCount, streamBuffer, southVertexCount); + const southCount = streamBuffer.nextUint32; + const southIndices = getIndexArray(pointCount, streamBuffer, southCount); - const eastVertexCount = streamBuffer.nextUint32; - const eastIndices = getIndexArray(vertexCount, streamBuffer, eastVertexCount); + const eastCount = streamBuffer.nextUint32; + const eastIndices = getIndexArray(pointCount, streamBuffer, eastCount); - const northVertexCount = streamBuffer.nextUint32; - const northIndices = getIndexArray(vertexCount, streamBuffer, northVertexCount); + const northCount = streamBuffer.nextUint32; + const northIndices = getIndexArray(pointCount, streamBuffer, northCount); // Extensions... let encodedNormalsBuffer; - let waterMaskBuffer; while (streamBuffer.curPos < streamBuffer.length) { const extensionId = streamBuffer.nextUint8; const extensionLength = streamBuffer.nextUint32; switch (extensionId) { case QuantizedMeshExtensionIds.OctEncodedNormals: - assert(vertexCount * 2 === extensionLength); + assert(pointCount * 2 === extensionLength); encodedNormalsBuffer = new Uint8Array(streamBuffer.arrayBuffer, streamBuffer.curPos, extensionLength); streamBuffer.advance(extensionLength); break; - case QuantizedMeshExtensionIds.WaterMask: - waterMaskBuffer = new Uint8Array(streamBuffer.arrayBuffer, streamBuffer.curPos, extensionLength); - streamBuffer.advance(extensionLength); - break; - case QuantizedMeshExtensionIds.Metadata: const stringLength = streamBuffer.nextUint32; if (stringLength > 0) { @@ -313,14 +304,13 @@ class CesiumTerrainProvider extends TerrainMeshProvider { } } - if (undefined !== encodedNormalsBuffer) { - } - if (undefined !== waterMaskBuffer) { - } + const mesh = TerrainMeshPrimitive.create({ pointQParams, pointCount, indexCount, wantSkirts: this._wantSkirts, westCount, eastCount, southCount, northCount, wantNormals: undefined !== encodedNormalsBuffer }); + for (let i = 0; i < indexCount;) + this.addTriangle(mesh, indices[i++], indices[i++], indices[i++]); const worldToEcef = tile.iModel.getEcefTransform().matrix; - for (let i = 0; i < vertexCount; i++) { + for (let i = 0; i < pointCount; i++) { const u = uBuffer[i]; const v = vBuffer[i]; projection.getPoint(uvScale * u, uvScale * v, minHeight + heightBuffer[i] * heightScale, CesiumTerrainProvider._scratchPoint); @@ -329,7 +319,7 @@ class CesiumTerrainProvider extends TerrainMeshProvider { const normalIndex = i * 2; OctEncodedNormal.decodeValue(encodedNormalsBuffer[normalIndex + 1] << 8 | encodedNormalsBuffer[normalIndex], CesiumTerrainProvider._scratchNormal); worldToEcef.multiplyTransposeVector(CesiumTerrainProvider._scratchNormal, CesiumTerrainProvider._scratchNormal); - mesh.addVertex(CesiumTerrainProvider._scratchPoint, CesiumTerrainProvider._scratchQPoint2d, OctEncodedNormal.fromVector(CesiumTerrainProvider._scratchNormal)); + mesh.addVertex(CesiumTerrainProvider._scratchPoint, CesiumTerrainProvider._scratchQPoint2d, OctEncodedNormal.encode(CesiumTerrainProvider._scratchNormal)); } else { mesh.addVertex(CesiumTerrainProvider._scratchPoint, CesiumTerrainProvider._scratchQPoint2d); } @@ -346,30 +336,30 @@ class CesiumTerrainProvider extends TerrainMeshProvider { this.generateSkirts(mesh, southIndices, projection, -skirtHeight, heightBuffer, minHeight, heightScale, wantNormals); this.generateSkirts(mesh, northIndices, projection, -skirtHeight, heightBuffer, minHeight, heightScale, wantNormals); } + assert (mesh.isCompleted); return mesh; } private addTriangle(mesh: TerrainMeshPrimitive, i0: number, i1: number, i2: number) { - mesh.indices.push(i0); - mesh.indices.push(i1); - mesh.indices.push(i2); + mesh.addTriangle(i0, i1, i2); } private generateSkirts(mesh: TerrainMeshPrimitive, indices: Uint16Array | Uint32Array, projection: MapTileProjection, skirtOffset: number, heightBuffer: Uint16Array, minHeight: number, heightScale: number, wantNormals: boolean) { for (let i = 0; i < indices.length; i++) { const index = indices[i]; + const paramIndex = index * 2; const height = minHeight + heightBuffer[index] * heightScale; - const uv = mesh.uvParams.unquantize(index, CesiumTerrainProvider._scratchPoint2d); - if (wantNormals) { - const normVal = mesh.normals[index].value; - mesh.addVertex(projection.getPoint(uv.x, uv.y, height + skirtOffset), mesh.uvParams.list[index], new OctEncodedNormal(normVal)); + CesiumTerrainProvider._scratchQPoint2d.setFromScalars(mesh.uvs[paramIndex], mesh.uvs[paramIndex+1]); + const uv = mesh.uvQParams.unquantize(CesiumTerrainProvider._scratchQPoint2d.x, CesiumTerrainProvider._scratchQPoint2d.y, CesiumTerrainProvider._scratchPoint2d); + if (wantNormals && mesh.normals) { + mesh.addVertex(projection.getPoint(uv.x, uv.y, height + skirtOffset), CesiumTerrainProvider._scratchQPoint2d, mesh.normals[index]); } else { - mesh.addVertex(projection.getPoint(uv.x, uv.y, height + skirtOffset), mesh.uvParams.list[index]); + mesh.addVertex(projection.getPoint(uv.x, uv.y, height + skirtOffset), CesiumTerrainProvider._scratchQPoint2d); } if (i) { - this.addTriangle(mesh, index, indices[i - 1], mesh.points.length - 2); - this.addTriangle(mesh, index, mesh.points.length - 2, mesh.points.length - 1); + this.addTriangle(mesh, index, indices[i - 1], mesh.nextPointIndex - 2); + this.addTriangle(mesh, index, mesh.nextPointIndex - 2, mesh.nextPointIndex - 1); } } } diff --git a/core/frontend/src/tile/map/EllipsoidTerrainProvider.ts b/core/frontend/src/tile/map/EllipsoidTerrainProvider.ts index c614b5be04b..ef74878689d 100644 --- a/core/frontend/src/tile/map/EllipsoidTerrainProvider.ts +++ b/core/frontend/src/tile/map/EllipsoidTerrainProvider.ts @@ -8,7 +8,7 @@ import { assert, Id64String } from "@bentley/bentleyjs-core"; import { Angle, Ellipsoid, EllipsoidPatch, Point2d, Point3d, Range1d, Range3d, Transform } from "@bentley/geometry-core"; -import { QPoint3dList } from "@bentley/imodeljs-common"; +import { QParams3d, QPoint2d } from "@bentley/imodeljs-common"; import { IModelConnection } from "../../IModelConnection"; import { TerrainMeshPrimitive } from "../../render/primitives/mesh/TerrainMeshPrimitive"; import { MapCartoRectangle, MapTile, QuadId, TerrainMeshProvider } from "../internal"; @@ -16,9 +16,11 @@ import { MapTilingScheme, WebMercatorTilingScheme } from "./MapTilingScheme"; const scratchPoint2d = Point2d.createZero(); const scratchPoint = Point3d.createZero(); +const scratchQPoint2d = new QPoint2d(); const scratchEllipsoid = Ellipsoid.create(Transform.createIdentity()); const scratchZeroRange = Range1d.createXX(0, 0); -const scratchPoints = new Array(); +let scratch8Points: Array; +let scratch8Params: Array; /** Terrain provider that produces geometry that represents a smooth ellipsoid without any height perturbations. * The area within the project extents are represented as planar tiles and other tiles are facetted approximations @@ -41,38 +43,49 @@ export class EllipsoidTerrainProvider extends TerrainMeshProvider { private getPlanarMesh(tile: MapTile): TerrainMeshPrimitive { const projection = tile.getProjection(); - const addQuad = ((m: TerrainMeshPrimitive, i0: number, i1: number, i2: number, i3: number) => m.indices.push(i0, i1, i2, i1, i3, i2)); let mesh: TerrainMeshPrimitive; + const skirtProps = { wantSkirts: false, northCount: 0, southCount: 0, eastCount: 0, westCount: 0}; // Skirts are explicitly created, no need to preallocate if (!this._wantSkirts) { - mesh = TerrainMeshPrimitive.create({ range: projection.localRange }); + mesh = TerrainMeshPrimitive.create({...skirtProps, pointQParams: QParams3d.fromRange(projection.localRange), pointCount: 4, indexCount: 6, wantNormals: false }); for (let v = 0; v < 2; v++) for (let u = 0; u < 2; u++) { - mesh.uvParams.add(Point2d.create(u, 1 - v)); - mesh.points.add(projection.getPoint(u, v, 0, scratchPoint)); + scratchQPoint2d.init(Point2d.create(u, 1 - v, scratchPoint2d), mesh.uvQParams); + mesh.addVertex(projection.getPoint(u, v, 0, scratchPoint), scratchQPoint2d); } - addQuad(mesh, 0, 1, 2, 3); + mesh.addQuad(0, 1, 2, 3); } else { - const points = new Array(); - const params = new Array(); + if (!scratch8Points || !scratch8Params) { + scratch8Points = new Array(); + scratch8Params = new Array(); + for (let i = 0; i < 8; i++) { + scratch8Points.push(Point3d.createZero()); + scratch8Params.push(Point2d.createZero()); + } + } + const skirtHeight = tile.range.xLength() / 20.0; - for (let v = 0; v < 2; v++) + for (let v = 0, i = 0; v < 2; v++) for (let u = 0; u < 2; u++) for (let h = 0; h < 2; h++) { - params.push(Point2d.create(u, 1 - v)); - points.push(projection.getPoint(u, v, h * skirtHeight)); + scratch8Params[i].set(u, 1 - v); + projection.getPoint(u, v, h * skirtHeight, scratch8Points[i]); + i++; } - mesh = TerrainMeshPrimitive.create({ range: Range3d.createArray(points) }); - points.forEach((point) => mesh.points.add(point)); - params.forEach((param) => mesh.uvParams.add(param)); - addQuad(mesh, 0, 2, 4, 6); + mesh = TerrainMeshPrimitive.create({...skirtProps, pointQParams: QParams3d.fromRange(Range3d.createArray(scratch8Points)), pointCount: 8, indexCount: 30, wantNormals: false }); + for (let i = 0 ; i < 8; i++) { + scratchQPoint2d.init(scratch8Params[i], mesh.uvQParams); + mesh.addVertex(scratch8Points[i], scratchQPoint2d); + } + + mesh.addQuad(0, 2, 4, 6); const reorder = [0, 2, 6, 4, 0]; for (let i = 0; i < 4; i++) { const iThis = reorder[i], iNext = reorder[i + 1]; - addQuad(mesh, iThis, iNext, iThis + 1, iNext + 1); + mesh.addQuad(iThis, iNext, iThis + 1, iNext + 1); } } - + assert (mesh.isCompleted); return mesh; } private getGlobeMesh(tile: MapTile): TerrainMeshPrimitive | undefined { @@ -96,53 +109,48 @@ export class EllipsoidTerrainProvider extends TerrainMeshProvider { const skirtPatch = EllipsoidPatch.createCapture(scratchEllipsoid, ellipsoidPatch.longitudeSweep, ellipsoidPatch.latitudeSweep); const scaleFactor = Math.max(.99, 1 - Math.sin(ellipsoidPatch.longitudeSweep.sweepRadians * delta)); skirtPatch.ellipsoid.transformRef.matrix.scaleColumnsInPlace(scaleFactor, scaleFactor, scaleFactor); - const nTotal = globeMeshDimension * globeMeshDimension; + const pointCount = globeMeshDimension * globeMeshDimension; + const rowMin = (bordersNorthPole || this._wantSkirts) ? 0 : 1; + const rowMax = (bordersSouthPole || this._wantSkirts) ? dimensionM1 : dimensionM2; + const colMin = this._wantSkirts ? 0 : 1; + const colMax = this._wantSkirts ? dimensionM1 : dimensionM2; + const indexCount = 6 * (rowMax - rowMin) * (colMax - colMin); - const mesh = TerrainMeshPrimitive.create({ range }); - if (0 === scratchPoints.length) - for (let i = 0; i < nTotal; i++) - scratchPoints.push(Point3d.createZero()); + const mesh = TerrainMeshPrimitive.create({ pointQParams: QParams3d.fromRange(range), pointCount, indexCount, wantSkirts: false, northCount: globeMeshDimension, southCount: globeMeshDimension, eastCount: globeMeshDimension, westCount: globeMeshDimension, wantNormals: false }); for (let iRow = 0, index = 0; iRow < globeMeshDimension; iRow++) { for (let iColumn = 0; iColumn < globeMeshDimension; iColumn++, index++) { let u = (iColumn ? (Math.min(dimensionM2, iColumn) - 1) : 0) * delta; let v = (iRow ? (Math.min(dimensionM2, iRow) - 1) : 0) * delta; - mesh.uvParams.add(Point2d.create(u, 1 - v, scratchPoint2d)); - const thisPoint = scratchPoints[index]; + scratchPoint2d.set(u, 1 - v); if (iRow === 0 || iRow === dimensionM1 || iColumn === 0 || iColumn === dimensionM1) { if (bordersSouthPole && iRow === dimensionM1) - skirtPatch.ellipsoid.radiansToPoint(0, -Angle.piOver2Radians, thisPoint); + skirtPatch.ellipsoid.radiansToPoint(0, -Angle.piOver2Radians, scratchPoint); else if (bordersNorthPole && iRow === 0) - skirtPatch.ellipsoid.radiansToPoint(0, Angle.piOver2Radians, thisPoint); + skirtPatch.ellipsoid.radiansToPoint(0, Angle.piOver2Radians, scratchPoint); else { u += (iColumn === 0) ? -skirtFraction : (iColumn === dimensionM1 ? skirtFraction : 0); v += (iRow === 0) ? -skirtFraction : (iRow === dimensionM1 ? skirtFraction : 0); - skirtPatch.uvFractionToPoint(u, v, thisPoint); + skirtPatch.uvFractionToPoint(u, v, scratchPoint); } } else { - projection.getPoint(u, v, 0, thisPoint); + projection.getPoint(u, v, 0, scratchPoint); } + scratchQPoint2d.init(scratchPoint2d, mesh.uvQParams); + mesh.addVertex(scratchPoint, scratchQPoint2d); } } - QPoint3dList.fromPoints(scratchPoints, mesh.points); - const rowMin = (bordersNorthPole || this._wantSkirts) ? 0 : 1; - const rowMax = (bordersSouthPole || this._wantSkirts) ? dimensionM1 : dimensionM2; - const colMin = this._wantSkirts ? 0 : 1; - const colMax = this._wantSkirts ? dimensionM1 : dimensionM2; for (let iRow = rowMin; iRow < rowMax; iRow++) { for (let iColumn = colMin; iColumn < colMax; iColumn++) { const base = iRow * globeMeshDimension + iColumn; const top = base + globeMeshDimension; - mesh.indices.push(base); - mesh.indices.push(base + 1); - mesh.indices.push(top); - mesh.indices.push(top); - mesh.indices.push(base + 1); - mesh.indices.push(top + 1); + mesh.addTriangle(base, base + 1, top); + mesh.addTriangle(top, base + 1, top + 1); } } + assert (mesh.isCompleted); return mesh; } public async getMesh(tile: MapTile, _data: Uint8Array): Promise { diff --git a/core/frontend/src/tile/map/MapTile.ts b/core/frontend/src/tile/map/MapTile.ts index 1eed67e0f39..8a6459ae36e 100644 --- a/core/frontend/src/tile/map/MapTile.ts +++ b/core/frontend/src/tile/map/MapTile.ts @@ -10,13 +10,13 @@ import { assert, dispose } from "@bentley/bentleyjs-core"; import { AxisOrder, BilinearPatch, ClipPlane, ClipPrimitive, ClipShape, ClipVector, Constant, ConvexClipPlaneSet, EllipsoidPatch, LongitudeLatitudeNumber, Matrix3d, Point3d, PolygonOps, Range1d, Range2d, Range3d, Ray3d, Transform, Vector2d, Vector3d } from "@bentley/geometry-core"; import { ColorByName, ColorDef, FrustumPlanes, GlobeMode, PackedFeatureTable, RenderTexture } from "@bentley/imodeljs-common"; import { IModelApp } from "../../IModelApp"; -import { GraphicBuilder, TileTreeLoadStatus } from "../../imodeljs-frontend"; +import { GraphicBuilder } from "../../render/GraphicBuilder"; import { TerrainMeshPrimitive } from "../../render/primitives/mesh/TerrainMeshPrimitive"; import { RenderGraphic } from "../../render/RenderGraphic"; import { RenderMemory } from "../../render/RenderMemory"; -import { RenderSystem, RenderTerrainMeshGeometry, TerrainTexture } from "../../render/RenderSystem"; +import { RenderRealityMeshGeometry, RenderSystem, TerrainTexture } from "../../render/RenderSystem"; import { ViewingSpace } from "../../ViewingSpace"; -import { ImageryMapTile, MapCartoRectangle, MapTileLoader, MapTileTree, QuadId, RealityTile, Tile, TileContent, TileDrawArgs, TileLoadStatus, TileParams, TraversalSelectionContext } from "../internal"; +import { ImageryMapTile, MapCartoRectangle, MapTileLoader, MapTileTree, QuadId, RealityTile, Tile, TileContent, TileDrawArgs, TileLoadStatus, TileParams, TileTreeLoadStatus, TraversalSelectionContext } from "../internal"; import { TileGraphicType } from "../TileTreeReference"; /** @internal */ @@ -93,7 +93,7 @@ class PlanarProjection extends MapTileProjection { /** @internal */ export interface TerrainTileContent extends TileContent { terrain?: { - geometry?: RenderTerrainMeshGeometry; + geometry?: RenderRealityMeshGeometry; /** Used on leaves to support up-sampling. */ mesh?: TerrainMeshPrimitive; }; @@ -116,7 +116,7 @@ export class MapTile extends RealityTile { private _imageryTiles?: ImageryMapTile[]; public everLoaded = false; // If the tile is only required for availability metadata, load it once and then allow it to be unloaded. protected _heightRange: Range1d | undefined; - protected _geometry?: RenderTerrainMeshGeometry; + protected _geometry?: RenderRealityMeshGeometry; protected _mesh?: TerrainMeshPrimitive; // Primitive retained on leaves only for upsampling. public get isReady(): boolean { return super.isReady && this.baseImageryIsReady; } public get geometry() { return this._geometry; } @@ -421,7 +421,7 @@ export class MapTile extends RealityTile { return undefined; const textures = this.getDrapeTextures(); - const graphic = IModelApp.renderSystem.createTerrainMeshGraphic(geometry, PackedFeatureTable.pack(this.mapLoader.featureTable), this.contentId, this.mapTree.baseColor, this.mapTree.baseTransparent, textures); + const graphic = IModelApp.renderSystem.createRealityMeshGraphic(geometry, PackedFeatureTable.pack(this.mapLoader.featureTable), this.contentId, this.mapTree.baseColor, this.mapTree.baseTransparent, textures); // We no longer need the drape tiles. if (this.imageryIsReady) @@ -646,7 +646,6 @@ export class UpsampledMapTile extends MapTile { if (undefined === parentMesh) { return undefined; } - const thisId = this.quadId, parentId = parent.quadId; const levelDelta = thisId.level - parentId.level; const thisColumn = thisId.column - (parentId.column << levelDelta); @@ -654,9 +653,12 @@ export class UpsampledMapTile extends MapTile { const scale = 1.0 / (1 << levelDelta); const parentParameterRange = Range2d.createXYXY(scale * thisColumn, scale * thisRow, scale * (thisColumn + 1), scale * (thisRow + 1)); const upsample = parentMesh.upsample(parentParameterRange); + if (undefined === upsample) + return undefined; + this.adjustHeights(upsample.heightRange.low, upsample.heightRange.high); const projection = parent.getProjection(this.heightRange); - this._geometry = IModelApp.renderSystem.createTerrainMeshGeometry(upsample.mesh, projection.transformFromLocal); + this._geometry = IModelApp.renderSystem.createRealityMeshFromTerrain(upsample.mesh, projection.transformFromLocal); } return this._geometry; } diff --git a/core/frontend/src/tile/map/MapTileLoader.ts b/core/frontend/src/tile/map/MapTileLoader.ts index 5aa1efbd5ed..c5d63ab5d2d 100644 --- a/core/frontend/src/tile/map/MapTileLoader.ts +++ b/core/frontend/src/tile/map/MapTileLoader.ts @@ -83,7 +83,7 @@ export class MapTileLoader extends RealityTileLoader { return {}; const projection = tile.getProjection(tile.heightRange); - const terrainGeometry = system.createTerrainMeshGeometry(mesh, projection.transformFromLocal); + const terrainGeometry = system.createRealityMeshFromTerrain(mesh, projection.transformFromLocal); let unavailableChild = false; if (quadId.level < this.maxDepth) { diff --git a/core/frontend/src/tile/map/MapTileTree.ts b/core/frontend/src/tile/map/MapTileTree.ts index 069475a84e8..ccdfe95aace 100644 --- a/core/frontend/src/tile/map/MapTileTree.ts +++ b/core/frontend/src/tile/map/MapTileTree.ts @@ -6,12 +6,12 @@ * @module Tiles */ -import { assert, compareBooleans, compareNumbers, compareStrings, Id64String } from "@bentley/bentleyjs-core"; +import { assert, compareBooleans, compareNumbers, compareStrings, compareStringsOrUndefined, CompressedId64Set, Id64String } from "@bentley/bentleyjs-core"; import { Angle, AngleSweep, Constant, Ellipsoid, EllipsoidPatch, Point3d, Range1d, Range3d, Ray3d, Transform, Vector3d, XYZProps, } from "@bentley/geometry-core"; import { - BackgroundMapSettings, BaseLayerSettings, Cartographic, ColorDef, GeoCoordStatus, GlobeMode, MapLayerSettings, PlanarClipMaskPriority, TerrainHeightOriginMode, + BackgroundMapSettings, BaseLayerSettings, Cartographic, ColorDef, FeatureAppearance, GeoCoordStatus, GlobeMode, MapLayerSettings, PlanarClipMaskPriority, TerrainHeightOriginMode, TerrainProviderName, } from "@bentley/imodeljs-common"; import { ApproximateTerrainHeights } from "../../ApproximateTerrainHeights"; @@ -122,7 +122,7 @@ export class MapTileTree extends RealityTileTree { private _layerSettings = new Map(); public addImageryLayer(tree: ImageryMapTileTree, settings: MapLayerSettings) { - const maxLayers = IModelApp.renderSystem.maxTerrainImageryLayers; + const maxLayers = IModelApp.renderSystem.maxRealityImageryLayers; if (this.imageryTrees.length < maxLayers) { this.imageryTrees.push(tree); this._layerSettings.set(tree.modelId, settings); @@ -376,6 +376,7 @@ interface MapTreeId { baseColor: ColorDef; baseTransparent: boolean; mapTransparent: boolean; + maskModelIds?: string; } /** @internal */ @@ -407,38 +408,41 @@ class MapTreeSupplier implements TileTreeSupplier { public compareTileTreeIds(lhs: MapTreeId, rhs: MapTreeId): number { let cmp = compareNumbers(lhs.uniqueId, rhs.uniqueId); if (0 === cmp) { - cmp = compareBooleans(lhs.isOverlay, rhs.isOverlay); - if (0 === cmp && !lhs.isOverlay) { - cmp = compareBooleans(lhs.wantSkirts, rhs.wantSkirts); - if (0 === cmp) { - cmp = compareBooleans(lhs.wantNormals, rhs.wantNormals); + cmp = compareStringsOrUndefined(lhs.maskModelIds, rhs.maskModelIds); + if (0 === cmp) { + cmp = compareBooleans(lhs.isOverlay, rhs.isOverlay); + if (0 === cmp && !lhs.isOverlay) { + cmp = compareBooleans(lhs.wantSkirts, rhs.wantSkirts); if (0 === cmp) { - cmp = compareNumbers(lhs.globeMode, rhs.globeMode); + cmp = compareBooleans(lhs.wantNormals, rhs.wantNormals); if (0 === cmp) { - cmp = compareNumbers(lhs.baseColor ? lhs.baseColor.tbgr : -1, rhs.baseColor ? rhs.baseColor.tbgr : -1); + cmp = compareNumbers(lhs.globeMode, rhs.globeMode); if (0 === cmp) { - cmp = compareBooleans(lhs.baseTransparent, rhs.baseTransparent); + cmp = compareNumbers(lhs.baseColor ? lhs.baseColor.tbgr : -1, rhs.baseColor ? rhs.baseColor.tbgr : -1); if (0 === cmp) { - cmp = compareBooleans(lhs.mapTransparent, rhs.mapTransparent); + cmp = compareBooleans(lhs.baseTransparent, rhs.baseTransparent); if (0 === cmp) { - cmp = compareBooleans(lhs.applyTerrain, rhs.applyTerrain); + cmp = compareBooleans(lhs.mapTransparent, rhs.mapTransparent); if (0 === cmp) { - if (lhs.applyTerrain) { + cmp = compareBooleans(lhs.applyTerrain, rhs.applyTerrain); + if (0 === cmp) { + if (lhs.applyTerrain) { // Terrain-only settings. - cmp = compareStrings(lhs.terrainProviderName, rhs.terrainProviderName); - if (0 === cmp) { - cmp = compareNumbers(lhs.terrainHeightOrigin, rhs.terrainHeightOrigin); + cmp = compareStrings(lhs.terrainProviderName, rhs.terrainProviderName); if (0 === cmp) { - cmp = compareNumbers(lhs.terrainHeightOriginMode, rhs.terrainHeightOriginMode); - if (0 === cmp) - cmp = compareNumbers(lhs.terrainExaggeration, rhs.terrainExaggeration); + cmp = compareNumbers(lhs.terrainHeightOrigin, rhs.terrainHeightOrigin); + if (0 === cmp) { + cmp = compareNumbers(lhs.terrainHeightOriginMode, rhs.terrainHeightOriginMode); + if (0 === cmp) + cmp = compareNumbers(lhs.terrainExaggeration, rhs.terrainExaggeration); + } } - } - } else { + } else { // Non-Terrain (flat) settings. - cmp = compareNumbers(lhs.mapGroundBias, rhs.mapGroundBias); - if (0 === cmp) - cmp = compareBooleans(lhs.useDepthBuffer, rhs.useDepthBuffer); + cmp = compareNumbers(lhs.mapGroundBias, rhs.mapGroundBias); + if (0 === cmp) + cmp = compareBooleans(lhs.useDepthBuffer, rhs.useDepthBuffer); + } } } } @@ -493,6 +497,8 @@ class MapTreeSupplier implements TileTreeSupplier { assert(false, "Invalid Terrain Provider"); return undefined; } + if (id.maskModelIds) + await iModel.models.load(CompressedId64Set.decompressSet(id.maskModelIds)); const treeProps = new MapTileTreeProps(modelId, loader, iModel); return new MapTileTree(treeProps, ecefToDb, bimElevationBias, geodeticOffset, gcsConverterAvailable, terrainProvider.tilingScheme, id); @@ -516,7 +522,7 @@ export class MapTileTreeReference extends TileTreeReference { private _baseColor?: ColorDef; private readonly _imageryTrees: ImageryMapLayerTreeReference[] = new Array(); private _baseTransparent = false; - private _symbologyOverrides = new FeatureSymbology.Overrides(); /** Empty overrides so that maps ignore the view overrides (isolate etc.) */ + private _symbologyOverrides: FeatureSymbology.Overrides | undefined; private _planarClipMask?: PlanarClipMaskState; public constructor(settings: BackgroundMapSettings, private _baseLayerSettings: BaseLayerSettings | undefined, private _layerSettings: MapLayerSettings[], iModel: IModelConnection, public isOverlay: boolean, private _isDrape: boolean, private _overrideTerrainDisplay?: CheckTerrainDisplayOverride) { @@ -636,6 +642,7 @@ export class MapTileTreeReference extends TileTreeReference { baseColor: this._baseColor, baseTransparent: this._baseTransparent, mapTransparent: this.settings.transparency > 0, + maskModelIds: this._planarClipMask?.settings.modelIds, }; if (undefined !== this._overrideTerrainDisplay) { @@ -696,6 +703,15 @@ export class MapTileTreeReference extends TileTreeReference { if (this._planarClipMask && this._planarClipMask.settings.isValid) context.addPlanarClassifier(tree.modelId, undefined, this._planarClipMask); + const nonLocatable = this.settings.locatable ? undefined : true; + const transparency = this.settings.transparency ? this.settings.transparency : undefined; + if (nonLocatable || transparency) { + this._symbologyOverrides = new FeatureSymbology.Overrides(); + this._symbologyOverrides.overrideModel(tree.modelId, FeatureAppearance.fromJSON({ transparency, nonLocatable })); + } else { + this._symbologyOverrides = undefined; + } + const args = this.createDrawArgs(context); if (undefined !== args) tree.draw(args); diff --git a/core/frontend/src/tools/MeasureTool.ts b/core/frontend/src/tools/MeasureTool.ts index c42649d6792..4e5d7adf36b 100644 --- a/core/frontend/src/tools/MeasureTool.ts +++ b/core/frontend/src/tools/MeasureTool.ts @@ -116,16 +116,53 @@ class MeasureMarker extends Marker { } } +/** @internal */ +interface Location { point: Point3d, adjustedPoint: Point3d, refAxes: Matrix3d } +/** @internal */ +interface Segment { distance: number, slope: number, start: Point3d, end: Point3d, delta: Vector3d, adjustedStart: Point3d, adjustedEnd: Point3d, adjustedDelta: Vector3d, refAxes: Matrix3d, marker: MeasureMarker } + +/** @internal */ +function adjustPoint(ev: BeButtonEvent, segments?: Array, locations?: Array): Point3d { + // If the point was from a hit we must transform it by the model display tyransform of what got hit. + if (undefined === ev.viewport || undefined === ev.viewport.view.modelDisplayTransformProvider) + return ev.point; + if (undefined !== IModelApp.accuSnap.currHit && undefined !== IModelApp.accuSnap.currHit.modelId) { + if ("0" !== IModelApp.accuSnap.currHit.modelId) { + const newPoint = ev.point.clone(); + ev.viewport.view.transformPointByModelDisplayTransform(IModelApp.accuSnap.currHit.modelId, newPoint, true); + return newPoint; + } else { + // Must have snapped to a decoration, so look through previous any segments & locations for a match to get an adjusted point. + if (undefined !== segments) { + for (const seg of segments) { + if (seg.start.isExactEqual(ev.point)) + return seg.adjustedStart.clone(); + if (seg.end.isExactEqual(ev.point)) + return seg.adjustedEnd.clone(); + } + } + if (undefined !== locations) { + for (const loc of locations) { + if (loc.point.isExactEqual(ev.point)) + return loc.adjustedPoint.clone(); + } + } + } + } + return ev.point; +} + /** @alpha */ export class MeasureDistanceTool extends PrimitiveTool { public static toolId = "Measure.Distance"; public static iconSpec = "icon-measure-distance"; - protected readonly _locationData = new Array<{ point: Point3d, refAxes: Matrix3d }>(); - protected readonly _acceptedSegments = new Array<{ distance: number, slope: number, start: Point3d, end: Point3d, delta: Vector3d, refAxes: Matrix3d, marker: MeasureMarker }>(); + protected readonly _locationData = new Array(); + protected readonly _acceptedSegments = new Array(); protected _totalDistance: number = 0.0; protected _totalDistanceMarker?: MeasureLabel; protected _snapGeomId?: string; protected _lastMotionPt?: Point3d; + protected _lastMotionAdjustedPt?: Point3d; protected allowView(vp: Viewport) { return vp.view.isSpatialView() || vp.view.isDrawingView(); } public isCompatibleViewport(vp: Viewport | undefined, isSelectedViewChange: boolean): boolean { return (super.isCompatibleViewport(vp, isSelectedViewChange) && undefined !== vp && this.allowView(vp)); } @@ -200,10 +237,10 @@ export class MeasureDistanceTool extends PrimitiveTool { return (undefined === geomData ? undefined : [geomData]); } - protected displayDynamicDistance(context: DecorateContext, points: Point3d[]): void { + protected displayDynamicDistance(context: DecorateContext, points: Point3d[], adjustedPoints: Point3d[]): void { let totalDistance = 0.0; - for (let i = 0; i < points.length - 1; i++) - totalDistance += points[i].distance(points[i + 1]); + for (let i = 0; i < adjustedPoints.length - 1; i++) + totalDistance += adjustedPoints[i].distance(adjustedPoints[i + 1]); if (0.0 === totalDistance) return; @@ -265,11 +302,15 @@ export class MeasureDistanceTool extends PrimitiveTool { if (!this.isCompatibleViewport(context.viewport, false)) return; - if (!isSuspended && this._locationData.length > 0 && undefined !== this._lastMotionPt) { + if (!isSuspended && this._locationData.length > 0 && undefined !== this._lastMotionPt && undefined !== this._lastMotionAdjustedPt) { const tmpPoints: Point3d[] = []; - for (const loc of this._locationData) + const tmpAdjustedPoints: Point3d[] = []; + for (const loc of this._locationData) { tmpPoints.push(loc.point); // Deep copy not necessary... + tmpAdjustedPoints.push(loc.adjustedPoint); + } tmpPoints.push(this._lastMotionPt); + tmpAdjustedPoints.push(this._lastMotionAdjustedPt); const builderDynVis = context.createGraphicBuilder(GraphicType.WorldDecoration); const colorDynVis = context.viewport.hilite.color; @@ -286,7 +327,7 @@ export class MeasureDistanceTool extends PrimitiveTool { builderDynHid.addLineString(tmpPoints); context.addDecorationFromBuilder(builderDynHid); - this.displayDynamicDistance(context, tmpPoints); + this.displayDynamicDistance(context, tmpPoints, tmpAdjustedPoints); } if (this._acceptedSegments.length > 0) { @@ -334,10 +375,15 @@ export class MeasureDistanceTool extends PrimitiveTool { public async onMouseMotion(ev: BeButtonEvent): Promise { if (this._locationData.length > 0 && undefined !== ev.viewport) { - if (undefined !== this._lastMotionPt) - this._lastMotionPt.setFrom(ev.point); - else - this._lastMotionPt = ev.point.clone(); + const point = ev.point; + const adjustedPoint = adjustPoint(ev, this._acceptedSegments, this._locationData); + if (undefined !== this._lastMotionPt) { + this._lastMotionPt.setFrom(point); + this._lastMotionAdjustedPt?.setFrom(adjustedPoint); + } else { + this._lastMotionPt = point.clone(); + this._lastMotionAdjustedPt = adjustedPoint; + } ev.viewport.invalidateDecorations(); } } @@ -431,7 +477,7 @@ export class MeasureDistanceTool extends PrimitiveTool { } protected async updateSelectedMarkerToolTip(seg: any, ev: BeButtonEvent, reopenToolTip: boolean): Promise { - seg.marker.title = await this.getMarkerToolTip(seg.distance, seg.slope, seg.start, seg.end, seg.marker.isSelected ? seg.delta : undefined); + seg.marker.title = await this.getMarkerToolTip(seg.distance, seg.slope, seg.adjustedStart, seg.adjustedEnd, seg.marker.isSelected ? seg.adjustedDelta : undefined); if (!reopenToolTip || undefined === ev.viewport || !IModelApp.notifications.isToolTipOpen) return; IModelApp.notifications.clearToolTip(); @@ -441,17 +487,21 @@ export class MeasureDistanceTool extends PrimitiveTool { protected async acceptNewSegments(): Promise { if (this._locationData.length > 1) { for (let i = 0; i <= this._locationData.length - 2; i++) { + const adjustedStart = this._locationData[i].adjustedPoint; + const adjustedEnd = this._locationData[i + 1].adjustedPoint; + const distance = adjustedStart.distance(adjustedEnd); + const xyDist = adjustedStart.distanceXY(adjustedEnd); + const zDist = adjustedEnd.z - adjustedStart.z; + const slope = (0.0 === xyDist ? Math.PI : Math.atan(zDist / xyDist)); + const adjustedDelta = Vector3d.createStartEnd(adjustedStart, adjustedEnd); + const refAxes = this._locationData[i].refAxes; + refAxes.multiplyTransposeVectorInPlace(adjustedDelta); const start = this._locationData[i].point; const end = this._locationData[i + 1].point; - const distance = start.distance(end); - const xyDist = start.distanceXY(end); - const zDist = end.z - start.z; - const slope = (0.0 === xyDist ? Math.PI : Math.atan(zDist / xyDist)); const delta = Vector3d.createStartEnd(start, end); - const refAxes = this._locationData[i].refAxes; refAxes.multiplyTransposeVectorInPlace(delta); - const toolTip = await this.getMarkerToolTip(distance, slope, start, end); + const toolTip = await this.getMarkerToolTip(distance, slope, adjustedStart, adjustedEnd); const marker = new MeasureMarker((this._acceptedSegments.length + 1).toString(), toolTip, start.interpolate(0.5, end), Point2d.create(25, 25)); const segMarkerButtonFunc = (ev: BeButtonEvent) => { @@ -481,7 +531,7 @@ export class MeasureDistanceTool extends PrimitiveTool { }; marker.onMouseButton = segMarkerButtonFunc; // eslint-disable-line @typescript-eslint/unbound-method - this._acceptedSegments.push({ distance, slope, start, end, delta, refAxes, marker }); + this._acceptedSegments.push({ distance, slope, start, end, delta, adjustedStart, adjustedEnd, adjustedDelta, refAxes, marker }); } } this._locationData.length = 0; @@ -497,6 +547,7 @@ export class MeasureDistanceTool extends PrimitiveTool { public async onDataButtonDown(ev: BeButtonEvent): Promise { const point = ev.point.clone(); + const adjustedPoint = adjustPoint(ev, this._acceptedSegments, this._locationData); const refAxes = this.getReferenceAxes(ev.viewport); const zDir = refAxes.columnZ(); const normal = refAxes.columnZ(); @@ -532,7 +583,7 @@ export class MeasureDistanceTool extends PrimitiveTool { } } - this._locationData.push({ point, refAxes }); + this._locationData.push({ point, adjustedPoint, refAxes }); if (this._locationData.length > 1 && !ev.isControlKey) await this.acceptNewSegments(); @@ -677,7 +728,8 @@ export class MeasureLocationTool extends PrimitiveTool { public async onDataButtonDown(ev: BeButtonEvent): Promise { const point = ev.point.clone(); - const toolTip = await this.getMarkerToolTip(point); + const adjustedPoint = adjustPoint(ev); + const toolTip = await this.getMarkerToolTip(adjustedPoint); const marker = new MeasureMarker((this._acceptedLocations.length + 1).toString(), toolTip, point, Point2d.create(25, 25)); this._acceptedLocations.push(marker); diff --git a/core/frontend/src/tools/SelectTool.ts b/core/frontend/src/tools/SelectTool.ts index 865378952a3..5cccb5d2183 100644 --- a/core/frontend/src/tools/SelectTool.ts +++ b/core/frontend/src/tools/SelectTool.ts @@ -190,7 +190,8 @@ export class SelectionTool extends PrimitiveTool { sections.push(ToolAssistance.createSection(mousePickInstructions, ToolAssistance.inputsLabel)); const touchPickInstructions: ToolAssistanceInstruction[] = []; - touchPickInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.OneTouchTap, CoreTools.translate("ElementSet.Inputs.AcceptElement"), false, ToolAssistanceInputMethod.Touch)); + if (!ToolAssistance.createTouchCursorInstructions(touchPickInstructions)) + touchPickInstructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.OneTouchTap, CoreTools.translate("ElementSet.Inputs.AcceptElement"), false, ToolAssistanceInputMethod.Touch)); sections.push(ToolAssistance.createSection(touchPickInstructions, ToolAssistance.inputsLabel)); break; case SelectionMethod.Line: diff --git a/core/frontend/src/tools/ToolAdmin.ts b/core/frontend/src/tools/ToolAdmin.ts index 6e6a2c411cd..924b2da3ea2 100644 --- a/core/frontend/src/tools/ToolAdmin.ts +++ b/core/frontend/src/tools/ToolAdmin.ts @@ -1573,7 +1573,15 @@ export class ToolAdmin { viewport.drawLocateCursor(context, ev.viewPoint, viewport.pixelsFromInches(IModelApp.locateManager.apertureInches), this.isLocateCircleOn, hit); } - public get isLocateCircleOn(): boolean { return this.toolState.locateCircleOn && this.currentInputState.inputSource === InputSource.Mouse && this._canvasDecoration === undefined; } + public get isLocateCircleOn(): boolean { + if (!this.toolState.locateCircleOn || undefined !== this._canvasDecoration) + return false; + + if (InputSource.Mouse === this.currentInputState.inputSource) + return true; + + return (InputSource.Touch === this.currentInputState.inputSource && undefined !== IModelApp.accuSnap.touchCursor); + } /** @internal */ public beginDynamics(): void { diff --git a/core/frontend/src/tools/ToolAssistance.ts b/core/frontend/src/tools/ToolAssistance.ts index acaab32a77a..c8516476833 100644 --- a/core/frontend/src/tools/ToolAssistance.ts +++ b/core/frontend/src/tools/ToolAssistance.ts @@ -262,7 +262,7 @@ export class ToolAssistance { */ public static createTouchCursorInstructions(instructions: ToolAssistanceInstruction[]): boolean { const accuSnap = IModelApp.accuSnap; - if (accuSnap.isSnapEnabled && accuSnap.isSnapEnabledByUser && undefined === accuSnap.touchCursor) { + if (undefined === accuSnap.touchCursor && accuSnap.wantVirtualCursor) { instructions.push(ToolAssistance.createInstruction(ToolAssistanceImage.OneTouchTap, this.translateTouch("Activate"), false, ToolAssistanceInputMethod.Touch)); return true; } else if (undefined !== accuSnap.touchCursor) { diff --git a/core/frontend/src/tools/ToolSettings.ts b/core/frontend/src/tools/ToolSettings.ts index 8fd42bedbf3..8aa6512c258 100644 --- a/core/frontend/src/tools/ToolSettings.ts +++ b/core/frontend/src/tools/ToolSettings.ts @@ -19,6 +19,8 @@ export class ToolSettings { public static doubleClickTimeout = BeDuration.fromMilliseconds(500); /** Number of screen inches of movement allowed between clicks to still qualify as a double-click. */ public static doubleClickToleranceInches = 0.05; + /** @beta Use virtual cursor to help with locating elements using touch input. By default it's only enabled for snapping. */ + public static enableVirtualCursorForLocate = false; /** If true, view rotation tool keeps the up vector (worldZ) aligned with screenY. */ public static preserveWorldUp = true; /** Delay with a touch on the surface before a move operation begins. */ diff --git a/core/frontend/src/tools/ViewTool.ts b/core/frontend/src/tools/ViewTool.ts index b9483dc5511..e39ca848d4f 100644 --- a/core/frontend/src/tools/ViewTool.ts +++ b/core/frontend/src/tools/ViewTool.ts @@ -1245,8 +1245,16 @@ class ViewRotate extends HandleWithInertia { plane.getNormalRef().setFrom(vp.view.getZVector()); return true; } - if (super.adjustDepthPoint(isValid, vp, plane, source)) + if (super.adjustDepthPoint(isValid, vp, plane, source)) { + if (DepthPointSource.Geometry === source && vp instanceof ScreenViewport) { + // If we had hit something we might need to undo the model display transform of the hit. + const hitDetail = vp.picker.getHit(0); + if (undefined !== hitDetail && undefined !== hitDetail.modelId) { + vp.view.transformPointByModelDisplayTransform(hitDetail.modelId, plane.getOriginRef(), false); + } + } return true; + } plane.getOriginRef().setFrom(this.viewTool.targetCenterWorld); return false; } diff --git a/core/geometry/package.json b/core/geometry/package.json index 354d0e417de..98c9c5c86bf 100644 --- a/core/geometry/package.json +++ b/core/geometry/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/geometry-core", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "Bentley Core Geometry library", "main": "lib/geometry-core.js", "typings": "lib/geometry-core", @@ -32,12 +32,12 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13" }, "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/flatbuffers": "~1.10.0", "@types/mocha": "^5.2.5", diff --git a/core/geometry/src/clipping/ConvexClipPlaneSet.ts b/core/geometry/src/clipping/ConvexClipPlaneSet.ts index a0fd6c5acd6..09b8526b5f1 100644 --- a/core/geometry/src/clipping/ConvexClipPlaneSet.ts +++ b/core/geometry/src/clipping/ConvexClipPlaneSet.ts @@ -172,7 +172,7 @@ export class ConvexClipPlaneSet implements Clipper, PolygonClipper { perp.z = 0.0; if (!leftIsInside) - perp.negate(); + perp.scaleInPlace(-1.0); const perpNormalized = perp.normalize(); if (perpNormalized) { diff --git a/core/geometry/src/geometry4d/ViewGraphicsOps.ts b/core/geometry/src/geometry4d/ViewGraphicsOps.ts index 8ad8131891c..a2b7c8e99d0 100644 --- a/core/geometry/src/geometry4d/ViewGraphicsOps.ts +++ b/core/geometry/src/geometry4d/ViewGraphicsOps.ts @@ -16,6 +16,7 @@ import { PolygonOps } from "../geometry3d/PolygonOps"; import { Range1d, Range3d } from "../geometry3d/Range"; import { Segment1d } from "../geometry3d/Segment1d"; import { Transform } from "../geometry3d/Transform"; +import { Order2Bezier } from "../numerics/BezierPolynomials"; import { Map4d } from "./Map4d"; import { Matrix4d } from "./Matrix4d"; /** @@ -57,7 +58,12 @@ export class ViewportGraphicsGridSpacingOptions { * * 0 ==> output entire line (entire line is clipped by view frustum but not limited by neighbors) * * 1 ==> clip line where it is close predecessor line */ - public clippingOption: 0 | 1; + public clipIfCloseToNeighborLine: 0 | 1; + /** + * clipping of each line to the view frustum. + */ + public clipToViewFrustum: boolean; + /** * distance-between-lines criteria for use when filtering lines. * * Units depend on choice of map (view or npc) @@ -68,11 +74,12 @@ export class ViewportGraphicsGridSpacingOptions { */ public gridMultiple: number; - private constructor(distanceBetweenLines: number, cullingOption: 0 | 1 | 2, clippingOption: 0 | 1, gridMultiple: number) { + private constructor(distanceBetweenLines: number, cullingOption: 0 | 1 | 2, clippingOption: 0 | 1, gridMultiple: number, clipToViewFrustum: boolean) { this.distanceBetweenLines = distanceBetweenLines; this.cullingOption = cullingOption; - this.clippingOption = clippingOption; + this.clipIfCloseToNeighborLine = clippingOption; this.gridMultiple = gridMultiple; + this.clipToViewFrustum = clipToViewFrustum; } /** * Create a ViewportGraphicsSpacingOptions instance @@ -81,13 +88,13 @@ export class ViewportGraphicsGridSpacingOptions { * @param clippingOption See ViewportGraphicsGridSpacingOptions * @param gridMultiple 1 for all grid lines, 10 for every 10th line etc */ - public static create(distanceBetweenLines: number, cullingOption: 0 | 1 | 2 = 2, clippingOption: 0 | 1 = 1, gridMultiple = 1) { + public static create(distanceBetweenLines: number, cullingOption: 0 | 1 | 2 = 2, clippingOption: 0 | 1 = 1, gridMultiple = 1, clipToViewFrustum: boolean = true) { return new ViewportGraphicsGridSpacingOptions(distanceBetweenLines, cullingOption, clippingOption, - Math.max (1, Math.floor (gridMultiple))); + Math.max (1, Math.floor (gridMultiple)), clipToViewFrustum); } /** Return a member-by-member clone */ public clone(): ViewportGraphicsGridSpacingOptions { - return new ViewportGraphicsGridSpacingOptions(this.distanceBetweenLines, this.cullingOption, this.clippingOption, this.gridMultiple); + return new ViewportGraphicsGridSpacingOptions(this.distanceBetweenLines, this.cullingOption, this.clipIfCloseToNeighborLine, this.gridMultiple, this.clipToViewFrustum); } } @@ -171,7 +178,17 @@ class LineProximityContext { return true; } - private static _horizonTrimFraction = 0.90; + private fractionAtOffsetFromLineA(offsetDistance: number, point0B: Point3d, point1B: Point3d, defaultFraction: number): number { + const npc0B = this.worldToNPC.multiplyPoint3d(point0B, 1.0); + const npc1B = this.worldToNPC.multiplyPoint3d(point1B, 1.0); + const c = offsetDistance - this.npc0A.x * this.uy - this.npc0A.y * this.ux; + const b0 = Geometry.crossProductXYXY(npc0B.x, npc0B.y, this.ux, this.uy) - c * npc0B.w; + const b1 = Geometry.crossProductXYXY(npc1B.x, npc1B.y, this.ux, this.uy) - c * npc0B.w; + const fraction = Order2Bezier.solveCoffs(b0, b1); + if (fraction !== undefined) + return fraction; + return defaultFraction; + } /** * * return the fractional interval on line B, such that points in the interval are at a distance minimumDistance or larger * * If line B jumps complete between "far negative" and "far positive", only the first fractional part is returned. @@ -190,6 +207,7 @@ class LineProximityContext { ): boolean { if (this.divMagU === undefined) return false; + const minimumDistance = options.distanceBetweenLines; this.worldToNPC.multiplyPoint3dQuietNormalize(point0B, this.npc0B); this.worldToNPC.multiplyPoint3dQuietNormalize(point1B, this.npc1B); @@ -200,19 +218,27 @@ class LineProximityContext { if (d1 < -minimumDistance) { return this.setRanges(fractions, 0, 1, perspectiveZStartEnd, this.npc0B.z, this.npc1B.z); } else { - return this.setRanges(fractions, 0, Geometry.safeDivideFraction(-minimumDistance - d0, d1 - d0, 0.0), perspectiveZStartEnd, this.npc0B.z, this.npc1B.z); + return this.setRanges(fractions, + 0, this.fractionAtOffsetFromLineA(-minimumDistance, point0B, point1B, 1.0), + perspectiveZStartEnd, this.npc0B.z, this.npc1B.z); } } else if (d0 > minimumDistance) { if (d1 > minimumDistance) { return this.setRanges(fractions, 0, 1, perspectiveZStartEnd, this.npc0B.z, this.npc1B.z); } else { - return this.setRanges(fractions, 0.0, Geometry.safeDivideFraction(minimumDistance - d0, d1 - d0, 0.0), perspectiveZStartEnd, this.npc0B.z, this.npc1B.z); + return this.setRanges(fractions, + 0.0, this.fractionAtOffsetFromLineA(minimumDistance, point0B, point1B, 1.0), + perspectiveZStartEnd, this.npc0B.z, this.npc1B.z); } } else { // d0 starts inside -- may move outside if (d1 > minimumDistance) { - return this.setRanges(fractions, Geometry.safeDivideFraction(minimumDistance - d0, d1 - d0, 0.0), 1.0, perspectiveZStartEnd, this.npc0B.z, this.npc1B.z); + return this.setRanges(fractions, + this.fractionAtOffsetFromLineA(minimumDistance, point0B, point1B, 0.0), 1.0, + perspectiveZStartEnd, this.npc0B.z, this.npc1B.z); } else if (d1 < -minimumDistance) { - return this.setRanges(fractions, Geometry.safeDivideFraction(-minimumDistance - d0, d1 - d0, 0.0), 1.0, perspectiveZStartEnd, this.npc0B.z, this.npc1B.z); + return this.setRanges(fractions, + this.fractionAtOffsetFromLineA(-minimumDistance, point0B, point1B, 0.0), 1.0, + perspectiveZStartEnd, this.npc0B.z, this.npc1B.z); } return false; } @@ -257,6 +283,7 @@ export class ViewGraphicsOps { * @internal */ export class GridInViewContext { + private static _announceAllCandidates = false; private static getRestrictedGridRange(npcLoop: GrowableXYZArray, stLoop: GrowableXYZArray, viewRange: Range3d, _maxLinesInDirection: number): Range3d { const stRange1 = stLoop.getRange(); // this might be unreasonably large @@ -475,7 +502,7 @@ export class GridInViewContext { if (options.cullingOption === 1) lineContext.moveLineBToLineA(); } else { - if (options.clippingOption === 0 || fractionRange.isExact01) + if (options.clipIfCloseToNeighborLine === 0 || fractionRange.isExact01) announceLine(clippedPointWorld0, clippedPointWorld1, perspectiveZStartEnd.x0, perspectiveZStartEnd.x1, startEndDistance, @@ -506,7 +533,10 @@ export class GridInViewContext { gridLineIdentifier.index = iy; gridPoint0.set(xLow, iy); gridPoint1.set(xHigh, iy); - this._gridSpaceClipper.announceClippedSegmentIntervals(0.0, 1.0, gridPoint0, gridPoint1, announceInterval); + if (!options.clipToViewFrustum) + announceInterval(0, 1); + else + this._gridSpaceClipper.announceClippedSegmentIntervals(0.0, 1.0, gridPoint0, gridPoint1, announceInterval); } // sweep left to right @@ -522,7 +552,10 @@ export class GridInViewContext { gridPoint0.set(ix, yLow); gridPoint1.set(ix, yHigh); gridLineIdentifier.index = ix; - this._gridSpaceClipper.announceClippedSegmentIntervals(0.0, 1.0, gridPoint0, gridPoint1, announceInterval); + if (!options.clipToViewFrustum) + announceInterval(0, 1); + else + this._gridSpaceClipper.announceClippedSegmentIntervals(0.0, 1.0, gridPoint0, gridPoint1, announceInterval); } return numAnnounced > 0; diff --git a/core/hypermodeling/package.json b/core/hypermodeling/package.json index 0131d460310..9b06b923f33 100644 --- a/core/hypermodeling/package.json +++ b/core/hypermodeling/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/hypermodeling-frontend", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js hypermodeling package", "main": "lib/hypermodeling-frontend.js", "typings": "lib/hypermodeling-frontend", @@ -35,23 +35,23 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/geometry-core": "^2.15.0-dev.6", - "@bentley/imodeljs-common": "^2.15.0-dev.6", - "@bentley/imodeljs-frontend": "^2.15.0-dev.6", - "@bentley/imodeljs-i18n": "^2.15.0-dev.6", - "@bentley/ui-abstract": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/geometry-core": "^2.15.0-dev.13", + "@bentley/imodeljs-common": "^2.15.0-dev.13", + "@bentley/imodeljs-frontend": "^2.15.0-dev.13", + "@bentley/imodeljs-i18n": "^2.15.0-dev.13", + "@bentley/ui-abstract": "^2.15.0-dev.13" }, "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/certa": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", - "@bentley/ui-abstract": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/certa": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", + "@bentley/ui-abstract": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", "@types/node": "10.14.1", diff --git a/core/i18n/package.json b/core/i18n/package.json index ad993832d59..fb096519978 100644 --- a/core/i18n/package.json +++ b/core/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/imodeljs-i18n", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js localization code", "main": "lib/imodeljs-i18n", "typings": "lib/imodeljs-i18n", @@ -30,16 +30,16 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13" }, "//devDependencies": [ "NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install", "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/i18next": "^8.4.2", "@types/i18next-browser-languagedetector": "^2.0.1", "@types/node": "10.14.1", diff --git a/core/imodel-bridge/package.json b/core/imodel-bridge/package.json index b0df5e68887..de42f20742e 100644 --- a/core/imodel-bridge/package.json +++ b/core/imodel-bridge/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/imodel-bridge", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js bridge components", "main": "lib/imodel-bridge.js", "typings": "lib/imodel-bridge", @@ -40,31 +40,31 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/backend-itwin-client": "^2.15.0-dev.6", - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/context-registry-client": "^2.15.0-dev.6", - "@bentley/geometry-core": "^2.15.0-dev.6", - "@bentley/imodelhub-client": "^2.15.0-dev.6", - "@bentley/imodeljs-backend": "^2.15.0-dev.6", - "@bentley/imodeljs-common": "^2.15.0-dev.6", - "@bentley/rbac-client": "^2.15.0-dev.6" + "@bentley/backend-itwin-client": "^2.15.0-dev.13", + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/context-registry-client": "^2.15.0-dev.13", + "@bentley/geometry-core": "^2.15.0-dev.13", + "@bentley/imodelhub-client": "^2.15.0-dev.13", + "@bentley/imodeljs-backend": "^2.15.0-dev.13", + "@bentley/imodeljs-common": "^2.15.0-dev.13", + "@bentley/rbac-client": "^2.15.0-dev.13" }, "//devDependencies": [ "NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install", "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/backend-itwin-client": "2.15.0-dev.6", - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/oidc-signin-tool": "2.15.0-dev.6", - "@bentley/rbac-client": "2.15.0-dev.6", + "@bentley/backend-itwin-client": "2.15.0-dev.13", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/oidc-signin-tool": "2.15.0-dev.13", + "@bentley/rbac-client": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", "@types/node": "10.14.1", diff --git a/core/logger-config/package.json b/core/logger-config/package.json index 521ad6e7193..87793ad75dc 100644 --- a/core/logger-config/package.json +++ b/core/logger-config/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/logger-config", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "Logger configuration", "main": "lib/logger-config.js", "typings": "lib/logger-config", @@ -30,16 +30,16 @@ "iModel" ], "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", + "@bentley/bentleyjs-core": "^2.15.0-dev.13", "bunyan": "^1.8.12", "bunyan-seq": "^0.2.0", "request": "^2.88.0", "request-promise": "^4.2.0" }, "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/bunyan": "^1.8.4", "@types/bunyan-seq": "^0.2.0", "@types/node": "10.14.1", diff --git a/core/markup/package.json b/core/markup/package.json index b1545612cc6..e35ff4345d8 100644 --- a/core/markup/package.json +++ b/core/markup/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/imodeljs-markup", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js markup package", "main": "lib/imodeljs-markup.js", "imodeljsSharedLibrary": true, @@ -39,21 +39,21 @@ "@svgdotjs/svg.js": "3.0.13" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/geometry-core": "^2.15.0-dev.6", - "@bentley/imodeljs-common": "^2.15.0-dev.6", - "@bentley/imodeljs-frontend": "^2.15.0-dev.6", - "@bentley/imodeljs-i18n": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/geometry-core": "^2.15.0-dev.13", + "@bentley/imodeljs-common": "^2.15.0-dev.13", + "@bentley/imodeljs-frontend": "^2.15.0-dev.13", + "@bentley/imodeljs-i18n": "^2.15.0-dev.13" }, "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/certa": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/certa": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", "@types/node": "10.14.1", diff --git a/core/mobile-manager/package.json b/core/mobile-manager/package.json index 2dff8a28fb7..f0a08b67a25 100644 --- a/core/mobile-manager/package.json +++ b/core/mobile-manager/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/mobile-manager", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iTwin.js MobileHost and MobileApp", "license": "MIT", "engines": { @@ -31,26 +31,28 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/frontend-authorization-client": "^2.15.0-dev.6", - "@bentley/imodeljs-common": "^2.15.0-dev.6", - "@bentley/imodeljs-frontend": "^2.15.0-dev.6", - "@bentley/imodeljs-backend": "^2.15.0-dev.6", - "@bentley/itwin-client": "^2.15.0-dev.6", - "@bentley/imodelhub-client": "^2.15.0-dev.6", + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/frontend-authorization-client": "^2.15.0-dev.13", + "@bentley/imodeljs-common": "^2.15.0-dev.13", + "@bentley/imodeljs-frontend": "^2.15.0-dev.13", + "@bentley/imodeljs-backend": "^2.15.0-dev.13", + "@bentley/itwin-client": "^2.15.0-dev.13", + "@bentley/imodelhub-client": "^2.15.0-dev.13", + "@bentley/presentation-common": "^2.15.0-dev.13", "ws": "^7.2.0", "js-base64": "^2.4.5" }, "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/frontend-authorization-client": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/imodelhub-client": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/frontend-authorization-client": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/imodelhub-client": "2.15.0-dev.13", + "@bentley/presentation-common": "2.15.0-dev.13", "@types/node": "10.14.1", "@types/mocha": "^5.2.5", "@types/chai": "^4.1.4", diff --git a/core/mobile-manager/src/backend/MobileHost.ts b/core/mobile-manager/src/backend/MobileHost.ts index 777d20cb14a..989cd440faf 100644 --- a/core/mobile-manager/src/backend/MobileHost.ts +++ b/core/mobile-manager/src/backend/MobileHost.ts @@ -5,9 +5,11 @@ import { BeEvent, BriefcaseStatus, ClientRequestContext, Logger } from "@bentley/bentleyjs-core"; import { IModelHost, IpcHandler, IpcHost, NativeHost, NativeHostOpts } from "@bentley/imodeljs-backend"; -import { NativeAppAuthorizationConfiguration } from "@bentley/imodeljs-common"; +import { IModelReadRpcInterface, IModelTileRpcInterface, NativeAppAuthorizationConfiguration, RpcInterfaceDefinition, SnapshotIModelRpcInterface } from "@bentley/imodeljs-common"; import { CancelRequest, DownloadFailed, ProgressCallback, UserCancelledError } from "@bentley/itwin-client"; +import { PresentationRpcInterface } from "@bentley/presentation-common"; import { BatteryState, DeviceEvents, mobileAppChannel, MobileAppFunctions, Orientation } from "../common/MobileAppProps"; +import { MobileRpcManager } from "../common/MobileRpcManager"; import { MobileAuthorizationBackend } from "../MobileBackend"; import { setupMobileRpc } from "./MobileRpcServer"; @@ -77,6 +79,9 @@ class MobileAppHandler extends IpcHandler implements MobileAppFunctions { export interface MobileHostOpts extends NativeHostOpts { mobileHost?: { device?: MobileDevice; + /** list of RPC interface definitions to register */ + rpcInterfaces?: RpcInterfaceDefinition[]; + }; } @@ -146,5 +151,14 @@ export class MobileHost { if (IpcHost.isValid) MobileAppHandler.register(); IModelHost.authorizationClient = new MobileAuthorizationBackend(); + + const rpcInterfaces = opt?.mobileHost?.rpcInterfaces ?? [ + IModelReadRpcInterface, + IModelTileRpcInterface, + SnapshotIModelRpcInterface, + PresentationRpcInterface, + ]; + + MobileRpcManager.initializeImpl(rpcInterfaces); } } diff --git a/core/mobile-manager/src/backend/iOSHost.ts b/core/mobile-manager/src/backend/iOSHost.ts index 4f7196b2457..5c563a2a977 100644 --- a/core/mobile-manager/src/backend/iOSHost.ts +++ b/core/mobile-manager/src/backend/iOSHost.ts @@ -6,16 +6,19 @@ import { IpcWebSocketBackend } from "@bentley/imodeljs-common"; import { MobileDevice, MobileHost, MobileHostOpts } from "./MobileHost"; +/** @beta */ +export type IOSHostOpts = MobileHostOpts; + /** @beta */ export class IOSHost extends MobileHost { /** * Start the backend of an IOS app. */ - public static async startup(opt?: MobileHostOpts): Promise { + public static async startup(opt?: IOSHostOpts): Promise { const device = opt?.mobileHost?.device ?? new (MobileDevice as any)(); // The abstract functions of MobileDevice are implemented at runtime in native code. (global as any).__iTwinJsNativeBridge = device; // for native side const socket = opt?.ipcHost?.socket ?? new IpcWebSocketBackend(); - return MobileHost.startup({ ...opt, mobileHost: { device }, ipcHost: { socket } }); + return MobileHost.startup({ ...opt, mobileHost: { ...opt?.mobileHost, device }, ipcHost: { ...opt?.ipcHost, socket } }); } } diff --git a/core/mobile-manager/src/frontend/IOSApp.ts b/core/mobile-manager/src/frontend/IOSApp.ts index 5161810c86a..68da9a2dc96 100644 --- a/core/mobile-manager/src/frontend/IOSApp.ts +++ b/core/mobile-manager/src/frontend/IOSApp.ts @@ -3,15 +3,18 @@ * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import { IModelAppOptions } from "@bentley/imodeljs-frontend"; +import { NativeAppOpts } from "@bentley/imodeljs-frontend"; import { MobileApp } from "./MobileApp"; +/** @beta */ +export type IOSAppOpts = NativeAppOpts; + /** @beta */ export class IOSApp { private static _isValid = false; public static get isValid() { return this._isValid; } - public static async startup(opts?: { iModelApp?: IModelAppOptions }) { + public static async startup(opts?: IOSAppOpts) { if (!this._isValid) { this._isValid = true; } diff --git a/core/orbitgt/package.json b/core/orbitgt/package.json index bdb2656935b..eb3d1dd2982 100644 --- a/core/orbitgt/package.json +++ b/core/orbitgt/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/orbitgt-core", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "", "main": "lib/imodeljs-orbitgt.js", "typings": "lib/imodeljs-orbitgt", @@ -29,9 +29,9 @@ "url": "http://www.bentley.com" }, "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", "@types/node": "10.14.1", diff --git a/core/quantity/package.json b/core/quantity/package.json index c04c72f844a..43960592ac2 100644 --- a/core/quantity/package.json +++ b/core/quantity/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/imodeljs-quantity", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "Quantity parsing, formatting and conversions for iModel.js", "main": "lib/imodeljs-quantity.js", "typings": "lib/imodeljs-quantity", @@ -31,9 +31,9 @@ "url": "http://www.bentley.com" }, "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", "@types/glob": "^5.0.35", @@ -50,7 +50,7 @@ "typescript": "~4.1.0" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13" }, "nyc": { "nycrc-path": "./node_modules/@bentley/build-tools/.nycrc" diff --git a/core/webgl-compatibility/package.json b/core/webgl-compatibility/package.json index f56f9d27250..6e66449336c 100644 --- a/core/webgl-compatibility/package.json +++ b/core/webgl-compatibility/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/webgl-compatibility", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "APIs for determining the level of compatibility of a browser+device with the iModel.js rendering system.", "license": "MIT", "main": "lib/webgl-compatibility.js", @@ -33,13 +33,13 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13" }, "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/certa": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/certa": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", "@types/node": "10.14.1", diff --git a/docs/changehistory/NextVersion.md b/docs/changehistory/NextVersion.md index dc88bf25397..0ddbd53d593 100644 --- a/docs/changehistory/NextVersion.md +++ b/docs/changehistory/NextVersion.md @@ -14,35 +14,45 @@ publish: false ## New settings UI features -### Add Settings Page to set Quantity Formatting Overrides +### Add settings tabs and pages to UI -The [QuantityFormatSettingsPanel]($ui-framework) component has been added to the @bentley/ui-framework package to provide the UI to set both the [PresentationUnitSystem]($presentation-common) and formatting overrides in the [QuantityFormatter]($frontend). This panel can be used in the new [SettingsContainer]($ui-core) UI component. The function `getQuantityFormatsSettingsManagerEntry` will return a [SettingsTabEntry]($ui-core) for use by the [SettingsManager]($ui-core). Below is an example of registering the `QuantityFormatSettingsPanel` with the `SettingsManager`. +#### Quantity formatting settings + +The [QuantityFormatSettingsPage]($ui-framework) component has been added to provide the UI to set both the [PresentationUnitSystem]($presentation-common) and formatting overrides in the [QuantityFormatter]($frontend). This component can be used in the new [SettingsContainer]($ui-core) UI component. The function `getQuantityFormatsSettingsManagerEntry` will return a [SettingsTabEntry]($ui-core) for use by the [SettingsManager]($ui-core). + +#### User Interface Settings + +The [UiSettingsPage]($ui-framework) component has been to provide the UI to set general UI settings that effect the look and feel of the App UI user interface. This component can be used in the new [SettingsContainer]($ui-core) UI component. The function `getUiSettingsManagerEntry` will return a [SettingsTabEntry]($ui-core) for use by the [SettingsManager]($ui-core). + +#### Registering settings + +Below is an example of registering the `QuantityFormatSettingsPage` with the `SettingsManager`. ```ts // Sample settings provider that dynamically adds settings into the setting stage -export class AppSettingsProvider implements SettingsProvider { - public readonly id = "AppSettingsProvider"; +export class AppSettingsTabsProvider implements SettingsTabsProvider { + public readonly id = "AppSettingsTabsProvider"; public getSettingEntries(_stageId: string, _stageUsage: string): ReadonlyArray | undefined { return [ getQuantityFormatsSettingsManagerEntry(10, {availableUnitSystems:new Set(["metric","imperial","usSurvey"])}), + getUiSettingsManagerEntry(30, true), ]; } public static initializeAppSettingProvider() { - UiFramework.settingsManager.addSettingsProvider(new AppSettingsProvider()); + UiFramework.settingsManager.addSettingsProvider(new AppSettingsTabsProvider()); } } - ``` -The `QuantityFormatSettingsPanel` is marked as alpha in this release and is subject to minor modifications in future releases. +The `QuantityFormatSettingsPage` is marked as alpha in this release and is subject to minor modifications in future releases. ## @bentley/imodeljs-quantity package The alpha classes, interfaces, and definitions in the package `@bentley/imodeljs-quantity` have been updated to beta. -## Incremental Precompilation of Shaders Enabled by Default +## Incremental precompilation of shaders enabled by default To help prevent delays when a user interacts with a [Viewport]($frontend), the WebGL render system now by default precompiles shader programs used by the [RenderSystem]($frontend) before any Viewport is opened. @@ -50,7 +60,42 @@ Shader precompilation will cease once all shader programs have been compiled, or To disable this functionality, set the `doIdleWork` property of the `RenderSystem.Options` object passed to `IModelApp.startup` to false. -## Breaking Api Changes +## Added NativeHost.settingsStore for storing user-level settings for native applications + +The @beta class `NativeHost` now has a member [NativeHost.settingsStore]($backend) that may be used by native applications to store user-level data in a file in the [[NativeHost.appSettingsCacheDir]($backend) directory. It uses the [NativeAppStorage]($backend) api to store and load key/value pairs. Note that these settings are stored in a local file that may be deleted by the user, so it should only be used for a local cache of values that may be restored elsewhere. + +## NativeApp is now @beta + +The class [NativeApp]($frontend) has been promoted from @alpha to @beta. `NativeApp` is relevant for both Electron and mobile applications. Please provide feedback if you have issues or concerns on its use. + +## Properly declare changeSetId + +There were a number of places where *changeSetId* variables/parameters were incorrectly typed as [GuidString]($bentley) instead of `string`. +A *changeSetId* is a string hash value based on the ChangeSet contents and parent. It is not a GUID. +This is not a breaking change because `GuidString` is just a type alias for `string`. +It was, however, confusing from a usage and documentation perspective and needed to be corrected. + +## Promoted APIs + +The following APIs have been promoted to `public`. Public APIs are guaranteed to remain stable for the duration of the current major version of a package. +### @bentley/bentleyjs-core + * [assert]($bentleyjs-core) for asserting logic invariants. + * [ProcessDetector]($bentleyjs-core) for querying the type of executing Javascript process. + * [ObservableSet]($bentleyjs-core) for a [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) that emits events when its contents are modified. + * [ByteStream]($bentleyjs-core) for extracting data from binary streams. + * Types related to collections of [Id64String]($bentleyjs-core)s + * [OrderedId64Iterable]($bentleyjs-core) and [OrderedId64Array]($bentleyjs-core) + * [CompressedId64Set]($bentleyjs-core) and [MutableCompressedId64Set]($bentleyjs-core) + +## Breaking API changes + +### @bentley/ui-core package + +The beta class `SettingsProvider` was renamed to `SettingsTabsProvider`. + +### @bentley/ui-framework package + +The beta class `QuantityFormatSettingsPanel` was renamed to `QuantityFormatSettingsPage`. ### @bentley/imodeljs-quantity package diff --git a/docs/learning/ui/core/Settings.md b/docs/learning/ui/core/Settings.md index 684aa2bf204..cf08ec5466c 100644 --- a/docs/learning/ui/core/Settings.md +++ b/docs/learning/ui/core/Settings.md @@ -1,20 +1,20 @@ # Settings -The [SettingsManager]($ui-core) allows the registration of [SettingsProvider]($ui-core) classes to provide the settings to display with the [SettingsContainer]($ui-core) component. In an application that employs App UI, the [SettingsModalFrontstage]($ui-framework) frontstage will display the SettingsContainer and its entries. +The [SettingsManager]($ui-core) allows the registration of [SettingsTabsProvider]($ui-core) classes to provide the settings to display with the [SettingsContainer]($ui-core) component. In an application that employs App UI, the [SettingsModalFrontstage]($ui-framework) frontstage will display the SettingsContainer and its entries. ## SettingsTabEntry -Registered [SettingsProvider]($ui-core) instance will implement the method `getSettingEntries` to return an array of [SettingsProvider]($ui-core) items. Each SettingsTabEntry will populate a Tab entry in the [SettingsContainer]($ui-core) component. The `tabId` property is a string and must be unique across all registered SettingsProviders. A common practice is to prefix the tabId with the package name to ensure uniqueness. The `page` property holds the React.Element that will be used to construct the component to edit settings for this entry. It is the `page's` responsibility to persist and retrieve persisted settings. If the `page` contains multiple properties that must be saved together the SettingsTabEntry can set the `pageWillHandleCloseRequest` property to `true`. The `page's` control should then register to be notified when the setting container is closing or when the active SettingsEnty is changing so that any unsaved data can be saved. Two React hooks are provided to assist: `settingsManager.onProcessSettingsTabActivation` and `settingsManager.onProcessSettingsContainerClose`. +Registered [SettingsTabsProvider]($ui-core) instance will implement the method `getSettingEntries` to return an array of [SettingsTabsProvider]($ui-core) items. Each SettingsTabEntry will populate a Tab entry in the [SettingsContainer]($ui-core) component. The `tabId` property is a string and must be unique across all registered SettingsProviders. A common practice is to prefix the tabId with the package name to ensure uniqueness. The `page` property holds the React.Element that will be used to construct the component to edit settings for this entry. It is the `page's` responsibility to persist and retrieve persisted settings. If the `page` contains multiple properties that must be saved together the SettingsTabEntry can set the `pageWillHandleCloseRequest` property to `true`. The `page's` control should then register to be notified when the setting container is closing or when the active SettingsEnty is changing so that any unsaved data can be saved. Two React hooks are provided to assist: `settingsManager.onProcessSettingsTabActivation` and `settingsManager.onProcessSettingsContainerClose`. ## Example -### Example SettingsProvider +### Example SettingsTabsProvider Example below shows a settings provide that provides two settings pages. The first one depicts a page that has properties that cannot be saved immediately as individual properties are changed. It must be treated as a modal where once all values are define a save button is used the save the changes. The second settings page handles settings that can be immediately saved when changed and does not require any special processing when the page is closed. ```tsx // Sample UI items provider that dynamically adds ui items -export class ExampleSettingsProvider implements SettingsProvider { +export class ExampleSettingsProvider implements SettingsTabsProvider { public readonly id = "myApp:ExampleSettingsProvider"; public getSettingEntries(stageId: string, stageUsage: string): ReadonlyArray | undefined { @@ -145,6 +145,6 @@ function SaveFormatModalDialog({ persistChangesFunc, persistChangesFuncArg, onDi ## API Reference - [SettingsManager]($ui-core) -- [SettingsProvider]($ui-core) +- [SettingsTabsProvider]($ui-core) - [SettingsContainer]($ui-core) - [SettingsTabEntry]($ui-core) diff --git a/docs/learning/ui/framework/Backstage.md b/docs/learning/ui/framework/Backstage.md index a39d300915a..72865192c15 100644 --- a/docs/learning/ui/framework/Backstage.md +++ b/docs/learning/ui/framework/Backstage.md @@ -24,7 +24,7 @@ export function AppBackstageComposer() { } ``` -Note: the static method `SettingsModalFrontstage.getBackstageActionItem` used above, will create an entry for a `Settings` stage. This stage will display [SettingsTabEntry]($ui-core) items from [SettingsProvider]($ui-core) classes registered with the [SettingsManager]($ui-core). The `SettingsManager` instance is referenced by property `UiFramework.settingsManager`. +Note: the static method `SettingsModalFrontstage.getBackstageActionItem` used above, will create an entry for a `Settings` stage. This stage will display [SettingsTabEntry]($ui-core) items from [SettingsTabsProvider]($ui-core) classes registered with the [SettingsManager]($ui-core). The `SettingsManager` instance is referenced by property `UiFramework.settingsManager`. See additional info in [Backstage](../../../learning/ui/abstract/Backstage.md). diff --git a/docs/learning/ui/framework/UiSettings.md b/docs/learning/ui/framework/UiSettings.md new file mode 100644 index 00000000000..cea021f318a --- /dev/null +++ b/docs/learning/ui/framework/UiSettings.md @@ -0,0 +1,131 @@ +# UI Settings + +'UI Settings' refers to settings that define the state of different parts of the UI. Settings like if the UI is displayed in "dark" or "light" theme, size and location of column sizes or panel sizes. + +## Settings Storage + +Settings that are set up to be stored between "session" need to be stored and retrieved from some storage location. There are two provided storage locations that will serve that purpose. [LocalSettingsStorage]($ui-core) will use browser localStorage. [UserSettingsStorage]($ui-framework) will use the Product Settings Service available through IModelApp.settings. Please note that UserSettingsStorage requires the user to be logged-in to have access to this storage. + +If an application wants to store settings only for the current session [SessionSettingsStorage]($ui-core) is available. + +An application can choose to create and register their own class that implements the [UiSettingsStorage](ui-core) interface, if a custom storage location is desired for UI Settings. + +### Defining which storage to use + +Typically in the index.tsx file of an IModelApp that uses `App UI` user interface the code will set the storage location once the user has signed in. + +```ts + public static getUiSettingsStorage(): UiSettingsStorage { + const authorized = !!IModelApp.authorizationClient && IModelApp.authorizationClient.isAuthorized; + if (!authorized) { + return MyIModelApp._localUiSettingsStorage; // instance of LocalSettingsStorage + } + return MyIModelApp._UserUiSettingsStorage; // instance of UserSettingsStorage + } +``` + +The component [UiSettingsProvider]($ui-framework) can be added into the component tree to provide the storage object via React context. See hook [useUiSettingsStorageContext]($ui-framework). Below is an example of how to wrap the [ConfigurableUiContent]($ui-framework) element so that the context is available to all App UI components. + +```tsx + + } + /> + +``` + +## UserSettingsProvider + +The [UserSettingsProvider]($ui-framework) interface can be implemented by classes that want to restore their saved settings when the method [UiFramework.setUiSettingsStorage]($ui-framework) is called to set the UiSettingsStorage. A `UserSettingsProvider` class must be registered by calling [UiFramework.registerUserSettingsProvider] and supplying the implementing class instance. The `UserSettingsProvider` interface only requires that the provider define a unique `providerId` that is used to ensure the provider is only registered once. It must also implement the method `loadUserSettings(storage: UiSettingsStorage)` to asynchronously load its settings from [UiSettingsStorage](ui-core). + +### AppUiSettings + +The [AppUiSettings]($ui-framework) class, which implements the UserSettingsProvider interface, can be instantiated by the IModelApp and registered as an UserSettingsProvider. This is left up to the application so each one can provide default values for the settings maintained by the `AppUiSettings` class. Below is an excerpt from application startup code that shows the registration of `AppUiSettings`. + +```ts + public static async initialize() { + await UiFramework.initialize(undefined); + + // initialize Presentation + await Presentation.initialize({ + activeLocale: IModelApp.i18n.languageList()[0], + }); + Presentation.selection.scopes.activeScope = "top-assembly"; + + // other initialization calls not shown in this example excerpt + + // app specific call to register setting pages to display + AppSettingsTabsProvider.initializeAppSettingProvider(); + + // Create and register the AppUiSettings instance to provide default for ui settings in Redux store + const lastTheme = (window.localStorage&&window.localStorage.getItem("uifw:defaultTheme"))??SYSTEM_PREFERRED_COLOR_THEME; + const defaults = { + colorTheme: lastTheme ?? SYSTEM_PREFERRED_COLOR_THEME, + dragInteraction: false, + frameworkVersion: "2", + widgetOpacity: 0.8, + }; + + // initialize any settings providers that may need to have defaults set by iModelApp + UiFramework.registerUserSettingsProvider(new AppUiSettings(defaults)); + + // go ahead and initialize settings before login or in case login is by-passed + await UiFramework.setUiSettingsStorage(SampleAppIModelApp.getUiSettingsStorage()); + } +``` + +## Settings Components + +### Quantity Formatting Settings + + The [QuantityFormatSettingsPage]($ui-framework) component provides the UI to set both the [PresentationUnitSystem]($presentation-common) and formatting overrides in the [QuantityFormatter]($frontend). This component can be used in the new [SettingsContainer]($ui-core) UI component. The function `getQuantityFormatsSettingsManagerEntry` will return a [SettingsTabEntry]($ui-core) for use by the [SettingsManager]($ui-core). + +### User Interface Settings + + The [UiSettingsPage]($ui-framework) component provides the UI to set general UI settings that effect the look and feel of the App UI user interface. This component can be used in the new [SettingsContainer]($ui-core) UI component. The function `getUiSettingsManagerEntry` will return a [SettingsTabEntry]($ui-core) for use by the [SettingsManager]($ui-core). + +### Settings stage + +UI and Quantity Settings as well as other settings can be present to the user for editing using the stage [SettingsModalFrontstage]($ui-framework). This stage will display all [SettingsTabEntry]($ui-core) entries that are provided via [SettingsTabsProvider]($ui-core) classes. `SettingsTabsProvider` classes can be registered with the [SettingsManager]($ui-core) by the host application, package, or extension loaded into an IModelApp using the App UI user interface. The steps to add a settings stage include. + +#### Adding a backstage item + +The [SettingsModalFrontstage.getBackstageActionItem] method can be used to get a [BackstageActionItem]($ui-abstract) that is used to construct the backstage. Below is an example of how to set up a backstage menu component that will display the 'Settings' entry if `SettingsTabEntry` items are provided. + +```tsx +export function AppBackstageComposerComponent({ userInfo }: AppBackstageComposerProps) { + const [backstageItems] = React.useState(() => { + return [ + BackstageItemUtilities.createStageLauncher(ViewsFrontstage.stageId, 100, 10, IModelApp.i18n.translate("SampleApp:backstage.viewIModel"), + IModelApp.i18n.translate("SampleApp:backstage.iModelStage"), `svg:${stageIconSvg}`), + SettingsModalFrontstage.getBackstageActionItem (100, 20), + ]; + }); + + return ( + } + /> + ); +} +``` + +#### Defining a SettingsTabsProvider + +Below is an example [SettingsTabsProvider]($ui-core) class that adds two settings pages, one for Units Formatting and the other for UI Settings. In the `AppUiSettings` example above the call to the static method [AppSettingsTabsProvider.initializeAppSettingProvider] is called to add this provider with the SettingsManager instance held by UiFramework. + +```tsx +export class AppSettingsTabsProvider implements SettingsTabsProvider { + public readonly id = "AppSettingsTabsProvider"; + + public getSettingEntries(_stageId: string, _stageUsage: string): ReadonlyArray | undefined { + return [ + getQuantityFormatsSettingsManagerEntry(10, {availableUnitSystems:new Set(["metric","imperial","usSurvey"])}), + getUiSettingsManagerEntry(20), + ]; + } + + public static initializeAppSettingProvider() { + UiFramework.settingsManager.addSettingsProvider(new AppSettingsTabsProvider()); + } +``` diff --git a/docs/learning/ui/framework/index.md b/docs/learning/ui/framework/index.md index 40901b37752..a09344455b8 100644 --- a/docs/learning/ui/framework/index.md +++ b/docs/learning/ui/framework/index.md @@ -20,6 +20,7 @@ There are numerous React components and TypeScript classes in the `@bentley/ui-f - [Dialogs](./Dialogs.md) - [Workflows and Tasks](./TasksWorkflows.md) - [KeyboardShortcut]($ui-framework:KeyboardShortcut) - A keystroke or combination of keystrokes used to launch a command or tool. +- [UI Settings](./UiSettings.md) ## Other Topics diff --git a/domains/analytical/backend/package.json b/domains/analytical/backend/package.json index 1b3d39d0707..819e618c51c 100644 --- a/domains/analytical/backend/package.json +++ b/domains/analytical/backend/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/analytical-backend", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "main": "lib/analytical-backend.js", "typings": "lib/analytical-backend", "license": "MIT", @@ -32,20 +32,20 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/imodeljs-backend": "^2.15.0-dev.6", - "@bentley/imodeljs-common": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/imodeljs-backend": "^2.15.0-dev.13", + "@bentley/imodeljs-common": "^2.15.0-dev.13" }, "//devDependencies": [ "NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install", "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/fs-extra": "^4.0.7", "@types/mocha": "^5.2.5", diff --git a/domains/linear-referencing/backend/package.json b/domains/linear-referencing/backend/package.json index eff31c99bd1..e38eb5611a4 100644 --- a/domains/linear-referencing/backend/package.json +++ b/domains/linear-referencing/backend/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/linear-referencing-backend", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "main": "lib/linear-referencing-backend.js", "typings": "lib/linear-referencing-backend", "license": "MIT", @@ -32,22 +32,22 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/imodeljs-backend": "^2.15.0-dev.6", - "@bentley/imodeljs-common": "^2.15.0-dev.6", - "@bentley/linear-referencing-common": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/imodeljs-backend": "^2.15.0-dev.13", + "@bentley/imodeljs-common": "^2.15.0-dev.13", + "@bentley/linear-referencing-common": "^2.15.0-dev.13" }, "//devDependencies": [ "NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install", "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/linear-referencing-common": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/linear-referencing-common": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/fs-extra": "^4.0.7", "@types/mocha": "^5.2.5", diff --git a/domains/linear-referencing/common/package.json b/domains/linear-referencing/common/package.json index e1ea72dde85..e25abf5c2c4 100644 --- a/domains/linear-referencing/common/package.json +++ b/domains/linear-referencing/common/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/linear-referencing-common", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "main": "lib/linear-referencing-common.js", "typings": "lib/linear-referencing-common", "license": "MIT", @@ -31,18 +31,18 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/imodeljs-common": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/imodeljs-common": "^2.15.0-dev.13" }, "//devDependencies": [ "NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install", "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/fs-extra": "^4.0.7", "@types/mocha": "^5.2.5", diff --git a/domains/physical-material/backend/package.json b/domains/physical-material/backend/package.json index ea02b99e83e..3c9dc4d8d08 100644 --- a/domains/physical-material/backend/package.json +++ b/domains/physical-material/backend/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/physical-material-backend", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "main": "lib/physical-material-backend.js", "typings": "lib/physical-material-backend", "license": "MIT", @@ -31,20 +31,20 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/imodeljs-backend": "^2.15.0-dev.6", - "@bentley/imodeljs-common": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/imodeljs-backend": "^2.15.0-dev.13", + "@bentley/imodeljs-common": "^2.15.0-dev.13" }, "//devDependencies": [ "NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install", "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/fs-extra": "^4.0.7", "@types/mocha": "^5.2.5", diff --git a/editor/backend/package.json b/editor/backend/package.json index ddd847ee1d9..6bd4ad5199b 100644 --- a/editor/backend/package.json +++ b/editor/backend/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/imodeljs-editor-backend", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js editor backend", "main": "lib/imodeljs-editor-backend.js", "typings": "lib/imodeljs-editor-backend", @@ -34,22 +34,22 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/geometry-core": "^2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/imodeljs-common": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/geometry-core": "^2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/imodeljs-common": "^2.15.0-dev.13" }, "//devDependencies": [ "NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install", "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", "@types/deep-assign": "^0.1.0", @@ -70,7 +70,7 @@ "typescript": "~4.1.0" }, "dependencies": { - "@bentley/imodeljs-editor-common": "2.15.0-dev.6" + "@bentley/imodeljs-editor-common": "2.15.0-dev.13" }, "nyc": { "nycrc-path": "./node_modules/@bentley/build-tools/.nycrc" diff --git a/editor/common/package.json b/editor/common/package.json index 0750c2b494a..f52acf7c486 100644 --- a/editor/common/package.json +++ b/editor/common/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/imodeljs-editor-common", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js editing properties common to frontend and backend", "main": "lib/imodeljs-editor-common.js", "typings": "lib/imodeljs-editor-common", @@ -33,16 +33,16 @@ }, "dependencies": {}, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/geometry-core": "^2.15.0-dev.6", - "@bentley/imodeljs-common": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/geometry-core": "^2.15.0-dev.13", + "@bentley/imodeljs-common": "^2.15.0-dev.13" }, "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", "@types/node": "10.14.1", diff --git a/editor/frontend/package.json b/editor/frontend/package.json index c0218eed9b6..79041b50b73 100644 --- a/editor/frontend/package.json +++ b/editor/frontend/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/imodeljs-editor-frontend", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js frontend components", "main": "lib/imodeljs-editor-frontend.js", "typings": "lib/imodeljs-editor-frontend", @@ -34,21 +34,21 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/imodeljs-common": "^2.15.0-dev.6", - "@bentley/imodeljs-i18n": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/imodeljs-common": "^2.15.0-dev.13", + "@bentley/imodeljs-i18n": "^2.15.0-dev.13" }, "//devDependencies": [ "NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install", "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/certa": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/certa": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", "@types/node": "10.14.1", @@ -69,10 +69,10 @@ "NOTE: imodeljs-editor-frontend should remain UI technology agnostic, so no react/angular dependencies are allowed" ], "dependencies": { - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/ui-abstract": "2.15.0-dev.6", - "@bentley/imodeljs-editor-common": "2.15.0-dev.6" + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/ui-abstract": "2.15.0-dev.13", + "@bentley/imodeljs-editor-common": "2.15.0-dev.13" }, "nyc": { "nycrc-path": "./node_modules/@bentley/build-tools/.nycrc" diff --git a/example-code/app/package.json b/example-code/app/package.json index 0f45d7e68a3..d3b3ede1e0a 100644 --- a/example-code/app/package.json +++ b/example-code/app/package.json @@ -18,17 +18,17 @@ }, "repository": {}, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/context-registry-client": "2.15.0-dev.6", - "@bentley/imodelhub-client": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/logger-config": "2.15.0-dev.6", - "@bentley/rbac-client": "2.15.0-dev.6", - "@bentley/electron-manager": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/context-registry-client": "2.15.0-dev.13", + "@bentley/imodelhub-client": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/logger-config": "2.15.0-dev.13", + "@bentley/rbac-client": "2.15.0-dev.13", + "@bentley/electron-manager": "2.15.0-dev.13", "body-parser": "^1.18.2", "chai": "^4.1.2", "electron": "^11.1.0", @@ -43,9 +43,9 @@ "webpack": "4.42.0" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/oidc-signin-tool": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/oidc-signin-tool": "2.15.0-dev.13", "@types/body-parser": "^1.17.0", "@types/chai": "^4.1.4", "@types/express": "^4.16.1", diff --git a/example-code/snippets/package.json b/example-code/snippets/package.json index da5f64acfbd..ded16b4f109 100644 --- a/example-code/snippets/package.json +++ b/example-code/snippets/package.json @@ -17,16 +17,16 @@ }, "repository": {}, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/context-registry-client": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodelhub-client": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/backend-itwin-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/rbac-client": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/context-registry-client": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodelhub-client": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/backend-itwin-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/rbac-client": "2.15.0-dev.13", "body-parser": "^1.18.2", "chai": "^4.1.2", "electron": "^11.1.0", @@ -41,9 +41,9 @@ "webpack": "4.42.0" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/oidc-signin-tool": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/oidc-signin-tool": "2.15.0-dev.13", "@types/body-parser": "^1.17.0", "@types/chai": "^4.1.4", "@types/express": "^4.16.1", diff --git a/extensions/geonames/package.json b/extensions/geonames/package.json index 25436f31bd8..f4963b6c26b 100644 --- a/extensions/geonames/package.json +++ b/extensions/geonames/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/geonames-extension", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "Geolocation Extension", "main": "index.js", "license": "MIT", @@ -25,10 +25,10 @@ "url": "http://www.bentley.com" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/extension-webpack-tools": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/extension-webpack-tools": "2.15.0-dev.13", "@types/node": "10.14.1", "cpx": "^1.5.0", "eslint": "^6.8.0", @@ -36,13 +36,13 @@ "typescript": "~4.1.0" }, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", - "@bentley/imodeljs-quantity": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", + "@bentley/imodeljs-quantity": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", "svg-sprite-loader": "4.2.1" }, "eslintConfig": { diff --git a/extensions/iot-demo/package.json b/extensions/iot-demo/package.json index cb1927896c0..a0fb5fd7c89 100644 --- a/extensions/iot-demo/package.json +++ b/extensions/iot-demo/package.json @@ -27,9 +27,9 @@ "url": "http://www.bentley.com" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/extension-webpack-tools": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/extension-webpack-tools": "2.15.0-dev.13", "@types/react": "16.9.43", "@types/classnames": "^2.2.3", "cpx": "^1.5.0", @@ -38,18 +38,18 @@ "typescript": "~4.1.0" }, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-quantity": "2.15.0-dev.6", - "@bentley/ui-abstract": "2.15.0-dev.6", - "@bentley/ui-components": "2.15.0-dev.6", - "@bentley/ui-core": "2.15.0-dev.6", - "@bentley/ui-framework": "2.15.0-dev.6", - "@bentley/ui-ninezone": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-quantity": "2.15.0-dev.13", + "@bentley/ui-abstract": "2.15.0-dev.13", + "@bentley/ui-components": "2.15.0-dev.13", + "@bentley/ui-core": "2.15.0-dev.13", + "@bentley/ui-framework": "2.15.0-dev.13", + "@bentley/ui-ninezone": "2.15.0-dev.13", "classnames": "^2.2.5", "react": "^16.8.0", "svg-sprite-loader": "4.2.1" diff --git a/extensions/map-layers/package.json b/extensions/map-layers/package.json index 8a992dc3c33..9ffdd1071a2 100644 --- a/extensions/map-layers/package.json +++ b/extensions/map-layers/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/map-layers", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "Extension that adds a Map Layers Widget", "main": "lib/map-layers.js", "typings": "lib/map-layers", @@ -33,23 +33,23 @@ "url": "http://www.bentley.com" }, "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/extension-webpack-tools": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", - "@bentley/imodeljs-quantity": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/presentation-common": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/extension-webpack-tools": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", + "@bentley/imodeljs-quantity": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/presentation-common": "2.15.0-dev.13", "@bentley/react-scripts": "3.4.9", - "@bentley/ui-abstract": "2.15.0-dev.6", - "@bentley/ui-components": "2.15.0-dev.6", - "@bentley/ui-core": "2.15.0-dev.6", - "@bentley/ui-framework": "2.15.0-dev.6", - "@bentley/ui-ninezone": "2.15.0-dev.6", + "@bentley/ui-abstract": "2.15.0-dev.13", + "@bentley/ui-components": "2.15.0-dev.13", + "@bentley/ui-core": "2.15.0-dev.13", + "@bentley/ui-framework": "2.15.0-dev.13", + "@bentley/ui-ninezone": "2.15.0-dev.13", "@testing-library/react": "^8.0.1", "@testing-library/react-hooks": "^3.2.1", "@types/classnames": "^2.2.3", @@ -88,18 +88,18 @@ "react-select": "3.1.0" }, "peerDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", - "@bentley/imodeljs-quantity": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/ui-abstract": "2.15.0-dev.6", - "@bentley/ui-components": "2.15.0-dev.6", - "@bentley/ui-core": "2.15.0-dev.6", - "@bentley/ui-framework": "2.15.0-dev.6", - "@bentley/ui-ninezone": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", + "@bentley/imodeljs-quantity": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/ui-abstract": "2.15.0-dev.13", + "@bentley/ui-components": "2.15.0-dev.13", + "@bentley/ui-core": "2.15.0-dev.13", + "@bentley/ui-framework": "2.15.0-dev.13", + "@bentley/ui-ninezone": "2.15.0-dev.13", "react": "^16.8.0", "react-dom": "^16.8.0" }, diff --git a/full-stack-tests/core/package.json b/full-stack-tests/core/package.json index f6826a3d287..ae386d6e137 100644 --- a/full-stack-tests/core/package.json +++ b/full-stack-tests/core/package.json @@ -24,38 +24,38 @@ }, "repository": {}, "dependencies": { - "@bentley/backend-itwin-client": "2.15.0-dev.6", - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/express-server": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/hypermodeling-frontend": "2.15.0-dev.6", - "@bentley/imodelhub-client": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-editor-backend": "2.15.0-dev.6", - "@bentley/imodeljs-editor-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-editor-common": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", - "@bentley/imodeljs-markup": "2.15.0-dev.6", - "@bentley/electron-manager": "2.15.0-dev.6", - "@bentley/rbac-client": "2.15.0-dev.6", + "@bentley/backend-itwin-client": "2.15.0-dev.13", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/express-server": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/hypermodeling-frontend": "2.15.0-dev.13", + "@bentley/imodelhub-client": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-editor-backend": "2.15.0-dev.13", + "@bentley/imodeljs-editor-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-editor-common": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", + "@bentley/imodeljs-markup": "2.15.0-dev.13", + "@bentley/electron-manager": "2.15.0-dev.13", + "@bentley/rbac-client": "2.15.0-dev.13", "chai": "^4.1.2", "chai-as-promised": "^7", "fs-extra": "^8.1.0", "electron": "^11.1.0" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/certa": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/context-registry-client": "2.15.0-dev.6", - "@bentley/frontend-authorization-client": "2.15.0-dev.6", - "@bentley/oidc-signin-tool": "2.15.0-dev.6", - "@bentley/product-settings-client": "2.15.0-dev.6", - "@bentley/extension-webpack-tools": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/certa": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/context-registry-client": "2.15.0-dev.13", + "@bentley/frontend-authorization-client": "2.15.0-dev.13", + "@bentley/oidc-signin-tool": "2.15.0-dev.13", + "@bentley/product-settings-client": "2.15.0-dev.13", + "@bentley/extension-webpack-tools": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", "@types/fs-extra": "^4.0.7", diff --git a/full-stack-tests/core/src/frontend/hub/IModelConnection.test.ts b/full-stack-tests/core/src/frontend/hub/IModelConnection.test.ts index dcdf75a3bbc..13368c7220b 100644 --- a/full-stack-tests/core/src/frontend/hub/IModelConnection.test.ts +++ b/full-stack-tests/core/src/frontend/hub/IModelConnection.test.ts @@ -150,7 +150,7 @@ describe("IModelConnection (#integration)", () => { assert.isNotNull(noVersionsIModel3); }); - it("should send a usage log everytime an iModel is opened", async () => { + it.skip("should send a usage log everytime an iModel is opened", async () => { const projectId = await TestUtility.getTestProjectId("iModelJsIntegrationTest"); const iModelId = await TestUtility.getTestIModelId(projectId, "ReadOnlyTest"); diff --git a/full-stack-tests/core/src/frontend/hub/IModelOpen.test.ts b/full-stack-tests/core/src/frontend/hub/IModelOpen.test.ts index ac52a86e917..3397c7c2465 100644 --- a/full-stack-tests/core/src/frontend/hub/IModelOpen.test.ts +++ b/full-stack-tests/core/src/frontend/hub/IModelOpen.test.ts @@ -14,7 +14,7 @@ import { TestUtility } from "./TestUtility"; describe("Opening IModelConnection (#integration)", () => { let testProjectId: GuidString; let testIModelId: GuidString; - let testChangeSetId: GuidString; + let testChangeSetId: string; before(async () => { await MockRender.App.startup({ diff --git a/full-stack-tests/imodelhub-client/package.json b/full-stack-tests/imodelhub-client/package.json index bffc3e629ac..a7e63607ac0 100644 --- a/full-stack-tests/imodelhub-client/package.json +++ b/full-stack-tests/imodelhub-client/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/imodelhub-client-tests", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "main": "lib/imodelhub-client-tests.js", "description": "Tests the iModelHub client", "license": "MIT", @@ -36,14 +36,14 @@ "url": "http://www.bentley.com" }, "dependencies": { - "@bentley/backend-itwin-client": "2.15.0-dev.6", - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/context-registry-client": "2.15.0-dev.6", - "@bentley/frontend-authorization-client": "2.15.0-dev.6", - "@bentley/imodelhub-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/rbac-client": "2.15.0-dev.6", + "@bentley/backend-itwin-client": "2.15.0-dev.13", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/context-registry-client": "2.15.0-dev.13", + "@bentley/frontend-authorization-client": "2.15.0-dev.13", + "@bentley/imodelhub-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/rbac-client": "2.15.0-dev.13", "chai": "^4.1.2", "deep-assign": "^2.0.0", "fs-extra": "^8.1.0", @@ -52,10 +52,10 @@ "nock": "^12.0.3" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/oidc-signin-tool": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/oidc-signin-tool": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/deep-assign": "^0.1.0", "@types/fs-extra": "^4.0.7", diff --git a/full-stack-tests/native-app/package.json b/full-stack-tests/native-app/package.json index b4e935b2bf5..8b25c0d4c55 100644 --- a/full-stack-tests/native-app/package.json +++ b/full-stack-tests/native-app/package.json @@ -20,31 +20,31 @@ }, "repository": {}, "dependencies": { - "@bentley/backend-itwin-client": "2.15.0-dev.6", - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/context-registry-client": "2.15.0-dev.6", - "@bentley/express-server": "2.15.0-dev.6", - "@bentley/frontend-authorization-client": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodelhub-client": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-markup": "2.15.0-dev.6", - "@bentley/rbac-client": "2.15.0-dev.6", - "@bentley/electron-manager": "2.15.0-dev.6", + "@bentley/backend-itwin-client": "2.15.0-dev.13", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/context-registry-client": "2.15.0-dev.13", + "@bentley/express-server": "2.15.0-dev.13", + "@bentley/frontend-authorization-client": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodelhub-client": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-markup": "2.15.0-dev.13", + "@bentley/rbac-client": "2.15.0-dev.13", + "@bentley/electron-manager": "2.15.0-dev.13", "chai": "^4.1.2", "fs-extra": "^8.1.0", "electron": "^11.1.0" }, "devDependencies": { - "@bentley/certa": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/oidc-signin-tool": "2.15.0-dev.6", + "@bentley/certa": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/oidc-signin-tool": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/fs-extra": "^4.0.7", "@types/mocha": "^5.2.5", diff --git a/full-stack-tests/native-app/src/frontend/standalone/NativeAppStorage.test.ts b/full-stack-tests/native-app/src/frontend/standalone/NativeAppStorage.test.ts index d9cbd87abb2..4477b0ce5d6 100644 --- a/full-stack-tests/native-app/src/frontend/standalone/NativeAppStorage.test.ts +++ b/full-stack-tests/native-app/src/frontend/standalone/NativeAppStorage.test.ts @@ -39,7 +39,7 @@ describe("NativeApp Storage frontend", () => { } } assert.equal((await test1.getKeys()).length, dataset.length); - await test1.close(true); + await NativeApp.closeStorage(test1, true); }); it("Override and type check", async () => { @@ -80,6 +80,7 @@ describe("NativeApp Storage frontend", () => { assert.equal((await test1.getData("key1") as Uint8Array).length, testArray.length); await test1.removeData("key1"); assert.isUndefined(await test1.getData("key1")); + await NativeApp.closeStorage(test1, true); }); }); diff --git a/full-stack-tests/presentation/package.json b/full-stack-tests/presentation/package.json index 708d629b39f..52a9b2c541d 100644 --- a/full-stack-tests/presentation/package.json +++ b/full-stack-tests/presentation/package.json @@ -22,23 +22,23 @@ "cover": "npm run test" }, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/oidc-signin-tool": "2.15.0-dev.6", - "@bentley/presentation-common": "2.15.0-dev.6", - "@bentley/presentation-backend": "2.15.0-dev.6", - "@bentley/presentation-frontend": "2.15.0-dev.6", - "@bentley/presentation-components": "2.15.0-dev.6", - "@bentley/presentation-testing": "2.15.0-dev.6", - "@bentley/product-settings-client": "2.15.0-dev.6", - "@bentley/ui-core": "2.15.0-dev.6", - "@bentley/ui-components": "2.15.0-dev.6", - "@bentley/ui-abstract": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/oidc-signin-tool": "2.15.0-dev.13", + "@bentley/presentation-common": "2.15.0-dev.13", + "@bentley/presentation-backend": "2.15.0-dev.13", + "@bentley/presentation-frontend": "2.15.0-dev.13", + "@bentley/presentation-components": "2.15.0-dev.13", + "@bentley/presentation-testing": "2.15.0-dev.13", + "@bentley/product-settings-client": "2.15.0-dev.13", + "@bentley/ui-core": "2.15.0-dev.13", + "@bentley/ui-components": "2.15.0-dev.13", + "@bentley/ui-abstract": "2.15.0-dev.13", "@testing-library/react-hooks": "^3.2.1", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", @@ -59,7 +59,7 @@ "cpx": "^1.5.0", "deep-equal": "^1", "faker": "^4.1.0", - "immer": "8.0.1", + "immer": "^9.0.1", "mocha": "^5.2.0", "react": "^16.8.0", "react-dom": "^16.8.0", @@ -71,9 +71,9 @@ "xmlhttprequest": "^1.8.0" }, "devDependencies": { - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/react": "16.9.43", "@types/react-dom": "^16.8.0", "@types/testing-library__react-hooks": "^3.1.0", diff --git a/full-stack-tests/rpc-interface/package.json b/full-stack-tests/rpc-interface/package.json index d72db0c0800..bb3a96476d1 100644 --- a/full-stack-tests/rpc-interface/package.json +++ b/full-stack-tests/rpc-interface/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/rpcinterface-full-stack-tests", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "Test the full iModel.js stack (frontend and backend) using standard RPC interfaces", "license": "MIT", "scripts": { @@ -34,23 +34,23 @@ "url": "http://www.bentley.com" }, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/certa": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/context-registry-client": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodelhub-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", - "@bentley/imodeljs-quantity": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/oidc-signin-tool": "2.15.0-dev.6", - "@bentley/presentation-common": "2.15.0-dev.6", - "@bentley/presentation-frontend": "2.15.0-dev.6", - "@bentley/rbac-client": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/certa": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/context-registry-client": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodelhub-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", + "@bentley/imodeljs-quantity": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/oidc-signin-tool": "2.15.0-dev.13", + "@bentley/presentation-common": "2.15.0-dev.13", + "@bentley/presentation-frontend": "2.15.0-dev.13", + "@bentley/rbac-client": "2.15.0-dev.13", "chai": "^4.1.2", "chai-as-promised": "^7", "dotenv": "^8.2.0", @@ -60,9 +60,9 @@ "puppeteer": "chrome-86" }, "devDependencies": { - "@bentley/express-server": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/presentation-backend": "2.15.0-dev.6", + "@bentley/express-server": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/presentation-backend": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", "@types/dotenv": "^6.1.1", diff --git a/full-stack-tests/rpc/package.json b/full-stack-tests/rpc/package.json index 935f477ee3f..cb6f573d678 100644 --- a/full-stack-tests/rpc/package.json +++ b/full-stack-tests/rpc/package.json @@ -18,14 +18,14 @@ }, "repository": {}, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/express-server": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/electron-manager": "2.15.0-dev.6", - "@bentley/mobile-manager": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/express-server": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/electron-manager": "2.15.0-dev.13", + "@bentley/mobile-manager": "2.15.0-dev.13", "chai": "^4.1.2", "electron": "^11.1.0", "express": "^4.16.3", @@ -33,9 +33,9 @@ "spdy": "^4.0.1" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/certa": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/certa": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/express": "^4.16.1", "@types/mocha": "^5.2.5", diff --git a/full-stack-tests/rpc/src/backend/ipc.ts b/full-stack-tests/rpc/src/backend/ipc.ts index 3579dbb5b77..2cda6d968d5 100644 --- a/full-stack-tests/rpc/src/backend/ipc.ts +++ b/full-stack-tests/rpc/src/backend/ipc.ts @@ -8,6 +8,8 @@ import { BackendTestCallbacks } from "../common/SideChannels"; export async function setupIpcTest(before = async () => { }) { let socket: IpcWebSocketBackend; + let ready: () => void; + const started = new Promise((resolve) => ready = resolve); registerBackendCallback(BackendTestCallbacks.startIpcTest, () => { setTimeout(async () => { @@ -23,12 +25,15 @@ export async function setupIpcTest(before = async () => { }) { socket.handle("testinvoke", async (_event: Event, methodName: string, ...args: any[]) => { return [methodName, ...args]; }); + + ready(); }); return true; }); - registerBackendCallback(BackendTestCallbacks.sendIpcMessage, () => { + registerBackendCallback(BackendTestCallbacks.sendIpcMessage, async () => { + await started; socket.send("test", 4, 5, 6); return true; }); diff --git a/presentation/backend/package.json b/presentation/backend/package.json index 4fb9e610543..8c9a07a7205 100644 --- a/presentation/backend/package.json +++ b/presentation/backend/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/presentation-backend", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "Backend of iModel.js Presentation library", "license": "MIT", "repository": { @@ -37,20 +37,20 @@ "test:watch": "npm test -- --reporter min --watch-extensions ts --watch" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/imodeljs-backend": "^2.15.0-dev.6", - "@bentley/imodeljs-common": "^2.15.0-dev.6", - "@bentley/imodeljs-quantity": "^2.15.0-dev.6", - "@bentley/presentation-common": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/imodeljs-backend": "^2.15.0-dev.13", + "@bentley/imodeljs-common": "^2.15.0-dev.13", + "@bentley/imodeljs-quantity": "^2.15.0-dev.13", + "@bentley/presentation-common": "^2.15.0-dev.13" }, "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-quantity": "2.15.0-dev.6", - "@bentley/presentation-common": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-quantity": "2.15.0-dev.13", + "@bentley/presentation-common": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", "@types/chai-jest-snapshot": "^1.3.0", diff --git a/presentation/common/package.json b/presentation/common/package.json index 9edc06d91fa..66c1bf2cba3 100644 --- a/presentation/common/package.json +++ b/presentation/common/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/presentation-common", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "Common pieces for iModel.js presentation packages", "imodeljsSharedLibrary": true, "license": "MIT", @@ -43,16 +43,16 @@ "test:watch": "npm test -- --reporter min --watch-extensions ts --watch" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/imodeljs-common": "^2.15.0-dev.6", - "@bentley/imodeljs-quantity": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/imodeljs-common": "^2.15.0-dev.13", + "@bentley/imodeljs-quantity": "^2.15.0-dev.13" }, "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-quantity": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-quantity": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", "@types/chai-jest-snapshot": "^1.3.0", diff --git a/presentation/components/package.json b/presentation/components/package.json index 834f92dbd40..a02f5599b96 100644 --- a/presentation/components/package.json +++ b/presentation/components/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/presentation-components", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "React components based on iModel.js Presentation library", "main": "lib/presentation-components.js", "typings": "lib/presentation-components", @@ -47,30 +47,30 @@ "rxjs": "^6.6.2" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/imodeljs-common": "^2.15.0-dev.6", - "@bentley/imodeljs-frontend": "^2.15.0-dev.6", - "@bentley/imodeljs-i18n": "^2.15.0-dev.6", - "@bentley/presentation-common": "^2.15.0-dev.6", - "@bentley/presentation-frontend": "^2.15.0-dev.6", - "@bentley/ui-abstract": "^2.15.0-dev.6", - "@bentley/ui-components": "^2.15.0-dev.6", - "@bentley/ui-core": "^2.15.0-dev.6", + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/imodeljs-common": "^2.15.0-dev.13", + "@bentley/imodeljs-frontend": "^2.15.0-dev.13", + "@bentley/imodeljs-i18n": "^2.15.0-dev.13", + "@bentley/presentation-common": "^2.15.0-dev.13", + "@bentley/presentation-frontend": "^2.15.0-dev.13", + "@bentley/ui-abstract": "^2.15.0-dev.13", + "@bentley/ui-components": "^2.15.0-dev.13", + "@bentley/ui-core": "^2.15.0-dev.13", "react": "^16.8.0", "react-dom": "^16.8.0" }, "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", - "@bentley/presentation-common": "2.15.0-dev.6", - "@bentley/presentation-frontend": "2.15.0-dev.6", - "@bentley/ui-abstract": "2.15.0-dev.6", - "@bentley/ui-components": "2.15.0-dev.6", - "@bentley/ui-core": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", + "@bentley/presentation-common": "2.15.0-dev.13", + "@bentley/presentation-frontend": "2.15.0-dev.13", + "@bentley/ui-abstract": "2.15.0-dev.13", + "@bentley/ui-components": "2.15.0-dev.13", + "@bentley/ui-core": "2.15.0-dev.13", "@testing-library/react": "^8.0.1", "@testing-library/react-hooks": "^3.2.1", "@types/chai": "^4.1.4", @@ -98,7 +98,7 @@ "eslint": "^6.8.0", "faker": "^4.1.0", "ignore-styles": "^5.0.1", - "immer": "8.0.1", + "immer": "^9.0.1", "jsdom-global": "3.0.2", "mocha": "^5.2.0", "nyc": "^14.0.0", diff --git a/presentation/components/src/presentation-components/tree/controlled/UseUnifiedSelection.ts b/presentation/components/src/presentation-components/tree/controlled/UseUnifiedSelection.ts index 4905515bb61..1de9d0428fa 100644 --- a/presentation/components/src/presentation-components/tree/controlled/UseUnifiedSelection.ts +++ b/presentation/components/src/presentation-components/tree/controlled/UseUnifiedSelection.ts @@ -7,7 +7,6 @@ */ import { useCallback } from "react"; -import { from } from "rxjs/internal/observable/from"; import { takeUntil } from "rxjs/internal/operators/takeUntil"; import { tap } from "rxjs/internal/operators/tap"; import { Subject } from "rxjs/internal/Subject"; @@ -15,8 +14,8 @@ import { Guid, IDisposable } from "@bentley/bentleyjs-core"; import { Keys, KeySet, NodeKey } from "@bentley/presentation-common"; import { Presentation, SelectionChangeEventArgs, SelectionChangeType, SelectionHandler, SelectionHelper } from "@bentley/presentation-frontend"; import { - AbstractTreeNodeLoaderWithProvider, MutableTreeModel, MutableTreeModelNode, TreeEditingParams, TreeEventHandler, TreeModelChanges, TreeModelSource, - TreeNodeItem, TreeSelectionModificationEventArgs, TreeSelectionReplacementEventArgs, + AbstractTreeNodeLoaderWithProvider, MutableTreeModel, MutableTreeModelNode, toRxjsObservable, TreeEditingParams, TreeEventHandler, TreeModelChanges, + TreeModelSource, TreeNodeItem, TreeSelectionModificationEventArgs, TreeSelectionReplacementEventArgs, } from "@bentley/ui-components"; import { useDisposable } from "@bentley/ui-core"; import { IPresentationTreeDataProvider } from "../IPresentationTreeDataProvider"; @@ -90,7 +89,7 @@ export class UnifiedSelectionTreeEventHandler extends TreeEventHandler implement } public onSelectionModified({ modifications }: TreeSelectionModificationEventArgs) { - const withUnifiedSelection = from(modifications).pipe( + const withUnifiedSelection = toRxjsObservable(modifications).pipe( takeUntil(this._cancelled), tap({ next: ({ selectedNodeItems, deselectedNodeItems }) => { @@ -110,7 +109,7 @@ export class UnifiedSelectionTreeEventHandler extends TreeEventHandler implement public onSelectionReplaced({ replacements }: TreeSelectionReplacementEventArgs) { let firstEmission = true; - const withUnifiedSelection = from(replacements).pipe( + const withUnifiedSelection = toRxjsObservable(replacements).pipe( takeUntil(this._cancelled), tap({ next: ({ selectedNodeItems }) => { diff --git a/presentation/frontend/package.json b/presentation/frontend/package.json index 2e22c76c3da..50f9176f36d 100644 --- a/presentation/frontend/package.json +++ b/presentation/frontend/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/presentation-frontend", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "Frontend of iModel.js Presentation library", "main": "lib/presentation-frontend.js", "typings": "lib/presentation-frontend", @@ -43,23 +43,23 @@ "lodash": "^4.17.10" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/imodeljs-common": "^2.15.0-dev.6", - "@bentley/imodeljs-frontend": "^2.15.0-dev.6", - "@bentley/imodeljs-i18n": "^2.15.0-dev.6", - "@bentley/presentation-common": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/imodeljs-common": "^2.15.0-dev.13", + "@bentley/imodeljs-frontend": "^2.15.0-dev.13", + "@bentley/imodeljs-i18n": "^2.15.0-dev.13", + "@bentley/presentation-common": "^2.15.0-dev.13" }, "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/frontend-authorization-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/presentation-common": "2.15.0-dev.6", - "@bentley/product-settings-client": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/frontend-authorization-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/presentation-common": "2.15.0-dev.13", + "@bentley/product-settings-client": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", "@types/chai-jest-snapshot": "^1.3.0", diff --git a/presentation/testing/package.json b/presentation/testing/package.json index 01863125821..7c71ab642ce 100644 --- a/presentation/testing/package.json +++ b/presentation/testing/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/presentation-testing", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "", "license": "MIT", "repository": { @@ -38,20 +38,20 @@ "ignore-styles": "^5.0.1" }, "dependencies": { - "@bentley/backend-itwin-client": "2.15.0-dev.6", - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/presentation-backend": "2.15.0-dev.6", - "@bentley/presentation-common": "2.15.0-dev.6", - "@bentley/presentation-components": "2.15.0-dev.6", - "@bentley/presentation-frontend": "2.15.0-dev.6", - "@bentley/ui-abstract": "2.15.0-dev.6", - "@bentley/ui-components": "2.15.0-dev.6", + "@bentley/backend-itwin-client": "2.15.0-dev.13", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/presentation-backend": "2.15.0-dev.13", + "@bentley/presentation-common": "2.15.0-dev.13", + "@bentley/presentation-components": "2.15.0-dev.13", + "@bentley/presentation-frontend": "2.15.0-dev.13", + "@bentley/ui-abstract": "2.15.0-dev.13", + "@bentley/ui-components": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", "@types/chai-jest-snapshot": "^1.3.0", diff --git a/test-apps/analysis-importer/package.json b/test-apps/analysis-importer/package.json index 046a25a22ae..79b6469d265 100644 --- a/test-apps/analysis-importer/package.json +++ b/test-apps/analysis-importer/package.json @@ -15,16 +15,16 @@ }, "repository": {}, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", "body-parser": "^1.18.2" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/body-parser": "^1.17.0", "@types/express": "^4.16.1", "@types/node": "10.14.1", diff --git a/test-apps/display-performance-test-app/README.md b/test-apps/display-performance-test-app/README.md index 13480402025..21daa2619d6 100644 --- a/test-apps/display-performance-test-app/README.md +++ b/test-apps/display-performance-test-app/README.md @@ -10,6 +10,7 @@ * Run the command "npm run test:chrome" to run the backend and automatically bring up a Google Chrome browser window to start the test. This will also automatically kill **ALL** Google Chrome browser windows once the test has completed. * Run the command "npm run test:edge" to run the backend and automatically bring up an Edge browser window to start the test. This will also automatically kill **ALL** Edge browser windows once the test has completed. * Run the command "npm run test:firefox" to run the backend and automatically bring up a FireFox browser window to start the test. This will also automatically kill **ALL** FireFox browser windows once the test has completed. +* Run the command "npm run test:safari" to run the backend and automatically bring up a Safari browser window to start the test. This will also automatically kill **ALL** Safari browser windows once the test has completed. ## Options available for a performance test run: diff --git a/test-apps/display-performance-test-app/package.json b/test-apps/display-performance-test-app/package.json index 7a46fa8678b..25bdd6be5f7 100644 --- a/test-apps/display-performance-test-app/package.json +++ b/test-apps/display-performance-test-app/package.json @@ -18,7 +18,7 @@ "build:frontend": "cross-env DISABLE_ESLINT=true TRANSPILE_DEPS=false USE_FAST_SASS=true react-scripts build", "clean": "rimraf lib build .rush/temp/package-deps*.json", "docs": "", - "lint": "eslint -f visualstudio --config package.json --no-eslintrc ./src/**/*.ts 1>&2", + "lint": "eslint -f visualstudio --config package.json --no-eslintrc \"./src/**/*.ts\" 1>&2", "mobile": "tsc 1>&2 && webpack --config mobile.backend.webpack.config.js 1>&2 && webpack --config mobile.frontend.webpack.config.js 1>&2 && cpx \"public/**/*\" ./lib/mobile/public && cpx \"assets/**/*\" ./lib/mobile/assets ", "start": "npm run start:electron", "start:webserver": "cross-env BROWSER=none DISABLE_ESLINT=true TRANSPILE_DEPS=false USE_FAST_SASS=true react-scripts start", @@ -27,36 +27,37 @@ "test:chrome": "node ./lib/common/npmCommands.js chrome", "test:edge": "node ./lib/common/npmCommands.js edge", "test:firefox": "node ./lib/common/npmCommands.js firefox", + "test:safari": "node ./lib/common/npmCommands.js safari", "build:mobile-backend": "tsc 1>&2 && webpack --config ./node_modules/@bentley/webpack-tools/mobile/backend.config.js --env.outdir=./lib/mobile --env.entry=./lib/backend/MobileMain.js --env.bundlename=main ", "test": "", "cover": "" }, "repository": {}, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/context-registry-client": "2.15.0-dev.6", - "@bentley/electron-manager": "2.15.0-dev.6", - "@bentley/mobile-manager": "2.15.0-dev.6", - "@bentley/frontend-authorization-client": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodelhub-client": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/backend-itwin-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", - "@bentley/imodeljs-quantity": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/projectshare-client": "2.15.0-dev.6", - "@bentley/rbac-client": "2.15.0-dev.6", - "@bentley/ui-abstract": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/context-registry-client": "2.15.0-dev.13", + "@bentley/electron-manager": "2.15.0-dev.13", + "@bentley/mobile-manager": "2.15.0-dev.13", + "@bentley/frontend-authorization-client": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodelhub-client": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/backend-itwin-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", + "@bentley/imodeljs-quantity": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/projectshare-client": "2.15.0-dev.13", + "@bentley/rbac-client": "2.15.0-dev.13", + "@bentley/ui-abstract": "2.15.0-dev.13", "body-parser": "^1.18.2" }, "devDependencies": { - "@bentley/perf-tools": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/perf-tools": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@bentley/react-scripts": "3.4.9", "@types/body-parser": "^1.17.0", "@types/express": "^4.16.1", diff --git a/test-apps/display-performance-test-app/src/backend/DisplayPerfRpcImpl.ts b/test-apps/display-performance-test-app/src/backend/DisplayPerfRpcImpl.ts index 36a4dd75375..048c8dfd229 100644 --- a/test-apps/display-performance-test-app/src/backend/DisplayPerfRpcImpl.ts +++ b/test-apps/display-performance-test-app/src/backend/DisplayPerfRpcImpl.ts @@ -30,7 +30,7 @@ export default class DisplayPerfRpcImpl extends DisplayPerfRpcInterface { } let argOutputPath: string | undefined; process.argv.forEach((arg, index) => { - if (index >= 2 && arg !== "chrome" && arg !== "edge" && arg !== "firefox" && arg !== "headless" && arg.split(".").pop() !== "json") { + if (index >= 2 && arg !== "chrome" && arg !== "edge" && arg !== "firefox" && arg !== "safari" && arg !== "headless" && arg.split(".").pop() !== "json") { while (arg.endsWith("\\") || arg.endsWith("\/")) arg = arg.slice(0, -1); argOutputPath = `"argOutputPath": "${arg}",`; diff --git a/test-apps/display-performance-test-app/src/backend/WebMain.ts b/test-apps/display-performance-test-app/src/backend/WebMain.ts index 0f7bb918141..f6457aea95f 100644 --- a/test-apps/display-performance-test-app/src/backend/WebMain.ts +++ b/test-apps/display-performance-test-app/src/backend/WebMain.ts @@ -54,7 +54,7 @@ function startWebServer() { process.argv.forEach((arg) => { if (arg.split(".").pop() === "json") DisplayPerfRpcInterface.jsonFilePath = arg; - else if (arg === "chrome" || arg === "edge" || arg === "firefox") + else if (arg === "chrome" || arg === "edge" || arg === "firefox" || arg === "safari") browser = arg; else if (arg === "headless") chromeFlags.push("--headless"); @@ -99,13 +99,29 @@ function startWebServer() { // --------------------------------------------- // Start the browser, if given a specific one // --------------------------------------------- - if (browser === "chrome") - chromeLauncher.launch({ // eslint-disable-line @typescript-eslint/no-floating-promises - startingUrl: "http://localhost:3000", - chromeFlags, - }).then((val) => { DisplayPerfRpcInterface.chrome = val; }); - else if (browser === "firefox") - child_process.execSync("start firefox http://localhost:3000"); - else if (browser === "edge") - child_process.execSync("start microsoft-edge:http://localhost:3000"); + switch (browser) { + case "chrome": + if (process.platform === "darwin") { // Ie, if running on Mac + child_process.execSync("open -a \"Google Chrome\" http://localhost:3000"); + } else { + chromeLauncher.launch({ // eslint-disable-line @typescript-eslint/no-floating-promises + startingUrl: "http://localhost:3000", + chromeFlags, + }).then((val) => { DisplayPerfRpcInterface.chrome = val; }); + } + break; + case "edge": + child_process.execSync("start microsoft-edge:http://localhost:3000"); + break; + case "safari": + child_process.execSync("open -a Safari http://localhost:3000"); + break; + case "firefox": + if (process.platform === "darwin") { // Ie, if running on Mac + child_process.execSync("open -a firefox http://localhost:3000"); + } else { + child_process.execSync("start firefox http://localhost:3000"); + } + break; + } })(); diff --git a/test-apps/display-performance-test-app/src/common/npmCommands.ts b/test-apps/display-performance-test-app/src/common/npmCommands.ts index ce9dac0fb31..f571ba4640f 100644 --- a/test-apps/display-performance-test-app/src/common/npmCommands.ts +++ b/test-apps/display-performance-test-app/src/common/npmCommands.ts @@ -13,12 +13,28 @@ let browser = ""; for (let i = 2; i < process.argv.length; i++) { const curArg = process.argv[i]; args += `${curArg} `; - if (curArg === "chrome" || curArg === "edge" || curArg === "firefox") + if (curArg === "chrome" || curArg === "edge" || curArg === "firefox" || curArg === "safari") browser = curArg; } execSync(`npm run start:web ${args}`, { stdio: [0, 1, 2] }); -if (browser === "edge") - execSync("taskkill /f /im MicrosoftEdge.exe /t >nul"); -else if (browser === "firefox") - execSync("taskkill /f /im firefox.exe /t >nul"); +switch (browser) { + case "chrome": + if (process.platform === "darwin") { // Ie, if running on Mac + execSync("killall \"Google Chrome\""); + } + break; + case "edge": + execSync("taskkill /f /im msedge.exe /t >nul"); + break; + case "safari": + execSync("killall Safari"); + break; + case "firefox": + if (process.platform === "darwin") { // Ie, if running on Mac + execSync("killall firefox"); + } else { + execSync("taskkill /f /im firefox.exe /t >nul"); + } + break; +} diff --git a/test-apps/display-test-app/README.md b/test-apps/display-test-app/README.md index aeec97319f7..fb497775e63 100644 --- a/test-apps/display-test-app/README.md +++ b/test-apps/display-test-app/README.md @@ -201,6 +201,11 @@ display-test-app has access to all key-ins defined in the imodeljs-frontend and * `background=`: Preserve background color when drawing as a raster image. * **dta aspect skew decorator** *apply=0|1* - Toggle a decorator that draws a simple bspline curve based on the project extents, for testing the effect of aspect ratio skew on the curve stroke tolerance. Use in conjunction with `fdt aspect skew` to adjust the skew. If `apply` is 0, then the skew will have no effect on the curve's level of detail; otherwise a higher aspect ratio skew should produce higher-resolution curve graphics. * **dta classifyclip selected** *inside* - Color code elements from the current selection set based on their containment with the current view clip. Inside - Green, Outside - Red, Overlap - Blue. Specify optional inside arg to only determine inside or outside, not overlap. Disable clip in the view settings to select elements outside clip, use clip tool panel EDIT button to redisplay clip decoration after processing selection. Use key-in again without a clip or selection set to clear the color override. +* **dta grid settings** - Change the grid settings for the selected viewport. + * `spacing=number` Specify x and y grid reference line spacing in meters. + * `ratio=number` Specify y spacing as current x * ratio. + * `gridsPerRef=number` Specify number of grid lines to display per reference line. + * `orientation=0|1|2|3|4` Value for GridOrientationType. * **dta model transform** - Apply a display transform to all models currently displayed in the selected viewport. Origin is specified like `x=1 y=2 z=3`; pitch and roll as `p=45 r=90` in degrees. Any argument can be omitted. Omitting all arguments clears the display transform. Snapping intentionally does not take the display transform into account. ## Editing @@ -217,9 +222,7 @@ Using an editing session is optional, but outside of a session, the viewport's g ### Editing key-ins +display-test-app has access to all key-ins defined in the imodeljs-editor-frontend package. It also provides the following additional key-ins. + * `dta edit` - begin a new editing session, or end the current editing session. The title of the window or browser tab will update to reflect the current state: "[R/W]" indicating no current editing session, or "[EDIT]" indicating an active editing session. -* `dta delete elements` - delete all elements currently in the selection set. -* `dta move elements` - start moving elements. If no elements are currently in the selection set, you will be prompted to select one. First data point defines the start point; second defines the end point and moves the element(s) by the delta between the two points. * `dta place line string` - start placing a line string. Each data point defines another point in the string; a reset (right mouse button) finishes. The element is placed into the first spatial model and spatial category in the viewport's model and category selectors. -* `dta undo` - undo the most recent change. -* `dta redo` - redo the most recently-undone change. diff --git a/test-apps/display-test-app/package.json b/test-apps/display-test-app/package.json index 04ba1dc7d5e..7425de01238 100644 --- a/test-apps/display-test-app/package.json +++ b/test-apps/display-test-app/package.json @@ -19,7 +19,7 @@ "clean": "rimraf build lib .rush/temp/package-deps*.json", "copy:assets": "cpx \"./node_modules/@bentley/icons-generic-webfont/dist/**/*\" ./build", "docs": "", - "lint": "eslint -f visualstudio --config package.json --no-eslintrc ./src/**/*.ts 1>&2", + "lint": "eslint -f visualstudio --config package.json --no-eslintrc \"./src/**/*.ts\" 1>&2", "start": "cross-env NODE_ENV=development run-p start:webserver start:electron", "start:electron": "electron ./lib/backend/DtaElectronMain.js", "start:webserver": "cross-env BROWSER=none USE_FULL_SOURCEMAP=true DISABLE_ESLINT=true TRANSPILE_DEPS=false USE_FAST_SASS=true react-scripts start", @@ -37,34 +37,34 @@ "dependencies": { "@bentley/icons-generic": "^1.0.15", "@bentley/icons-generic-webfont": "^1.0.15", - "@bentley/frontend-devtools": "2.15.0-dev.6", - "@bentley/hypermodeling-frontend": "2.15.0-dev.6", - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/electron-manager": "2.15.0-dev.6", - "@bentley/mobile-manager": "2.15.0-dev.6", - "@bentley/frontend-authorization-client": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodelhub-client": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/backend-itwin-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", - "@bentley/imodeljs-quantity": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/imodeljs-markup": "2.15.0-dev.6", - "@bentley/imodeljs-editor-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-editor-backend": "2.15.0-dev.6", - "@bentley/imodeljs-editor-common": "2.15.0-dev.6", - "@bentley/rbac-client": "2.15.0-dev.6", - "@bentley/ui-abstract": "2.15.0-dev.6", - "@bentley/webgl-compatibility": "2.15.0-dev.6", + "@bentley/frontend-devtools": "2.15.0-dev.13", + "@bentley/hypermodeling-frontend": "2.15.0-dev.13", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/electron-manager": "2.15.0-dev.13", + "@bentley/mobile-manager": "2.15.0-dev.13", + "@bentley/frontend-authorization-client": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodelhub-client": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/backend-itwin-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", + "@bentley/imodeljs-quantity": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/imodeljs-markup": "2.15.0-dev.13", + "@bentley/imodeljs-editor-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-editor-backend": "2.15.0-dev.13", + "@bentley/imodeljs-editor-common": "2.15.0-dev.13", + "@bentley/rbac-client": "2.15.0-dev.13", + "@bentley/ui-abstract": "2.15.0-dev.13", + "@bentley/webgl-compatibility": "2.15.0-dev.13", "body-parser": "^1.18.2" }, "devDependencies": { - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@bentley/react-scripts": "3.4.9", "@types/body-parser": "^1.17.0", "@types/express": "^4.16.1", diff --git a/test-apps/display-test-app/public/locales/en/SVTTools.json b/test-apps/display-test-app/public/locales/en/SVTTools.json index 274fec0a9ee..0a57c0e67e0 100644 --- a/test-apps/display-test-app/public/locales/en/SVTTools.json +++ b/test-apps/display-test-app/public/locales/en/SVTTools.json @@ -10,6 +10,9 @@ "keyin": "dta classifyclip selected" } }, + "GridSettings": { + "keyin": "dta grid settings" + }, "Markup": { "keyin": "dta markup", "TestSelect": { @@ -106,6 +109,9 @@ "ApplyModelTransform": { "keyin": "dta model transform" }, + "ApplyModelDisplayScale": { + "keyin": "dta model scale" + }, "EditingSession": { "keyin": "dta edit" }, diff --git a/test-apps/display-test-app/src/backend/Backend.ts b/test-apps/display-test-app/src/backend/Backend.ts index a044386cd39..26c8645448a 100644 --- a/test-apps/display-test-app/src/backend/Backend.ts +++ b/test-apps/display-test-app/src/backend/Backend.ts @@ -245,18 +245,28 @@ export const initializeDtaBackend = async (electronHost?: ElectronHostOptions) = if (undefined !== logLevelEnv) logLevel = Logger.parseLogLevel(logLevelEnv); } + const opts = { + iModelHost, + electronHost, + nativeHost: { + applicationName: "display-test-app", + }, + mobileHost: { + rpcInterfaces: electronHost?.rpcInterfaces, + }, + }; /** register the implementation of our RPCs. */ RpcManager.registerImpl(DtaRpcInterface, DisplayTestAppRpc); if (ProcessDetector.isElectronAppBackend) { - await ElectronHost.startup({ electronHost, iModelHost }); + await ElectronHost.startup(opts); EditCommandAdmin.register(BasicManipulationCommand); } else if (ProcessDetector.isIOSAppBackend) { - await IOSHost.startup({ iModelHost }); + await IOSHost.startup(opts); } else if (ProcessDetector.isAndroidAppBackend) { - await AndroidHost.startup({ iModelHost }); + await AndroidHost.startup(opts); } else { - await LocalhostIpcHost.startup({ iModelHost }); + await LocalhostIpcHost.startup(opts); } // Set up logging (by default, no logging is enabled) diff --git a/test-apps/display-test-app/src/backend/DtaElectronMain.ts b/test-apps/display-test-app/src/backend/DtaElectronMain.ts index 4e35f14306f..27b1cd5d79d 100644 --- a/test-apps/display-test-app/src/backend/DtaElectronMain.ts +++ b/test-apps/display-test-app/src/backend/DtaElectronMain.ts @@ -9,24 +9,25 @@ import { dtaChannel, DtaIpcInterface } from "../common/DtaIpcInterface"; import { getRpcInterfaces, initializeDtaBackend } from "./Backend"; import { IpcHandler } from "@bentley/imodeljs-backend"; +const mainWindowName = "mainWindow"; const getWindowSize = () => { - let width = 1280; - let height = 800; const sizeStr = process.env.SVT_WINDOW_SIZE; if (typeof sizeStr === "string") { const parts = sizeStr.split(","); if (parts.length === 2) { - const w = Number.parseInt(parts[0], 10); - const h = Number.parseInt(parts[1], 10); + let width = Number.parseInt(parts[0], 10); + let height = Number.parseInt(parts[1], 10); - if (!Number.isNaN(w)) - width = w; + if (Number.isNaN(width)) + width = 1280; - if (!Number.isNaN(h)) - height = h; + if (Number.isNaN(height)) + height = 1024; + return { width, height, x: 100, y: 100 }; } } - return { width, height }; + + return ElectronHost.getWindowSizeSetting(mainWindowName); }; class DtaHandler extends IpcHandler implements DtaIpcInterface { @@ -52,11 +53,12 @@ const dtaElectronMain = async () => { await initializeDtaBackend(opts); - const autoOpenDevTools = (undefined === process.env.SVT_NO_DEV_TOOLS); - const maximizeWindow = (undefined === process.env.SVT_NO_MAXIMIZE_WINDOW); + // Restore previous window size, position and maximized state + const sizeAndPosition = getWindowSize(); + const maximizeWindow = undefined === sizeAndPosition || ElectronHost.getWindowMaximizedSetting(mainWindowName); // after backend is initialized, start display-test-app frontend process and open the window - await ElectronHost.openMainWindow({ ...getWindowSize(), show: !maximizeWindow, title: "Display Test App" }); + await ElectronHost.openMainWindow({ ...sizeAndPosition, show: !maximizeWindow, title: "Display Test App", storeWindowName: mainWindowName }); assert(ElectronHost.mainWindow !== undefined); if (maximizeWindow) { @@ -64,7 +66,7 @@ const dtaElectronMain = async () => { ElectronHost.mainWindow.show(); } - if (autoOpenDevTools) + if (undefined === process.env.SVT_NO_DEV_TOOLS) ElectronHost.mainWindow.webContents.toggleDevTools(); // Handle custom keyboard shortcuts diff --git a/test-apps/display-test-app/src/backend/SetToStandalone.ts b/test-apps/display-test-app/src/backend/SetToStandalone.ts index aa56cd4df3f..6435f9faeca 100644 --- a/test-apps/display-test-app/src/backend/SetToStandalone.ts +++ b/test-apps/display-test-app/src/backend/SetToStandalone.ts @@ -40,7 +40,7 @@ async function setToStandalone() { nativeDb.deleteAllTxns(); } - nativeDb.resetBriefcaseId(BriefcaseIdValue.Standalone); + nativeDb.resetBriefcaseId(BriefcaseIdValue.Unassigned); nativeDb.saveChanges(); nativeDb.closeIModel(); diff --git a/test-apps/display-test-app/src/frontend/App.ts b/test-apps/display-test-app/src/frontend/App.ts index 5c421e21eed..62e603e0dd7 100644 --- a/test-apps/display-test-app/src/frontend/App.ts +++ b/test-apps/display-test-app/src/frontend/App.ts @@ -25,6 +25,7 @@ import { DrawingAidTestTool } from "./DrawingAidTestTool"; import { EditingSessionTool, PlaceLineStringTool } from "./EditingTools"; import { FenceClassifySelectedTool } from "./Fence"; import { RecordFpsTool } from "./FpsMonitor"; +import { ChangeGridSettingsTool } from "./Grid"; import { IncidentMarkerDemoTool } from "./IncidentMarkerDemo"; import { MarkupSelectTestTool } from "./MarkupSelectTestTool"; import { Notifications } from "./Notifications"; @@ -38,6 +39,7 @@ import { import { TimePointComparisonTool } from "./TimePointComparison"; import { UiManager } from "./UiManager"; import { MarkupTool, ModelClipTool, SaveImageTool, ZoomToSelectedElementsTool } from "./Viewer"; +import { ApplyModelDisplayScaleTool } from "./DisplayScale"; class DisplayTestAppAccuSnap extends AccuSnap { private readonly _activeSnaps: SnapMode[] = [SnapMode.NearestKeypoint]; @@ -210,7 +212,9 @@ export class DisplayTestApp { const svtToolNamespace = IModelApp.i18n.registerNamespace("SVTTools"); [ + ApplyModelDisplayScaleTool, ApplyModelTransformTool, + ChangeGridSettingsTool, CloneViewportTool, CloseIModelTool, CloseWindowTool, diff --git a/test-apps/display-test-app/src/frontend/DisplayScale.ts b/test-apps/display-test-app/src/frontend/DisplayScale.ts new file mode 100644 index 00000000000..427deda4c4f --- /dev/null +++ b/test-apps/display-test-app/src/frontend/DisplayScale.ts @@ -0,0 +1,81 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Bentley Systems, Incorporated. All rights reserved. +* See LICENSE.md in the project root for license terms and full copyright notice. +*--------------------------------------------------------------------------------------------*/ + +import { Matrix3d, Point3d, Transform } from "@bentley/geometry-core"; +import { IModelApp, ModelDisplayTransformProvider, Tool } from "@bentley/imodeljs-frontend"; +import { parseArgs } from "@bentley/frontend-devtools"; + +class DisplayScaleTransformProvider implements ModelDisplayTransformProvider { + public constructor(private readonly _models: Set, private readonly _scaleTransform: Transform) { } + + public getModelDisplayTransform(modelId: string, baseTransform: Transform): Transform { + if (!this._models.has(modelId)) + return baseTransform; + + // Apply scale as last part of model to world transform. + return this._scaleTransform.multiplyTransformTransform(baseTransform); + } + + public get transform(): Transform { return this._scaleTransform.clone(); } +} + +/** Apply a display transform to all currently displayed models. */ +export class ApplyModelDisplayScaleTool extends Tool { + public static toolId = "ApplyModelDisplayScale"; + public static get minArgs() { return 0; } + public static get maxArgs() { return 3; } + + public run(scale: Point3d): boolean { + const vp = IModelApp.viewManager.selectedView; + if (!vp) + return false; + + const f = vp.getWorldFrustum(); + // If there was already a transform then we need to undo it for the frustum. + if (undefined !== vp.view.modelDisplayTransformProvider && vp.view.modelDisplayTransformProvider instanceof DisplayScaleTransformProvider) { + const t = vp.view.modelDisplayTransformProvider.transform; + const sx = t.matrix.getColumn(0).magnitude(); + const sy = t.matrix.getColumn(1).magnitude(); + const sz = t.matrix.getColumn(2).magnitude(); + const inverseMax = 1.0 / Math.max(sx, sy, sz); + const scaleFrustumInvTf = Transform.createRefs(Point3d.createZero(), Matrix3d.createScale(inverseMax, inverseMax, inverseMax)); + f.multiply(scaleFrustumInvTf); + } + + let scl; + let maxScale = 1.0; + if (scale.isAlmostEqual(Point3d.create(1.0, 1.0, 1.0))) { + if (undefined !== vp.view.modelDisplayTransformProvider) { + vp.view.modelDisplayTransformProvider = undefined; + } else { + return false; + } + scl = Matrix3d.createIdentity(); + } else { + scl = Matrix3d.createScale(scale.x, scale.y, scale.z); + maxScale = Math.max(scale.y, scale.y, scale.z); + } + + const models = new Set(); + vp.view.forEachModel((model) => models.add(model.id)); + + const sclTf = Transform.createRefs(Point3d.createZero(), scl); + const tp = new DisplayScaleTransformProvider(models, sclTf); + vp.setModelDisplayTransformProvider(tp); + + // Scale frustum uniformly using the largest of the scale values. + const scaleFrustumTf = Transform.createRefs(Point3d.createZero(), Matrix3d.createScale(maxScale, maxScale, maxScale)); + f.multiply(scaleFrustumTf); + vp.setupViewFromFrustum(f); + + return true; + } + + public parseAndRun(...input: string[]): boolean { + const args = parseArgs(input); + const scale = new Point3d(args.getFloat("x") ?? 1.0, args.getFloat("y") ?? 1.0, args.getFloat("z") ?? 1.0); + return this.run(scale); + } +} diff --git a/test-apps/display-test-app/src/frontend/FileOpen.ts b/test-apps/display-test-app/src/frontend/FileOpen.ts index 3bfde3c6b60..7c4fbe8e9cf 100644 --- a/test-apps/display-test-app/src/frontend/FileOpen.ts +++ b/test-apps/display-test-app/src/frontend/FileOpen.ts @@ -16,6 +16,7 @@ export async function selectFileName(selector: BrowserFileSelector | undefined): if (ProcessDetector.isElectronAppFrontend) { const opts: OpenDialogOptions = { properties: ["openFile"], + title: "Open iModel", filters: [{ name: "iModels", extensions: ["ibim", "bim"] }], }; diff --git a/test-apps/display-test-app/src/frontend/Grid.ts b/test-apps/display-test-app/src/frontend/Grid.ts new file mode 100644 index 00000000000..f99365bd5d9 --- /dev/null +++ b/test-apps/display-test-app/src/frontend/Grid.ts @@ -0,0 +1,86 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Bentley Systems, Incorporated. All rights reserved. +* See LICENSE.md in the project root for license terms and full copyright notice. +*--------------------------------------------------------------------------------------------*/ + +import { parseArgs } from "@bentley/frontend-devtools"; +import { GridOrientationType } from "@bentley/imodeljs-common"; +import { IModelApp, Tool } from "@bentley/imodeljs-frontend"; + +/** Change grid settings for testing. */ +export class ChangeGridSettingsTool extends Tool { + public static toolId = "GridSettings"; + public static get minArgs() { return 0; } + public static get maxArgs() { return 4; } + + public run(spacing?: number, ratio?: number, gridsPerRef?: number, orientation?: GridOrientationType): boolean { + const vp = IModelApp.viewManager.selectedView; + if (undefined === vp) + return false; + + if (undefined !== spacing) + vp.view.details.gridSpacing = { x: spacing, y: spacing }; + + if (undefined !== ratio) + vp.view.details.gridSpacing = { x: vp.view.details.gridSpacing.x, y: vp.view.details.gridSpacing.x * ratio }; + + if (undefined !== gridsPerRef) + vp.view.details.gridsPerRef = gridsPerRef; + + if (undefined !== orientation) + vp.view.details.gridOrientation = orientation; + + vp.invalidateScene(); // Needed to clear cached grid decoration... + return true; + } + + /** The keyin accepts the following arguments: + * - `spacing=number` Specify x and y grid reference line spacing in meters. + * - `ratio=number` Specify y spacing as current x * ratio. + * - `gridsPerRef=number` Specify number of grid lines to display per reference line. + * - `orientation=0|1|2|3|4` Value for GridOrientationType. + */ + public parseAndRun(...inputArgs: string[]): boolean { + let spacing; + let ratio; + let gridsPerRef; + let orientation; + const args = parseArgs(inputArgs); + + const spacingArg = args.getFloat("s"); + if (undefined !== spacingArg) + spacing = spacingArg; + + const ratioArg = args.getFloat("r"); + if (undefined !== ratioArg) + ratio = ratioArg; + + const gridsPerRefArg = args.getInteger("g"); + if (undefined !== gridsPerRefArg) + gridsPerRef = gridsPerRefArg; + + const orientationArg = args.getInteger("o"); + if (undefined !== orientationArg) { + switch (orientationArg) { + case 0: + orientation = GridOrientationType.View; + break; + case 1: + orientation = GridOrientationType.WorldXY; + break; + case 2: + orientation = GridOrientationType.WorldYZ; + break; + case 3: + orientation = GridOrientationType.WorldXZ; + break; + case 4: + orientation = GridOrientationType.AuxCoord; + break; + } + } + + this.run(spacing, ratio, gridsPerRef, orientation); + return true; + } +} diff --git a/test-apps/export-gltf/package.json b/test-apps/export-gltf/package.json index 6c97663bf41..f29fe060069 100644 --- a/test-apps/export-gltf/package.json +++ b/test-apps/export-gltf/package.json @@ -14,16 +14,16 @@ }, "repository": {}, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", "yargs": "^16.0.0" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/node": "10.14.1", "@types/yargs": "^12.0.5", "eslint": "^6.8.0", diff --git a/test-apps/export-obj/package.json b/test-apps/export-obj/package.json index e49ee031278..3d1a2b3a89e 100644 --- a/test-apps/export-obj/package.json +++ b/test-apps/export-obj/package.json @@ -14,16 +14,16 @@ }, "repository": {}, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", "yargs": "^16.0.0" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/node": "10.14.1", "@types/yargs": "^12.0.5", "eslint": "^6.8.0", diff --git a/test-apps/imjs-importer/package.json b/test-apps/imjs-importer/package.json index 0cc343984c8..32fce64dea2 100644 --- a/test-apps/imjs-importer/package.json +++ b/test-apps/imjs-importer/package.json @@ -24,21 +24,21 @@ "url": "http://www.bentley.com" }, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/backend-itwin-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", - "@bentley/logger-config": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/backend-itwin-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", + "@bentley/logger-config": "2.15.0-dev.13", "chai": "^4.1.2", "yargs": "^16.0.0" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", "@types/node": "10.14.1", diff --git a/test-apps/imodel-from-geojson/package.json b/test-apps/imodel-from-geojson/package.json index 38e7a0f2587..4b67e3e6f0b 100644 --- a/test-apps/imodel-from-geojson/package.json +++ b/test-apps/imodel-from-geojson/package.json @@ -14,18 +14,18 @@ }, "repository": {}, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", "fs-extra": "^8.1.0", "request-promise-native": "^1.0.5", "yargs": "^16.0.0" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/fs-extra": "^4.0.7", "@types/lodash": "^4.14.0", "@types/node": "10.14.1", diff --git a/test-apps/imodel-from-orbitgt/package.json b/test-apps/imodel-from-orbitgt/package.json index 694ea18b320..ed46909fd41 100644 --- a/test-apps/imodel-from-orbitgt/package.json +++ b/test-apps/imodel-from-orbitgt/package.json @@ -14,19 +14,19 @@ }, "repository": {}, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/orbitgt-core": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/orbitgt-core": "2.15.0-dev.13", "request-promise-native": "^1.0.5", "request": "^2.88.0", "yargs": "^16.0.0" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/fs-extra": "^4.0.7", "@types/lodash": "^4.14.0", "@types/node": "10.14.1", diff --git a/test-apps/imodel-from-reality-model/package.json b/test-apps/imodel-from-reality-model/package.json index 0feaeede328..9c3efc70e39 100644 --- a/test-apps/imodel-from-reality-model/package.json +++ b/test-apps/imodel-from-reality-model/package.json @@ -14,19 +14,19 @@ }, "repository": {}, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", "fs-extra": "^8.1.0", "request-promise-native": "^1.0.5", "request": "^2.88.0", "yargs": "^16.0.0" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/fs-extra": "^4.0.7", "@types/lodash": "^4.14.0", "@types/node": "10.14.1", diff --git a/test-apps/imodel-transformer/package.json b/test-apps/imodel-transformer/package.json index 298b1377ab2..9dadb972c60 100644 --- a/test-apps/imodel-transformer/package.json +++ b/test-apps/imodel-transformer/package.json @@ -9,24 +9,24 @@ "compile": "npm run build", "cover": "", "docs": "", - "lint": "eslint --max-warnings 0 ./src/**/*.ts 1>&2", + "lint": "eslint --max-warnings 0 \"./src/**/*.ts\" 1>&2", "start": "node ./lib/Main.js", "test": "betools test" }, "repository": {}, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", "fs-extra": "^8.1.0", "request-promise-native": "^1.0.5", "yargs": "^16.0.0" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", "@types/fs-extra": "^4.0.7", diff --git a/test-apps/ninezone-test-app/package.json b/test-apps/ninezone-test-app/package.json index 6251b547524..f191ea07d84 100644 --- a/test-apps/ninezone-test-app/package.json +++ b/test-apps/ninezone-test-app/package.json @@ -19,8 +19,8 @@ "url": "http://www.bentley.com" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@bentley/react-scripts": "3.4.9", "@types/classnames": "^2.2.3", "@types/react": "16.9.43", @@ -30,10 +30,10 @@ }, "dependencies": { "@bentley/icons-generic-webfont": "^1.0.15", - "@bentley/ui-abstract": "2.15.0-dev.6", - "@bentley/ui-components": "2.15.0-dev.6", - "@bentley/ui-core": "2.15.0-dev.6", - "@bentley/ui-ninezone": "2.15.0-dev.6", + "@bentley/ui-abstract": "2.15.0-dev.13", + "@bentley/ui-components": "2.15.0-dev.13", + "@bentley/ui-core": "2.15.0-dev.13", + "@bentley/ui-ninezone": "2.15.0-dev.13", "classnames": "^2.2.5", "highlight.js": "~10.6.0", "raf-schd": "^4.0.0", diff --git a/test-apps/presentation-test-app/package.json b/test-apps/presentation-test-app/package.json index 70953a8f46a..16923c75af1 100644 --- a/test-apps/presentation-test-app/package.json +++ b/test-apps/presentation-test-app/package.json @@ -20,7 +20,7 @@ "clean": "rimraf build lib .rush/temp/package-deps*.json", "docs": "npm run extract", "extract": "betools extract --fileExt=ts,tsx --extractFrom=./src --recursive --out=../../generated-docs/extract", - "lint": "eslint -f visualstudio --config package.json --no-eslintrc ./src/**/*.{ts,tsx} 1>&2", + "lint": "eslint -f visualstudio --config package.json --no-eslintrc \"./src/**/*.{ts,tsx}\" 1>&2", "electron": "run-p start:webserver start:electron", "start:electron": "electron ./lib/backend/main.js", "start:webserver": "cross-env BROWSER=none USE_FULL_SOURCEMAP=true DISABLE_ESLINT=true TRANSPILE_DEPS=false USE_FAST_SASS=true react-scripts start", @@ -30,32 +30,32 @@ "cover": "" }, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/electron-manager": "2.15.0-dev.6", - "@bentley/express-server": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/electron-manager": "2.15.0-dev.13", + "@bentley/express-server": "2.15.0-dev.13", "@bentley/icons-generic-webfont": "^1.0.15", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", - "@bentley/imodeljs-quantity": "2.15.0-dev.6", - "@bentley/presentation-common": "2.15.0-dev.6", - "@bentley/presentation-backend": "2.15.0-dev.6", - "@bentley/presentation-frontend": "2.15.0-dev.6", - "@bentley/presentation-components": "2.15.0-dev.6", - "@bentley/ui-core": "2.15.0-dev.6", - "@bentley/ui-components": "2.15.0-dev.6", - "@bentley/ui-abstract": "2.15.0-dev.6", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", + "@bentley/imodeljs-quantity": "2.15.0-dev.13", + "@bentley/presentation-common": "2.15.0-dev.13", + "@bentley/presentation-backend": "2.15.0-dev.13", + "@bentley/presentation-frontend": "2.15.0-dev.13", + "@bentley/presentation-components": "2.15.0-dev.13", + "@bentley/ui-core": "2.15.0-dev.13", + "@bentley/ui-components": "2.15.0-dev.13", + "@bentley/ui-abstract": "2.15.0-dev.13", "react": "^16.8.0", "react-dom": "^16.8.0", "react-select": "3.1.0", "semver": "^5.5.0" }, "devDependencies": { - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@bentley/react-scripts": "3.4.9", "@types/bunyan": "^1.8.4", "@types/react": "16.9.43", diff --git a/test-apps/presentation-test-app/src/backend/SampleIpcHandler.ts b/test-apps/presentation-test-app/src/backend/SampleIpcHandler.ts new file mode 100644 index 00000000000..e0dbc954e6c --- /dev/null +++ b/test-apps/presentation-test-app/src/backend/SampleIpcHandler.ts @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Bentley Systems, Incorporated. All rights reserved. +* See LICENSE.md in the project root for license terms and full copyright notice. +*--------------------------------------------------------------------------------------------*/ +import { Id64Arg } from "@bentley/bentleyjs-core"; +import { IModelDb, IpcHandler } from "@bentley/imodeljs-backend"; +import { ElementProps } from "@bentley/imodeljs-common"; +import { PRESENTATION_TEST_APP_IPC_CHANNEL_NAME, SampleIpcInterface } from "../common/SampleIpcInterface"; + +/** @internal */ +export class SampleIpcHandler extends IpcHandler implements SampleIpcInterface { + public channelName = PRESENTATION_TEST_APP_IPC_CHANNEL_NAME; + + public async deleteElements(imodelKey: string, elementIds: Id64Arg): Promise { + const iModelDb = IModelDb.tryFindByKey(imodelKey); + if (!iModelDb) + return; + + iModelDb.elements.deleteElement(elementIds); + iModelDb.saveChanges(); + } + + public async updateElement(imodelKey: string, newProps: ElementProps): Promise { + const iModelDb = IModelDb.tryFindByKey(imodelKey); + if (!iModelDb) + return; + + iModelDb.elements.updateElement(newProps); + iModelDb.saveChanges(); + } +} diff --git a/test-apps/presentation-test-app/src/backend/electron/ElectronMain.ts b/test-apps/presentation-test-app/src/backend/electron/ElectronMain.ts index 879554ca48e..8bd3670d09b 100644 --- a/test-apps/presentation-test-app/src/backend/electron/ElectronMain.ts +++ b/test-apps/presentation-test-app/src/backend/electron/ElectronMain.ts @@ -5,6 +5,7 @@ import * as path from "path"; import { ElectronHost, ElectronHostOptions } from "@bentley/electron-manager/lib/ElectronBackend"; import { RpcInterfaceDefinition } from "@bentley/imodeljs-common"; +import { SampleIpcHandler } from "../SampleIpcHandler"; /** * Initializes Electron backend @@ -18,6 +19,7 @@ export default async function initialize(rpcInterfaces: RpcInterfaceDefinition[] webResourcesPath: path.join(__dirname, "..", "..", "..", "build"), rpcInterfaces, developmentServer: process.env.NODE_ENV === "development", + ipcHandlers: [SampleIpcHandler], }; await ElectronHost.startup({ electronHost }); diff --git a/test-apps/presentation-test-app/src/backend/main.ts b/test-apps/presentation-test-app/src/backend/main.ts index 52f7adddc70..a999d7d0990 100644 --- a/test-apps/presentation-test-app/src/backend/main.ts +++ b/test-apps/presentation-test-app/src/backend/main.ts @@ -34,11 +34,12 @@ import { PresentationBackendLoggerCategory, PresentationBackendNativeLoggerCateg Presentation.initialize({ rulesetDirectories: [path.join("assets", "presentation_rules")], localeDirectories: [path.join("assets", "locales")], - mode: PresentationManagerMode.ReadOnly, + mode: PresentationManagerMode.ReadWrite, taskAllocationsMap: { [RequestPriority.Max]: 1, }, useMmap: true, + updatesPollInterval: 20, }); // __PUBLISH_EXTRACT_END__ diff --git a/test-apps/presentation-test-app/src/common/SampleIpcInterface.ts b/test-apps/presentation-test-app/src/common/SampleIpcInterface.ts new file mode 100644 index 00000000000..5443f7f6fb8 --- /dev/null +++ b/test-apps/presentation-test-app/src/common/SampleIpcInterface.ts @@ -0,0 +1,17 @@ + +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Bentley Systems, Incorporated. All rights reserved. +* See LICENSE.md in the project root for license terms and full copyright notice. +*--------------------------------------------------------------------------------------------*/ + +import { Id64Arg } from "@bentley/bentleyjs-core"; +import { ElementProps } from "@bentley/imodeljs-common"; + +/** @internal */ +export const PRESENTATION_TEST_APP_IPC_CHANNEL_NAME = "presentation-test-app-ipc-interface"; + +/** @internal */ +export interface SampleIpcInterface { + updateElement(imodelKey: string, newProps: ElementProps): Promise; + deleteElements(imodelKey: string, elementIds: Id64Arg): Promise; +} diff --git a/test-apps/presentation-test-app/src/frontend/api/MyAppFrontend.ts b/test-apps/presentation-test-app/src/frontend/api/MyAppFrontend.ts index b7e7343605a..053a1e4f22a 100644 --- a/test-apps/presentation-test-app/src/frontend/api/MyAppFrontend.ts +++ b/test-apps/presentation-test-app/src/frontend/api/MyAppFrontend.ts @@ -2,9 +2,10 @@ * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import { Guid, Logger } from "@bentley/bentleyjs-core"; -import { ViewQueryParams } from "@bentley/imodeljs-common"; -import { IModelConnection, SnapshotConnection } from "@bentley/imodeljs-frontend"; +import { Guid, Id64Arg, Logger, OpenMode } from "@bentley/bentleyjs-core"; +import { ElementProps, IModelError, ViewQueryParams } from "@bentley/imodeljs-common"; +import { AsyncMethodsOf, BriefcaseConnection, IModelConnection, IpcApp, PromiseReturnType, SnapshotConnection } from "@bentley/imodeljs-frontend"; +import { PRESENTATION_TEST_APP_IPC_CHANNEL_NAME, SampleIpcInterface } from "../../common/SampleIpcInterface"; import SampleRpcInterface from "../../common/SampleRpcInterface"; export class MyAppFrontend { @@ -19,8 +20,17 @@ export class MyAppFrontend { } public static async openIModel(path: string): Promise { - this.iModel = await SnapshotConnection.openFile(path); - Logger.logInfo("presentation", `Opened: ${this.iModel.name}`); + if (IpcApp.isValid) { + Logger.logInfo("presentation", `Trying to open standalone ${path}`); + this.iModel = await tryOpenStandalone(path); + } + + if (!this.iModel) { + Logger.logInfo("presentation", `Opening snapshot: ${path}`); + this.iModel = await SnapshotConnection.openFile(path); + Logger.logInfo("presentation", `Opened snapshot: ${this.iModel.name}`); + } + return this.iModel; } @@ -45,4 +55,35 @@ export class MyAppFrontend { label: spec.userLabel ?? spec.code.value!, })); } + + public static async updateElement(imodel: IModelConnection, newProps: ElementProps) { + if (!IpcApp.isValid) + throw new Error(`Updating element only supported in 'IpcApp'`); + return this.callIpc("updateElement", imodel.key, newProps); + } + + public static async deleteElements(imodel: IModelConnection, elementIds: Id64Arg) { + if (!IpcApp.isValid) + throw new Error(`Deleting elements only supported in 'IpcApp'`); + return this.callIpc("deleteElements", imodel.key, elementIds); + } + + private static async callIpc>(methodName: T, ...args: Parameters): Promise> { + return IpcApp.callIpcChannel(PRESENTATION_TEST_APP_IPC_CHANNEL_NAME, methodName, ...args); + } +} + +async function tryOpenStandalone(path: string) { + let iModel: IModelConnection | undefined; + try { + iModel = await BriefcaseConnection.openStandalone(path, OpenMode.ReadWrite); + Logger.logInfo("presentation", `Opened standalone: ${iModel.name}`); + } catch (err) { + if (err instanceof IModelError) { + Logger.logError("presentation", `Failed to open standalone: ${err.message}`, err.getMetaData); + } else { + Logger.logError("presentation", `Failed to open standalone.`); + } + } + return iModel; } diff --git a/test-apps/synchro-schedule-importer/package.json b/test-apps/synchro-schedule-importer/package.json index 7ab1d54a60a..8d090da0274 100644 --- a/test-apps/synchro-schedule-importer/package.json +++ b/test-apps/synchro-schedule-importer/package.json @@ -16,17 +16,17 @@ }, "repository": {}, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", "fs-extra": "^8.1.0", "yargs": "^16.0.0" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/fs-extra": "^4.0.7", "@types/lodash": "^4.14.0", "@types/node": "10.14.1", diff --git a/test-apps/ui-test-app/package.json b/test-apps/ui-test-app/package.json index d63724bbbca..acdec80cf91 100644 --- a/test-apps/ui-test-app/package.json +++ b/test-apps/ui-test-app/package.json @@ -12,7 +12,7 @@ "build:frontend": "npm run pseudolocalize && cross-env GENERATE_SOURCEMAP=false DISABLE_ESLINT=true TRANSPILE_DEPS=false USE_FAST_SASS=true DISABLE_TERSER=true react-scripts build", "clean": "rimraf lib build .rush/temp/package-deps*.json", "copy:assets": "cpx ./src/backend/web/BackendServer.config.json ./lib/backend/web", - "lint": "eslint -f visualstudio --config package.json --no-eslintrc ./src/**/*.{ts,tsx} 1>&2", + "lint": "eslint -f visualstudio --config package.json --no-eslintrc \"./src/**/*.{ts,tsx}\" 1>&2", "start": "run-p start:webserver start:electron", "start:electron": "electron ./lib/backend/main.js", "start:electron:prod": "cross-env NODE_ENV=production electron ./lib/backend/main.js", @@ -44,12 +44,12 @@ ], "devDependencies": { "@axe-core/react": "^4.1.1", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/logger-config": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/imodeljs-markup": "2.15.0-dev.6", - "@bentley/frontend-devtools": "2.15.0-dev.6", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/logger-config": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/imodeljs-markup": "2.15.0-dev.13", + "@bentley/frontend-devtools": "2.15.0-dev.13", "@bentley/react-scripts": "3.4.9", "@types/classnames": "^2.2.3", "@types/lorem-ipsum": "^1.0.2", @@ -76,43 +76,43 @@ "semver": "^5.5.0" }, "dependencies": { - "@bentley/backend-application-insights-client": "2.15.0-dev.6", - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/context-registry-client": "2.15.0-dev.6", - "@bentley/electron-manager": "2.15.0-dev.6", - "@bentley/mobile-manager": "2.15.0-dev.6", - "@bentley/express-server": "2.15.0-dev.6", - "@bentley/frontend-application-insights-client": "2.15.0-dev.6", - "@bentley/frontend-authorization-client": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", + "@bentley/backend-application-insights-client": "2.15.0-dev.13", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/context-registry-client": "2.15.0-dev.13", + "@bentley/electron-manager": "2.15.0-dev.13", + "@bentley/mobile-manager": "2.15.0-dev.13", + "@bentley/express-server": "2.15.0-dev.13", + "@bentley/frontend-application-insights-client": "2.15.0-dev.13", + "@bentley/frontend-authorization-client": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", "@bentley/icons-generic-webfont": "^1.0.15", "@bentley/icons-generic": "^1.0.15", - "@bentley/imodelhub-client": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/backend-itwin-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/frontend-devtools": "2.15.0-dev.6", - "@bentley/hypermodeling-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-markup": "2.15.0-dev.6", - "@bentley/imodeljs-quantity": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/imodeljs-editor-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-editor-backend": "2.15.0-dev.6", - "@bentley/imodeljs-editor-common": "2.15.0-dev.6", - "@bentley/presentation-backend": "2.15.0-dev.6", - "@bentley/presentation-common": "2.15.0-dev.6", - "@bentley/presentation-components": "2.15.0-dev.6", - "@bentley/presentation-frontend": "2.15.0-dev.6", - "@bentley/projectshare-client": "2.15.0-dev.6", - "@bentley/rbac-client": "2.15.0-dev.6", - "@bentley/ui-abstract": "2.15.0-dev.6", - "@bentley/ui-core": "2.15.0-dev.6", - "@bentley/ui-components": "2.15.0-dev.6", - "@bentley/ui-ninezone": "2.15.0-dev.6", - "@bentley/ui-framework": "2.15.0-dev.6", - "@bentley/map-layers": "2.15.0-dev.6", + "@bentley/imodelhub-client": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/backend-itwin-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/frontend-devtools": "2.15.0-dev.13", + "@bentley/hypermodeling-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-markup": "2.15.0-dev.13", + "@bentley/imodeljs-quantity": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/imodeljs-editor-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-editor-backend": "2.15.0-dev.13", + "@bentley/imodeljs-editor-common": "2.15.0-dev.13", + "@bentley/presentation-backend": "2.15.0-dev.13", + "@bentley/presentation-common": "2.15.0-dev.13", + "@bentley/presentation-components": "2.15.0-dev.13", + "@bentley/presentation-frontend": "2.15.0-dev.13", + "@bentley/projectshare-client": "2.15.0-dev.13", + "@bentley/rbac-client": "2.15.0-dev.13", + "@bentley/ui-abstract": "2.15.0-dev.13", + "@bentley/ui-core": "2.15.0-dev.13", + "@bentley/ui-components": "2.15.0-dev.13", + "@bentley/ui-ninezone": "2.15.0-dev.13", + "@bentley/ui-framework": "2.15.0-dev.13", + "@bentley/map-layers": "2.15.0-dev.13", "classnames": "^2.2.5", "lorem-ipsum": "^2.0.3", "mobx": "^5.8.0", diff --git a/test-apps/ui-test-app/public/index.html b/test-apps/ui-test-app/public/index.html index 5f41f564266..5f41375c29e 100644 --- a/test-apps/ui-test-app/public/index.html +++ b/test-apps/ui-test-app/public/index.html @@ -1,49 +1,50 @@ - - - - - - - - iModel.js UI Test App - - - + - - + + - - -

- - + \ No newline at end of file diff --git a/test-apps/ui-test-app/public/locales/en/SampleApp.json b/test-apps/ui-test-app/public/locales/en/SampleApp.json index da0f6afc417..5a5a7a5b4cf 100644 --- a/test-apps/ui-test-app/public/locales/en/SampleApp.json +++ b/test-apps/ui-test-app/public/locales/en/SampleApp.json @@ -28,26 +28,8 @@ "loading": "Loading..." }, "settingsStage": { - "settings": "Settings", - "light": "Light", - "dark": "Dark", - "systemPreferred": "System preferred", - "themeTitle": "Theme", - "themeDescription": "Toggle the theme between light and dark", - "autoHideTitle": "Auto-Hide UI", - "autoHideDescription": "Toggle the auto-hide of UI after inactivity", - "dragInteractionTitle": "Drag Interaction", - "dragInteractionDescription": "Toggle the toolbar interaction between click and drag", - "newUiTitle": "Use 2.0 Ui", - "newUiDescription": "Use new 2.0 UI layout", - "useProximityOpacityTitle": "Change Toolbar Opacity", - "useProximityOpacityDescription": "Change the toolbar opacity as the mouse gets closer or farther away", - "snapWidgetOpacityTitle": "Snap Widget Opacity", - "snapWidgetOpacityDescription": "Immediately change the toolbar opacity when the mouse gets close", "accuDrawNotificationsTitle": "Display AccuDraw Notifications", - "accuDrawNotificationsDescription": "Display toast notifications when AccuDraw status changes", - "widgetOpacityTitle": "Widget Opacity", - "widgetOpacityDescription": "Opacity when mouse is not hovering for 2.0 floating widgets and all 1.0 widgets" + "accuDrawNotificationsDescription": "Display toast notifications when AccuDraw status changes" }, "QuantityFormatModalFrontstage": { "QuantityFormatStage": "Quantity Format Overrides" @@ -212,7 +194,8 @@ "Coordinate": "Coordinate", "Length": "Length", "Angle": "Angle", - "ImageCheckBox": "Image Check Box" + "ImageCheckBox": "Image Check Box", + "Number": "Number" }, "Options": { "Red": "Red", diff --git a/test-apps/ui-test-app/src/backend/electron/ElectronMain.ts b/test-apps/ui-test-app/src/backend/electron/ElectronMain.ts index a330fb0379a..c9a318a47b4 100644 --- a/test-apps/ui-test-app/src/backend/electron/ElectronMain.ts +++ b/test-apps/ui-test-app/src/backend/electron/ElectronMain.ts @@ -2,26 +2,30 @@ * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import * as path from "path"; + +import { join } from "path"; import { assert } from "@bentley/bentleyjs-core"; -import { ElectronHost, ElectronHostOptions } from "@bentley/electron-manager/lib/ElectronBackend"; -import { getSupportedRpcs } from "../../common/rpcs"; +import { ElectronHost } from "@bentley/electron-manager/lib/ElectronBackend"; import { BasicManipulationCommand, EditCommandAdmin } from "@bentley/imodeljs-editor-backend"; +import { getSupportedRpcs } from "../../common/rpcs"; -/** - * Initializes Electron backend - */ -const autoOpenDevTools = (undefined === process.env.SVT_NO_DEV_TOOLS); -const maximizeWindow = (undefined === process.env.SVT_NO_MAXIMIZE_WINDOW); +const mainWindowName = "mainWindow"; +/** Initializes Electron backend */ export async function initializeElectron() { - const electronHost: ElectronHostOptions = { - webResourcesPath: path.join(__dirname, "..", "..", "..", "build"), - rpcInterfaces: getSupportedRpcs(), - developmentServer: process.env.NODE_ENV === "development", + + const opt = { + electronHost: { + webResourcesPath: join(__dirname, "..", "..", "..", "build"), + developmentServer: process.env.NODE_ENV === "development", + rpcInterfaces: getSupportedRpcs(), + }, + nativeHost: { + applicationName: "ui-test-app", + }, }; - await ElectronHost.startup({ electronHost }); + await ElectronHost.startup(opt); EditCommandAdmin.register(BasicManipulationCommand); // Handle custom keyboard shortcuts @@ -37,13 +41,18 @@ export async function initializeElectron() { }); }); - await ElectronHost.openMainWindow({ width: 800, height: 650, show: !maximizeWindow, title: "Ui Test App" }); + // Restore previous window size, position and maximized state + const sizeAndPosition = ElectronHost.getWindowSizeSetting(mainWindowName); + const maximizeWindow = undefined === sizeAndPosition || ElectronHost.getWindowMaximizedSetting(mainWindowName); + + await ElectronHost.openMainWindow({ ...sizeAndPosition, show: !maximizeWindow, title: "Ui Test App", storeWindowName: mainWindowName }); assert(ElectronHost.mainWindow !== undefined); if (maximizeWindow) { ElectronHost.mainWindow.maximize(); // maximize before showing to avoid resize event on startup ElectronHost.mainWindow.show(); } - if (autoOpenDevTools) + + if ((undefined === process.env.imjs_TESTAPP_NO_DEV_TOOLS)) ElectronHost.mainWindow.webContents.toggleDevTools(); } diff --git a/test-apps/ui-test-app/src/backend/mobile/MobileMain.ts b/test-apps/ui-test-app/src/backend/mobile/MobileMain.ts index dced0f3e1ae..a1db7f5746f 100644 --- a/test-apps/ui-test-app/src/backend/mobile/MobileMain.ts +++ b/test-apps/ui-test-app/src/backend/mobile/MobileMain.ts @@ -7,7 +7,7 @@ import * as fs from "fs"; import * as path from "path"; import { Logger, LogLevel, ProcessDetector } from "@bentley/bentleyjs-core"; import { IModelReadRpcInterface, IModelTileRpcInterface, SnapshotIModelRpcInterface } from "@bentley/imodeljs-common"; -import { AndroidHost, IOSHost, MobileRpcManager } from "@bentley/mobile-manager/lib/MobileBackend"; +import { AndroidHost, IOSHost } from "@bentley/mobile-manager/lib/MobileBackend"; import { Presentation } from "@bentley/presentation-backend"; import { PresentationRpcInterface } from "@bentley/presentation-common"; @@ -22,11 +22,18 @@ import { PresentationRpcInterface } from "@bentley/presentation-common"; Logger.initializeToConsole(); Logger.setLevelDefault(LogLevel.Trace); + const rpcInterfaces = [ + IModelReadRpcInterface, + IModelTileRpcInterface, + SnapshotIModelRpcInterface, + PresentationRpcInterface, + ]; + // initialize imodeljs-backend if (ProcessDetector.isIOSAppBackend) - await IOSHost.startup(); + await IOSHost.startup({ mobileHost: { rpcInterfaces } }); else - await AndroidHost.startup(); + await AndroidHost.startup({ mobileHost: { rpcInterfaces } }); // initialize presentation-backend Presentation.initialize({ @@ -37,12 +44,6 @@ import { PresentationRpcInterface } from "@bentley/presentation-common"; updatesPollInterval: 100, }); - MobileRpcManager.initializeImpl([ - IModelReadRpcInterface, - IModelTileRpcInterface, - SnapshotIModelRpcInterface, - PresentationRpcInterface, - ]); } catch (error) { Logger.logError("ui-test-app", error); process.exitCode = 1; diff --git a/test-apps/ui-test-app/src/frontend/AppUiSettings.ts b/test-apps/ui-test-app/src/frontend/AppUiSettings.ts deleted file mode 100644 index b74d712ac4b..00000000000 --- a/test-apps/ui-test-app/src/frontend/AppUiSettings.ts +++ /dev/null @@ -1,64 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Bentley Systems, Incorporated. All rights reserved. -* See LICENSE.md in the project root for license terms and full copyright notice. -*--------------------------------------------------------------------------------------------*/ -import { UiSetting, UiSettings } from "@bentley/ui-core"; -import { FrameworkAccuDraw, UiFramework, UiShowHideManager } from "@bentley/ui-framework"; -import { SampleAppIModelApp, SampleAppUiActionId } from "."; - -export class AppUiSettings { - private static _settingNamespace = "AppUiSettings"; - private _settings: Array> = []; - - public colorTheme: UiSetting; - public autoHideUi: UiSetting; - public useProximityOpacity: UiSetting; - public snapWidgetOpacity: UiSetting; - public dragInteraction: UiSetting; - public frameworkVersion: UiSetting; - public accuDrawNotifications: UiSetting; - public widgetOpacity: UiSetting; - - constructor() { - this._settings = []; - - this.colorTheme = new UiSetting(AppUiSettings._settingNamespace, "ColorTheme", UiFramework.getColorTheme, UiFramework.setColorTheme); - this._settings.push(this.colorTheme); - - this.autoHideUi = new UiSetting(AppUiSettings._settingNamespace, "AutoHideUi", - () => UiShowHideManager.autoHideUi, (value: boolean) => UiShowHideManager.autoHideUi = value); - this._settings.push(this.autoHideUi); - - this.useProximityOpacity = new UiSetting(AppUiSettings._settingNamespace, "UseProximityOpacity", - () => UiShowHideManager.useProximityOpacity, (value: boolean) => UiShowHideManager.useProximityOpacity = value); - this._settings.push(this.useProximityOpacity); - - this.snapWidgetOpacity = new UiSetting(AppUiSettings._settingNamespace, "SnapWidgetOpacity", - () => UiShowHideManager.snapWidgetOpacity, (value: boolean) => UiShowHideManager.snapWidgetOpacity = value); - this._settings.push(this.snapWidgetOpacity); - - this.dragInteraction = new UiSetting(AppUiSettings._settingNamespace, "DragInteraction", - () => SampleAppIModelApp.store.getState().sampleAppState.dragInteraction, - (value: boolean) => UiFramework.dispatchActionToStore(SampleAppUiActionId.setDragInteraction, value, true)); - this._settings.push(this.dragInteraction); - - this.frameworkVersion = new UiSetting(AppUiSettings._settingNamespace, "FrameworkVersion", - () => SampleAppIModelApp.store.getState().sampleAppState.frameworkVersion, - (value: string) => UiFramework.dispatchActionToStore(SampleAppUiActionId.setFrameworkVersion, value, true)); - this._settings.push(this.frameworkVersion); - - this.accuDrawNotifications = new UiSetting(AppUiSettings._settingNamespace, "AccuDrawNotifications", - () => FrameworkAccuDraw.displayNotifications, (value: boolean) => FrameworkAccuDraw.displayNotifications = value); - this._settings.push(this.accuDrawNotifications); - - this.widgetOpacity = new UiSetting(AppUiSettings._settingNamespace, "WidgetOpacity", - () => UiFramework.getWidgetOpacity(), (value: number) => UiFramework.setWidgetOpacity(value)); - this._settings.push(this.widgetOpacity); - } - - public async apply(uiSettings: UiSettings): Promise { - for (const setting of this._settings) { - await setting.getSettingAndApplyValue(uiSettings); - } - } -} diff --git a/test-apps/ui-test-app/src/frontend/appui/AppUi.tsx b/test-apps/ui-test-app/src/frontend/appui/AppUi.tsx index f42ebeddd50..9c37df06521 100644 --- a/test-apps/ui-test-app/src/frontend/appui/AppUi.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/AppUi.tsx @@ -21,7 +21,7 @@ import "./tooluiproviders/Tool2UiProvider"; import "./statusbars/AppStatusBar"; import "./navigationaids/CubeExampleNavigationAid"; import * as React from "react"; -import { BadgeType, FunctionKey, StagePanelLocation, StageUsage } from "@bentley/ui-abstract"; +import { BadgeType, FunctionKey, StagePanelLocation, StageUsage, WidgetState } from "@bentley/ui-abstract"; import { FillCentered } from "@bentley/ui-core"; import { AccuDrawCommandItems, @@ -40,7 +40,6 @@ import { UiFramework, WidgetDef, WidgetProvider, - WidgetState, WorkflowProps, WorkflowPropsList, ZoneLocation, @@ -488,6 +487,6 @@ export class AppUi { yLabel: "-Y-", }; - FrameworkAccuDraw.uiSettings = {...appSettings, ...userSettings}; + FrameworkAccuDraw.uiSettings = { ...appSettings, ...userSettings }; } } diff --git a/test-apps/ui-test-app/src/frontend/appui/backstage/AppBackstageComposer.tsx b/test-apps/ui-test-app/src/frontend/appui/backstage/AppBackstageComposer.tsx index eb777f3c98c..53d3f7eec34 100644 --- a/test-apps/ui-test-app/src/frontend/appui/backstage/AppBackstageComposer.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/backstage/AppBackstageComposer.tsx @@ -7,7 +7,7 @@ import { connect } from "react-redux"; import { UserInfo } from "@bentley/itwin-client"; import { IModelApp } from "@bentley/imodeljs-frontend"; import { BackstageItemUtilities, BadgeType, ConditionalBooleanValue } from "@bentley/ui-abstract"; -import { BackstageComposer, FrontstageManager, SettingsModalFrontstage, UserProfileBackstageItem } from "@bentley/ui-framework"; +import { BackstageComposer, FrontstageManager, SettingsModalFrontstage, UiFramework, UserProfileBackstageItem } from "@bentley/ui-framework"; import { ComponentExamplesModalFrontstage } from "../frontstages/component-examples/ComponentExamples"; import { LocalFileOpenFrontstage } from "../frontstages/LocalFileStage"; import { RootState, SampleAppIModelApp, SampleAppUiActionId } from "../.."; @@ -33,7 +33,7 @@ interface AppBackstageComposerProps { export function AppBackstageComposerComponent({ userInfo }: AppBackstageComposerProps) { const hiddenCondition3 = new ConditionalBooleanValue(() => SampleAppIModelApp.getTestProperty() === "HIDE", [SampleAppUiActionId.setTestProperty]); const enableCondition = new ConditionalBooleanValue(() => SampleAppIModelApp.getTestProperty() === "HIDE", [SampleAppUiActionId.setTestProperty]); - const notUi2Condition = new ConditionalBooleanValue(() => SampleAppIModelApp.getUiFrameworkProperty() === "1", [SampleAppUiActionId.toggleFrameworkVersion, SampleAppUiActionId.setFrameworkVersion]); + const notUi2Condition = new ConditionalBooleanValue(() => UiFramework.uiVersion === "1", ["configurableui:set-framework-version"]); const imodelIndexHidden = new ConditionalBooleanValue(() => SampleAppIModelApp.isIModelLocal, [SampleAppUiActionId.setIsIModelLocal]); const openLocalFileHidden = new ConditionalBooleanValue(() => SampleAppIModelApp.testAppConfiguration?.snapshotPath === undefined, [SampleAppUiActionId.setIsIModelLocal]); @@ -44,7 +44,7 @@ export function AppBackstageComposerComponent({ userInfo }: AppBackstageComposer BackstageItemUtilities.createStageLauncher("IModelOpen", 300, 10, IModelApp.i18n.translate("SampleApp:backstage.imodelopen"), undefined, "icon-folder-opened"), BackstageItemUtilities.createStageLauncher("IModelIndex", 300, 20, IModelApp.i18n.translate("SampleApp:backstage.imodelindex"), undefined, "icon-placeholder", { isHidden: imodelIndexHidden }), BackstageItemUtilities.createActionItem("SampleApp.open-local-file", 300, 30, async () => LocalFileOpenFrontstage.open(), IModelApp.i18n.translate("SampleApp:backstage:fileSelect"), undefined, "icon-placeholder", { isHidden: openLocalFileHidden }), - SettingsModalFrontstage.getBackstageActionItem (400, 10), + SettingsModalFrontstage.getBackstageActionItem(400, 10), ]; } @@ -58,7 +58,7 @@ export function AppBackstageComposerComponent({ userInfo }: AppBackstageComposer BackstageItemUtilities.createStageLauncher("IModelOpen", 300, 10, IModelApp.i18n.translate("SampleApp:backstage.imodelopen"), undefined, "icon-folder-opened"), BackstageItemUtilities.createStageLauncher("IModelIndex", 300, 20, IModelApp.i18n.translate("SampleApp:backstage.imodelindex"), undefined, "icon-placeholder", { isHidden: imodelIndexHidden }), BackstageItemUtilities.createActionItem("SampleApp.open-local-file", 300, 30, async () => LocalFileOpenFrontstage.open(), IModelApp.i18n.translate("SampleApp:backstage:fileSelect"), undefined, "icon-placeholder", { isHidden: openLocalFileHidden }), - SettingsModalFrontstage.getBackstageActionItem (400, 10), + SettingsModalFrontstage.getBackstageActionItem(400, 10), BackstageItemUtilities.createActionItem("SampleApp.componentExamples", 400, 20, () => FrontstageManager.openModalFrontstage(new ComponentExamplesModalFrontstage()), IModelApp.i18n.translate("SampleApp:backstage.componentExamples"), undefined, "icon-details", { badgeType: BadgeType.New }), ]; }); diff --git a/test-apps/ui-test-app/src/frontend/appui/frontstages/Frontstage1.tsx b/test-apps/ui-test-app/src/frontend/appui/frontstages/Frontstage1.tsx index dde5eb389a5..6bc9053d50d 100644 --- a/test-apps/ui-test-app/src/frontend/appui/frontstages/Frontstage1.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/frontstages/Frontstage1.tsx @@ -6,7 +6,7 @@ import * as React from "react"; import { PlaybackSettings, TimelineComponent, TimelinePausePlayAction, TimelinePausePlayArgs } from "@bentley/ui-components"; import { ActionItemButton, CommandItemDef, ContentLayoutManager, CoreTools, Frontstage, FrontstageDef, FrontstageManager, FrontstageProps, FrontstageProvider, GroupButton, - NavigationWidget, StagePanel, ToolButton, ToolWidget, useWidgetDirection, Widget, WidgetState, WidgetStateChangedEventArgs, Zone, ZoneLocation, + NavigationWidget, StagePanel, ToolButton, ToolWidget, useWidgetDirection, Widget, WidgetStateChangedEventArgs, Zone, ZoneLocation, ZoneState, } from "@bentley/ui-framework"; import { Direction, Toolbar } from "@bentley/ui-ninezone"; @@ -15,7 +15,7 @@ import { SmallStatusBarWidgetControl } from "../statusbars/SmallStatusBar"; import { HorizontalPropertyGridWidgetControl, VerticalPropertyGridWidgetControl } from "../widgets/PropertyGridDemoWidget"; import { TableDemoWidgetControl } from "../widgets/TableDemoWidget"; import { NestedFrontstage1 } from "./NestedFrontstage1"; -import { UiAdmin } from "@bentley/ui-abstract"; +import { UiAdmin, WidgetState } from "@bentley/ui-abstract"; /* eslint-disable react/jsx-key */ @@ -49,7 +49,7 @@ function SampleTimelineComponent() { const duration = 20 * 1000; const startDate = new Date(2014, 6, 6); const endDate = new Date(2016, 8, 12); - const [loop, setLoop] = React.useState (false); + const [loop, setLoop] = React.useState(false); const handleOnSettingsChange = (settings: PlaybackSettings) => { if (settings.loop !== undefined) { diff --git a/test-apps/ui-test-app/src/frontend/appui/frontstages/Frontstage2.tsx b/test-apps/ui-test-app/src/frontend/appui/frontstages/Frontstage2.tsx index 7dc579b674c..4ad7971aa08 100644 --- a/test-apps/ui-test-app/src/frontend/appui/frontstages/Frontstage2.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/frontstages/Frontstage2.tsx @@ -10,7 +10,7 @@ import { SelectionContextToolDefinitions, SessionStateActionId, SyncUiEventId, - ToolWidget, UiFramework, Widget, WidgetState, Zone, ZoneState, + ToolWidget, UiFramework, Widget, Zone, ZoneState, } from "@bentley/ui-framework"; import { AppTools } from "../../tools/ToolSpecifications"; import { TreeExampleContentControl } from "../contentviews/TreeExampleContent"; @@ -21,7 +21,7 @@ import { HorizontalPropertyGridContentControl, HorizontalPropertyGridWidgetControl, VerticalPropertyGridWidgetControl, } from "../widgets/PropertyGridDemoWidget"; import { IModelApp } from "@bentley/imodeljs-frontend"; -import { ConditionalBooleanValue } from "@bentley/ui-abstract"; +import { ConditionalBooleanValue, WidgetState } from "@bentley/ui-abstract"; /* eslint-disable react/jsx-key */ diff --git a/test-apps/ui-test-app/src/frontend/appui/frontstages/Frontstage3.tsx b/test-apps/ui-test-app/src/frontend/appui/frontstages/Frontstage3.tsx index 1a4617aa2a8..162901d4984 100644 --- a/test-apps/ui-test-app/src/frontend/appui/frontstages/Frontstage3.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/frontstages/Frontstage3.tsx @@ -3,9 +3,10 @@ * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ import * as React from "react"; +import { WidgetState } from "@bentley/ui-abstract"; import { ActionItemButton, ContentGroup, ContentLayoutDef, CoreTools, Frontstage, FrontstageProps, FrontstageProvider, GroupButton, IModelViewportControl, - NavigationWidget, ToolButton, ToolWidget, UiFramework, Widget, WidgetState, Zone, ZoneLocation, ZoneState, + NavigationWidget, ToolButton, ToolWidget, UiFramework, Widget, Zone, ZoneLocation, ZoneState, } from "@bentley/ui-framework"; import { Direction, Toolbar } from "@bentley/ui-ninezone"; import { AppTools } from "../../tools/ToolSpecifications"; @@ -37,7 +38,7 @@ export class Frontstage3 extends FrontstageProvider { contents: [ { classId: IModelViewportControl.id, - applicationData: { viewState: UiFramework.getDefaultViewState, iModelConnection: UiFramework.getIModelConnection, disableDefaultViewOverlay: true }, + applicationData: { viewState: UiFramework.getDefaultViewState, iModelConnection: UiFramework.getIModelConnection, disableDefaultViewOverlay: true }, }, { classId: App_IModelViewport.id, diff --git a/test-apps/ui-test-app/src/frontend/appui/frontstages/Frontstage4.tsx b/test-apps/ui-test-app/src/frontend/appui/frontstages/Frontstage4.tsx index 2a207d94925..7ff2a696e91 100644 --- a/test-apps/ui-test-app/src/frontend/appui/frontstages/Frontstage4.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/frontstages/Frontstage4.tsx @@ -5,13 +5,13 @@ import * as React from "react"; import { DialogItem, DialogItemValue, DialogLayoutDataProvider, DialogPropertyItem, DialogPropertySyncItem, - PropertyChangeResult, PropertyChangeStatus, PropertyDescription, StandardTypeNames, + PropertyChangeResult, PropertyChangeStatus, PropertyDescription, StandardTypeNames, WidgetState, } from "@bentley/ui-abstract"; import { CommandItemDef, ContentGroup, CoreTools, Frontstage, FrontstageProps, FrontstageProvider, GroupButton, ModalDialogManager, ModelessDialogManager, NavigationWidget, StagePanel, StagePanelState, ToolButton, - ToolWidget, Widget, WidgetState, Zone, ZoneState, + ToolWidget, Widget, Zone, ZoneState, } from "@bentley/ui-framework"; import { Direction, Toolbar } from "@bentley/ui-ninezone"; import { AppTools } from "../../tools/ToolSpecifications"; diff --git a/test-apps/ui-test-app/src/frontend/appui/frontstages/FrontstageUi2.tsx b/test-apps/ui-test-app/src/frontend/appui/frontstages/FrontstageUi2.tsx index d6d1208023d..0b9396044f0 100644 --- a/test-apps/ui-test-app/src/frontend/appui/frontstages/FrontstageUi2.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/frontstages/FrontstageUi2.tsx @@ -5,9 +5,9 @@ import * as React from "react"; import { BasicNavigationWidget, BasicToolWidget, ContentGroup, CoreTools, - Frontstage, FrontstageProps, FrontstageProvider, IModelViewportControl, StagePanel, StagePanelState, SyncUiEventArgs, SyncUiEventDispatcher, ToolbarHelper, UiFramework, Widget, WidgetState, Zone, + Frontstage, FrontstageProps, FrontstageProvider, IModelViewportControl, StagePanel, StagePanelState, SyncUiEventArgs, SyncUiEventDispatcher, ToolbarHelper, UiFramework, Widget, Zone, } from "@bentley/ui-framework"; -import { CommonToolbarItem, StageUsage } from "@bentley/ui-abstract"; +import { CommonToolbarItem, StageUsage, WidgetState } from "@bentley/ui-abstract"; import { ScreenViewport } from "@bentley/imodeljs-frontend"; import { AppTools } from "../../tools/ToolSpecifications"; import { SampleAppIModelApp, SampleAppUiActionId } from "../.."; diff --git a/test-apps/ui-test-app/src/frontend/appui/frontstages/NestedFrontstage1.tsx b/test-apps/ui-test-app/src/frontend/appui/frontstages/NestedFrontstage1.tsx index 2c29a688a4e..2f107e45d82 100644 --- a/test-apps/ui-test-app/src/frontend/appui/frontstages/NestedFrontstage1.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/frontstages/NestedFrontstage1.tsx @@ -3,9 +3,10 @@ * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ import * as React from "react"; +import { WidgetState } from "@bentley/ui-abstract"; import { ActionItemButton, CommandItemDef, CoreTools, Frontstage, FrontstageDef, FrontstageManager, FrontstageProps, FrontstageProvider, GroupButton, NavigationWidget, - NestedFrontstage, ToolButton, ToolWidget, Widget, WidgetState, Zone, ZoneLocation, ZoneState, + NestedFrontstage, ToolButton, ToolWidget, Widget, Zone, ZoneLocation, ZoneState, } from "@bentley/ui-framework"; import { Direction, Toolbar } from "@bentley/ui-ninezone"; import { AppTools } from "../../tools/ToolSpecifications"; @@ -129,7 +130,7 @@ class FrontstageToolWidget extends React.Component { public render() { return ( - .right-panel { display:flex; - justify-content: center; align-items: center; min-width: $settings-rightpanel-width; padding: 30px 50px 30px 30px; diff --git a/test-apps/ui-test-app/src/frontend/appui/frontstages/Settings.tsx b/test-apps/ui-test-app/src/frontend/appui/frontstages/Settings.tsx index f1370da3ca6..59a7f0dddc3 100644 --- a/test-apps/ui-test-app/src/frontend/appui/frontstages/Settings.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/frontstages/Settings.tsx @@ -8,171 +8,29 @@ import "./Settings.scss"; import * as React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { OptionType, Slider, ThemedSelect, ThemedSelectProps, Toggle } from "@bentley/ui-core"; -import { ColorTheme, FrameworkAccuDraw, SyncUiEventDispatcher, SYSTEM_PREFERRED_COLOR_THEME, UiFramework, UiShowHideManager } from "@bentley/ui-framework"; -import { RootState, SampleAppActions, SampleAppIModelApp, SampleAppUiActionId } from "../.."; -interface UiSettingsPageProps { - dragInteraction: boolean; - onToggleDragInteraction: () => void; - frameworkVersion: string; - onToggleFrameworkVersion: () => void; -} - -function isOptionType(value: OptionType | ReadonlyArray): value is OptionType { - if (Array.isArray(value)) - return false; - return true; -} +import { Toggle } from "@bentley/ui-core"; +import { FrameworkAccuDraw, UiFramework } from "@bentley/ui-framework"; /** UiSettingsPage displaying the active settings. */ -class UiSettingsPageComponent extends React.Component { - private _themeTitle: string = UiFramework.i18n.translate("SampleApp:settingsStage.themeTitle"); - private _themeDescription: string = UiFramework.i18n.translate("SampleApp:settingsStage.themeDescription"); - private _autoHideTitle: string = UiFramework.i18n.translate("SampleApp:settingsStage.autoHideTitle"); - private _autoHideDescription: string = UiFramework.i18n.translate("SampleApp:settingsStage.autoHideDescription"); - private _dragInteractionTitle: string = UiFramework.i18n.translate("SampleApp:settingsStage.dragInteractionTitle"); - private _dragInteractionDescription: string = UiFramework.i18n.translate("SampleApp:settingsStage.dragInteractionDescription"); - private _useNewUiTitle: string = UiFramework.i18n.translate("SampleApp:settingsStage.newUiTitle"); - private _useNewUiDescription: string = UiFramework.i18n.translate("SampleApp:settingsStage.newUiDescription"); - private _useProximityOpacityTitle: string = UiFramework.i18n.translate("SampleApp:settingsStage.useProximityOpacityTitle"); - private _useProximityOpacityDescription: string = UiFramework.i18n.translate("SampleApp:settingsStage.useProximityOpacityDescription"); - private _snapWidgetOpacityTitle: string = UiFramework.i18n.translate("SampleApp:settingsStage.snapWidgetOpacityTitle"); - private _snapWidgetOpacityDescription: string = UiFramework.i18n.translate("SampleApp:settingsStage.snapWidgetOpacityDescription"); - private _darkLabel = UiFramework.i18n.translate("SampleApp:settingsStage.dark"); - private _lightLabel = UiFramework.i18n.translate("SampleApp:settingsStage.light"); - private _systemPreferredLabel = UiFramework.i18n.translate("SampleApp:settingsStage.systemPreferred"); +export class AccudrawSettingsPageComponent extends React.Component { private _accuDrawNotificationsTitle: string = UiFramework.i18n.translate("SampleApp:settingsStage.accuDrawNotificationsTitle"); private _accuDrawNotificationsDescription: string = UiFramework.i18n.translate("SampleApp:settingsStage.accuDrawNotificationsDescription"); - private _widgetOpacityTitle: string = UiFramework.i18n.translate("SampleApp:settingsStage.widgetOpacityTitle"); - private _widgetOpacityDescription: string = UiFramework.i18n.translate("SampleApp:settingsStage.widgetOpacityDescription"); - - private _defaultThemeOption = { label: this._systemPreferredLabel, value: SYSTEM_PREFERRED_COLOR_THEME }; - private _themeOptions: Array = [ - this._defaultThemeOption, - { label: this._lightLabel, value: ColorTheme.Light }, - { label: this._darkLabel, value: ColorTheme.Dark }, - ]; - - private _getDefaultThemeOption() { - const theme = UiFramework.getColorTheme(); - for (const option of this._themeOptions) { - if (option.value === theme) - return option; - } - return this._defaultThemeOption; - } - - private _onThemeChange: ThemedSelectProps["onChange"] = async (value) => { - if (!value) - return; - if (!isOptionType(value)) - return; - - UiFramework.setColorTheme(value.value); - - await SampleAppIModelApp.appUiSettings.colorTheme.saveSetting(SampleAppIModelApp.uiSettings); - }; - - private _onAutoHideChange = async () => { - UiShowHideManager.autoHideUi = !UiShowHideManager.autoHideUi; - - await SampleAppIModelApp.appUiSettings.autoHideUi.saveSetting(SampleAppIModelApp.uiSettings); - }; - - private _onUseProximityOpacityChange = async () => { - UiShowHideManager.useProximityOpacity = !UiShowHideManager.useProximityOpacity; - - await SampleAppIModelApp.appUiSettings.useProximityOpacity.saveSetting(SampleAppIModelApp.uiSettings); - }; - - private _onSnapWidgetOpacityChange = async () => { - UiShowHideManager.snapWidgetOpacity = !UiShowHideManager.snapWidgetOpacity; - - await SampleAppIModelApp.appUiSettings.snapWidgetOpacity.saveSetting(SampleAppIModelApp.uiSettings); - }; private _onAccuDrawNotificationsChange = async () => { FrameworkAccuDraw.displayNotifications = !FrameworkAccuDraw.displayNotifications; - - await SampleAppIModelApp.appUiSettings.accuDrawNotifications.saveSetting(SampleAppIModelApp.uiSettings); - }; - - private _onWidgetOpacityChange = async (values: readonly number[]) => { - if (values.length > 0) { - UiFramework.setWidgetOpacity(values[0]); - await SampleAppIModelApp.appUiSettings.widgetOpacity.saveSetting(SampleAppIModelApp.uiSettings); - } }; public render(): React.ReactNode { return (
- - -
- } - /> - } - /> - } - /> - } - /> - } - /> - } - /> } - /> - v.toFixed(1)} - showTicks showTickLabels getTickCount={() => 10} formatTick={(v: number) => v.toFixed(1)} /> - } + settingUi={} /> ); } } -function mapStateToProps(state: RootState) { - return { dragInteraction: state.sampleAppState.dragInteraction, frameworkVersion: state.sampleAppState.frameworkVersion }; -} - -function mapDispatchToProps(dispatch: Dispatch) { - return { - onToggleDragInteraction: async () => { - dispatch(SampleAppActions.toggleDragInteraction()); - await SampleAppIModelApp.appUiSettings.dragInteraction.saveSetting(SampleAppIModelApp.uiSettings); - }, - onToggleFrameworkVersion: async () => { - dispatch(SampleAppActions.toggleFrameworkVersion()); - SyncUiEventDispatcher.dispatchSyncUiEvent(SampleAppUiActionId.toggleFrameworkVersion); - await SampleAppIModelApp.appUiSettings.frameworkVersion.saveSetting(SampleAppIModelApp.uiSettings); - }, - dispatch, - }; -} - -// eslint-disable-next-line @typescript-eslint/naming-convention -export const ConnectedUiSettingsPage = connect(mapStateToProps, mapDispatchToProps)(UiSettingsPageComponent); - interface SettingsItemProps { title: string; description: string; diff --git a/test-apps/ui-test-app/src/frontend/appui/frontstages/component-examples/ComponentExamplesProvider.tsx b/test-apps/ui-test-app/src/frontend/appui/frontstages/component-examples/ComponentExamplesProvider.tsx index 05967d765a4..c7ba50b2750 100644 --- a/test-apps/ui-test-app/src/frontend/appui/frontstages/component-examples/ComponentExamplesProvider.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/frontstages/component-examples/ComponentExamplesProvider.tsx @@ -24,7 +24,7 @@ import { SearchBox, Select, SettingsContainer, SettingsTabEntry, Slider, SmallText, Spinner, SpinnerSize, SplitButton, Subheading, Textarea, ThemedSelect, Tile, Title, Toggle, ToggleButtonType, UnderlinedButton, VerticalTabs, } from "@bentley/ui-core"; -import { MessageManager, ModalDialogManager, QuantityFormatSettingsPanel, ReactNotifyMessageDetails, UiFramework } from "@bentley/ui-framework"; +import { MessageManager, ModalDialogManager, QuantityFormatSettingsPage, ReactNotifyMessageDetails, UiFramework } from "@bentley/ui-framework"; import { SampleAppIModelApp } from "../../.."; import { ComponentExampleCategory, ComponentExampleProps } from "./ComponentExamples"; import { SampleContextMenu } from "./SampleContextMenu"; @@ -32,17 +32,17 @@ import { SampleExpandableBlock } from "./SampleExpandableBlock"; import { SampleImageCheckBox } from "./SampleImageCheckBox"; import { SamplePopupContextMenu } from "./SamplePopupContextMenu"; import { FormatPopupButton } from "./FormatPopupButton"; -import { ConnectedUiSettingsPage } from "../Settings"; +import { AccudrawSettingsPageComponent } from "../Settings"; function MySettingsPage() { const tabs: SettingsTabEntry[] = [ { itemPriority: 10, tabId: "Quantity", pageWillHandleCloseRequest: true, label: "Quantity", tooltip: "Quantity Format Settings", icon: "icon-measure", - page: , + page: , }, { - itemPriority: 20, tabId: "UI", label: "UI", subLabel: "UI and Accudraw", tooltip: "UI Settings", icon: "icon-paintbrush", - page: , + itemPriority: 20, tabId: "Accudraw", label: "Accudraw", tooltip: "Accudraw Settings", icon: "icon-paintbrush", + page: , }, { itemPriority: 30, tabId: "page3", label: "page3", page:
Page 3
}, { itemPriority: 40, tabId: "page4", label: "page4", subLabel: "disabled page4", isDisabled: true, page:
Page 4
}, diff --git a/test-apps/ui-test-app/src/frontend/appui/frontstages/editing/EditFrontstage.tsx b/test-apps/ui-test-app/src/frontend/appui/frontstages/editing/EditFrontstage.tsx index e44ab138cd8..81a1e0bc646 100644 --- a/test-apps/ui-test-app/src/frontend/appui/frontstages/editing/EditFrontstage.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/frontstages/editing/EditFrontstage.tsx @@ -5,12 +5,12 @@ import * as React from "react"; import { IModelApp, IModelConnection, ViewState } from "@bentley/imodeljs-frontend"; import { NodeKey } from "@bentley/presentation-common"; -import { CommonToolbarItem, ConditionalBooleanValue, StageUsage, ToolbarItemUtilities } from "@bentley/ui-abstract"; +import { CommonToolbarItem, ConditionalBooleanValue, StageUsage, ToolbarItemUtilities, WidgetState } from "@bentley/ui-abstract"; import { SelectionMode } from "@bentley/ui-components"; import { AccuDrawDialog, AccuDrawWidgetControl, BasicNavigationWidget, BasicToolWidget, CommandItemDef, ContentGroup, ContentLayoutDef, ContentLayoutProps, ContentProps, CoreTools, CustomItemDef, Frontstage, FrontstageProvider, IModelConnectedViewSelector, ModelessDialogManager, ModelsTreeNodeType, - StagePanel, ToolbarHelper, VisibilityComponentHierarchy, VisibilityWidget, Widget, WidgetState, Zone, ZoneLocation, ZoneState, + StagePanel, ToolbarHelper, VisibilityComponentHierarchy, VisibilityWidget, Widget, Zone, ZoneLocation, ZoneState, } from "@bentley/ui-framework"; import { SampleAppIModelApp, SampleAppUiActionId } from "../../../../frontend/index"; import { EditTools } from "../../../tools/editing/ToolSpecifications"; @@ -186,10 +186,12 @@ export class EditFrontstage extends FrontstageProvider { */ class AdditionalTools { - public additionalHorizontalToolbarItems: CommonToolbarItem[] = - ToolbarHelper.createToolbarItemsFromItemDefs([CoreTools.keyinPaletteButtonItemDef, EditTools.deleteElementTool, - EditTools.moveElementTool, EditTools.rotateElementTool, - EditTools.placeLineStringTool, EditTools.placeBlockTool], 100); + public additionalHorizontalToolbarItems: CommonToolbarItem[] = ToolbarHelper.createToolbarItemsFromItemDefs( + [ + CoreTools.keyinPaletteButtonItemDef, EditTools.deleteElementTool, + EditTools.moveElementTool, EditTools.rotateElementTool, + EditTools.placeLineStringTool, EditTools.placeBlockTool, + ], 100); private get _accudrawDialogItemVertical() { const dialogId = "accudraw-vertical"; diff --git a/test-apps/ui-test-app/src/frontend/appui/uiproviders/AppSettingsProvider.tsx b/test-apps/ui-test-app/src/frontend/appui/uiproviders/AppSettingsTabsProvider.tsx similarity index 58% rename from test-apps/ui-test-app/src/frontend/appui/uiproviders/AppSettingsProvider.tsx rename to test-apps/ui-test-app/src/frontend/appui/uiproviders/AppSettingsTabsProvider.tsx index ed6a44be141..cd3a42bcb52 100644 --- a/test-apps/ui-test-app/src/frontend/appui/uiproviders/AppSettingsProvider.tsx +++ b/test-apps/ui-test-app/src/frontend/appui/uiproviders/AppSettingsTabsProvider.tsx @@ -4,29 +4,29 @@ *--------------------------------------------------------------------------------------------*/ import * as React from "react"; -import { getQuantityFormatsSettingsManagerEntry, UiFramework } from "@bentley/ui-framework"; -import { SettingsProvider, SettingsTabEntry } from "@bentley/ui-core"; -import { ConnectedUiSettingsPage } from "../frontstages/Settings"; +import { getQuantityFormatsSettingsManagerEntry, getUiSettingsManagerEntry, UiFramework } from "@bentley/ui-framework"; +import { SettingsTabEntry, SettingsTabsProvider } from "@bentley/ui-core"; +import { AccudrawSettingsPageComponent } from "../frontstages/Settings"; // Sample settings provider that dynamically adds settings into the setting stage -export class AppSettingsProvider implements SettingsProvider { - public readonly id = "AppSettingsProvider"; +export class AppSettingsTabsProvider implements SettingsTabsProvider { + public readonly id = "AppSettingsTabsProvider"; public getSettingEntries(_stageId: string, _stageUsage: string): ReadonlyArray | undefined { return [ getQuantityFormatsSettingsManagerEntry(10, {availableUnitSystems:new Set(["metric","imperial","usSurvey"])}), { - itemPriority: 20, tabId: "ui-test-app:UI", label: "UI", - page: , + itemPriority: 20, tabId: "ui-test-app:Accudraw", label: "Accudraw", + page: , isDisabled: false, icon: "icon-paintbrush", - subLabel: "UI and Accudraw", - tooltip: "UI and Accudraw Settings", + tooltip: "Accudraw Settings", }, + getUiSettingsManagerEntry(30, true), ]; } public static initializeAppSettingProvider() { - UiFramework.settingsManager.addSettingsProvider(new AppSettingsProvider()); + UiFramework.settingsManager.addSettingsProvider(new AppSettingsTabsProvider()); } } diff --git a/test-apps/ui-test-app/src/frontend/index.tsx b/test-apps/ui-test-app/src/frontend/index.tsx index 01103347773..792b865bd90 100644 --- a/test-apps/ui-test-app/src/frontend/index.tsx +++ b/test-apps/ui-test-app/src/frontend/index.tsx @@ -36,12 +36,12 @@ import { PresentationUnitSystem } from "@bentley/presentation-common"; import { Presentation } from "@bentley/presentation-frontend"; import { getClassName } from "@bentley/ui-abstract"; import { BeDragDropContext } from "@bentley/ui-components"; -import { LocalUiSettings, UiSettings } from "@bentley/ui-core"; +import { LocalSettingsStorage, UiSettings } from "@bentley/ui-core"; import { - ActionsUnion, AppNotificationManager, ConfigurableUiContent, createAction, DeepReadonly, DragDropLayerRenderer, FrameworkAccuDraw, FrameworkReducer, + ActionsUnion, AppNotificationManager, AppUiSettings, ConfigurableUiContent, createAction, DeepReadonly, DragDropLayerRenderer, FrameworkAccuDraw, FrameworkReducer, FrameworkRootState, FrameworkToolAdmin, FrameworkUiAdmin, FrameworkVersion, FrontstageDeactivatedEventArgs, FrontstageDef, FrontstageManager, - IModelAppUiSettings, IModelInfo, ModalFrontstageClosedEventArgs, SafeAreaContext, StateManager, SyncUiEventDispatcher, ThemeManager, - ToolbarDragInteractionContext, UiFramework, UiSettingsProvider, + IModelInfo, ModalFrontstageClosedEventArgs, SafeAreaContext, StateManager, SyncUiEventDispatcher, SYSTEM_PREFERRED_COLOR_THEME, ThemeManager, ToolbarDragInteractionContext, + UiFramework, UiSettingsProvider, UserSettingsStorage, } from "@bentley/ui-framework"; import { SafeAreaInsets } from "@bentley/ui-ninezone"; import { getSupportedRpcs } from "../common/rpcs"; @@ -55,8 +55,7 @@ import { IModelViewportControl } from "./appui/contentviews/IModelViewport"; import { EditFrontstage } from "./appui/frontstages/editing/EditFrontstage"; import { LocalFileOpenFrontstage } from "./appui/frontstages/LocalFileStage"; import { ViewsFrontstage } from "./appui/frontstages/ViewsFrontstage"; -import { AppSettingsProvider } from "./appui/uiproviders/AppSettingsProvider"; -import { AppUiSettings } from "./AppUiSettings"; +import { AppSettingsTabsProvider } from "./appui/uiproviders/AppSettingsTabsProvider"; import { AppViewManager } from "./favorites/AppViewManager"; // Favorite Properties Support import { ElementSelectionListener } from "./favorites/ElementSelectionListener"; // Favorite Properties Support import { AnalysisAnimationTool } from "./tools/AnalysisAnimation"; @@ -73,7 +72,7 @@ import { EditingSessionTool } from "./tools/editing/PrimitiveToolEx"; RpcConfiguration.developmentMode = true; // cSpell:ignore setTestProperty sampleapp uitestapp setisimodellocal projectwise mobx hypermodeling testapp urlps -// cSpell:ignore toggledraginteraction toggleframeworkversion setdraginteraction setframeworkversion +// cSpell:ignore toggledraginteraction toggleframeworkversion set-drag-interaction set-framework-version /** Action Ids used by redux and to send sync UI components. Typically used to refresh visibility or enable state of control. * Use lower case strings to be compatible with SyncUi processing. @@ -82,25 +81,17 @@ export enum SampleAppUiActionId { setTestProperty = "sampleapp:settestproperty", setAnimationViewId = "sampleapp:setAnimationViewId", setIsIModelLocal = "sampleapp:setisimodellocal", - toggleDragInteraction = "sampleapp:toggledraginteraction", - toggleFrameworkVersion = "sampleapp:toggleframeworkversion", - setDragInteraction = "sampleapp:setdraginteraction", - setFrameworkVersion = "sampleapp:setframeworkversion", } export interface SampleAppState { testProperty: string; animationViewId: string; - dragInteraction: boolean; - frameworkVersion: string; isIModelLocal: boolean; } const initialState: SampleAppState = { testProperty: "", animationViewId: "", - dragInteraction: true, - frameworkVersion: "1", isIModelLocal: false, }; @@ -109,10 +100,6 @@ export const SampleAppActions = { setTestProperty: (testProperty: string) => createAction(SampleAppUiActionId.setTestProperty, testProperty), setAnimationViewId: (viewId: string) => createAction(SampleAppUiActionId.setAnimationViewId, viewId), setIsIModelLocal: (isIModelLocal: boolean) => createAction(SampleAppUiActionId.setIsIModelLocal, isIModelLocal), - toggleDragInteraction: () => createAction(SampleAppUiActionId.toggleDragInteraction), - toggleFrameworkVersion: () => createAction(SampleAppUiActionId.toggleFrameworkVersion), - setDragInteraction: (dragInteraction: boolean) => createAction(SampleAppUiActionId.setDragInteraction, dragInteraction), - setFrameworkVersion: (frameworkVersion: string) => createAction(SampleAppUiActionId.setFrameworkVersion, frameworkVersion), }; class SampleAppAccuSnap extends AccuSnap { @@ -147,18 +134,6 @@ function SampleAppReducer(state: SampleAppState = initialState, action: SampleAp case SampleAppUiActionId.setIsIModelLocal: { return { ...state, isIModelLocal: action.payload }; } - case SampleAppUiActionId.toggleDragInteraction: { - return { ...state, dragInteraction: !state.dragInteraction }; - } - case SampleAppUiActionId.toggleFrameworkVersion: { - return { ...state, frameworkVersion: state.frameworkVersion === "1" ? "2" : "1" }; - } - case SampleAppUiActionId.setDragInteraction: { - return { ...state, dragInteraction: action.payload }; - } - case SampleAppUiActionId.setFrameworkVersion: { - return { ...state, frameworkVersion: action.payload }; - } } return state; } @@ -180,7 +155,8 @@ export class SampleAppIModelApp { public static iModelParams: SampleIModelParams | undefined; public static testAppConfiguration: TestAppConfiguration | undefined; private static _appStateManager: StateManager | undefined; - private static _appUiSettings = new AppUiSettings(); + private static _localUiSettings = new LocalSettingsStorage(); + private static _UserUiSettingsStorage = new UserSettingsStorage(); // Favorite Properties Support private static _selectionSetListener = new ElementSelectionListener(true); @@ -189,17 +165,14 @@ export class SampleAppIModelApp { return StateManager.store as Store; } - public static get uiSettings(): UiSettings { - return UiFramework.getUiSettings(); - } - - public static set uiSettings(v: UiSettings) { - UiFramework.setUiSettings(v); - SampleAppIModelApp._appUiSettings.apply(v); // eslint-disable-line @typescript-eslint/no-floating-promises + public static getUiSettingsStorage(): UiSettings { + const authorized = !!IModelApp.authorizationClient && IModelApp.authorizationClient.isAuthorized; + if (SampleAppIModelApp.testAppConfiguration?.useLocalSettings || !authorized) { + return SampleAppIModelApp._localUiSettings; + } + return SampleAppIModelApp._UserUiSettingsStorage; } - public static get appUiSettings(): AppUiSettings { return SampleAppIModelApp._appUiSettings; } - public static async startup(opts: WebViewerAppOpts & NativeAppOpts): Promise { if (ProcessDetector.isElectronAppFrontend) { await ElectronApp.startup(opts); @@ -285,7 +258,23 @@ export class SampleAppIModelApp { // To test map-layer extension comment out the following and ensure ui-test-app\build\imjs_extensions contains map-layers, if not see Readme.md in map-layers package. await MapLayersUI.initialize(false); // if false then add widget in FrontstageDef - AppSettingsProvider.initializeAppSettingProvider(); + AppSettingsTabsProvider.initializeAppSettingProvider(); + + // Create and register the AppUiSettings instance to provide default for ui settings in Redux store + const lastTheme = (window.localStorage&&window.localStorage.getItem("uifw:defaultTheme"))??SYSTEM_PREFERRED_COLOR_THEME; + const defaults = { + colorTheme: lastTheme ?? SYSTEM_PREFERRED_COLOR_THEME, + dragInteraction: false, + frameworkVersion: "2", + widgetOpacity: 0.8, + }; + + // initialize any settings providers that may need to have defaults set by iModelApp + UiFramework.registerUserSettingsProvider(new AppUiSettings(defaults)); + + // go ahead and initialize settings before login or in case login is by-passed + await UiFramework.setUiSettingsStorage(SampleAppIModelApp.getUiSettingsStorage()); + // try starting up event loop if not yet started so key-in palette can be opened IModelApp.startEventLoop(); } @@ -521,7 +510,7 @@ export class SampleAppIModelApp { } public static getUiFrameworkProperty(): string { - return SampleAppIModelApp.store.getState().sampleAppState.frameworkVersion; + return SampleAppIModelApp.store.getState().frameworkState.configurableUiState.frameworkVersion; } public static saveAnimationViewId(value: string, immediateSync = false) { @@ -565,17 +554,17 @@ function AppFrameworkVersionComponent(props: { frameworkVersion: string, childre } function mapDragInteractionStateToProps(state: RootState) { - return { dragInteraction: state.sampleAppState.dragInteraction }; + return { dragInteraction: state.frameworkState.configurableUiState.useDragInteraction }; } function mapFrameworkVersionStateToProps(state: RootState) { - return { frameworkVersion: state.sampleAppState.frameworkVersion }; + return { frameworkVersion: state.frameworkState.configurableUiState.frameworkVersion }; } const AppDragInteraction = connect(mapDragInteractionStateToProps)(AppDragInteractionComponent); const AppFrameworkVersion = connect(mapFrameworkVersionStateToProps)(AppFrameworkVersionComponent); -class SampleAppViewer extends React.Component { +class SampleAppViewer extends React.Component { constructor(props: any) { super(props); @@ -586,7 +575,7 @@ class SampleAppViewer extends React.Component { + private _onUserStateChanged = async (_accessToken: AccessToken | undefined) => { const authorized = !!IModelApp.authorizationClient && IModelApp.authorizationClient.isAuthorized; - this.setState({ authorized, uiSettings: this.getUiSettings(authorized) }); + const uiSettingsStorage = SampleAppIModelApp.getUiSettingsStorage(); + await UiFramework.setUiSettingsStorage(uiSettingsStorage); + this.setState({ authorized, uiSettingsStorage}); this._initializeSignin(authorized); // eslint-disable-line @typescript-eslint/no-floating-promises }; - private getUiSettings(authorized: boolean): UiSettings { - if (SampleAppIModelApp.testAppConfiguration?.useLocalSettings || !authorized) { - SampleAppIModelApp.uiSettings = new LocalUiSettings(); - } else { - SampleAppIModelApp.uiSettings = new IModelAppUiSettings(); - } - - return SampleAppIModelApp.uiSettings; - } - private _handleFrontstageDeactivatedEvent = (args: FrontstageDeactivatedEventArgs): void => { Logger.logInfo(SampleAppIModelApp.loggerCategory(this), `Frontstage exit: id=${args.deactivatedFrontstageDef.id} totalTime=${args.totalTime} engagementTime=${args.engagementTime} idleTime=${args.idleTime}`); }; @@ -643,7 +624,7 @@ class SampleAppViewer extends React.Component {/** UiSettingsProvider is optional. By default LocalUiSettings is used to store UI settings. */} - + } /> @@ -760,7 +741,6 @@ async function main() { IModelApp.telemetry.addClient(applicationInsightsClient); } - // wait for both our i18n namespaces to be read. await SampleAppIModelApp.initialize(); // register new QuantityType diff --git a/test-apps/ui-test-app/src/frontend/tools/ToolSpecifications.tsx b/test-apps/ui-test-app/src/frontend/tools/ToolSpecifications.tsx index 8c609299aeb..3486b140ad5 100644 --- a/test-apps/ui-test-app/src/frontend/tools/ToolSpecifications.tsx +++ b/test-apps/ui-test-app/src/frontend/tools/ToolSpecifications.tsx @@ -13,12 +13,12 @@ import { PresentationUnitSystem } from "@bentley/presentation-common"; import { Presentation } from "@bentley/presentation-frontend"; import { BackstageItem, BackstageItemUtilities, CommonStatusBarItem, ConditionalBooleanValue, ConditionalStringValue, DialogButtonType, StatusBarSection, - UiItemsManager, UiItemsProvider, + UiItemsManager, UiItemsProvider, WidgetState, } from "@bentley/ui-abstract"; import { Dialog, MessageSeverity, Radio, ReactMessage, SvgPath, SvgSprite, UnderlinedButton } from "@bentley/ui-core"; import { Backstage, BaseItemState, CommandItemDef, ContentViewManager, FrontstageManager, MessageManager, ModalDialogManager, ReactNotifyMessageDetails, - StatusBarItemUtilities, SyncUiEventDispatcher, SyncUiEventId, ToolItemDef, WidgetState, withStatusFieldProps, + StatusBarItemUtilities, SyncUiEventDispatcher, SyncUiEventId, ToolItemDef, withStatusFieldProps, } from "@bentley/ui-framework"; import { FooterSeparator } from "@bentley/ui-ninezone"; import { SampleAppIModelApp } from "../"; diff --git a/test-apps/ui-test-app/src/frontend/tools/ToolWithSettings.ts b/test-apps/ui-test-app/src/frontend/tools/ToolWithSettings.ts index 2b2875f857d..b0614b27504 100644 --- a/test-apps/ui-test-app/src/frontend/tools/ToolWithSettings.ts +++ b/test-apps/ui-test-app/src/frontend/tools/ToolWithSettings.ts @@ -15,7 +15,7 @@ import { FormatterSpec } from "@bentley/imodeljs-quantity"; import { DialogItem, DialogLayoutDataProvider, DialogProperty, DialogPropertyItem, DialogPropertySyncItem, EnumerationChoice, InputEditorSizeParams, PropertyChangeResult, PropertyChangeStatus, - PropertyDescriptionHelper, PropertyEditorParamTypes, RelativePosition, SuppressLabelEditorParams, SyncPropertiesChangeEvent, + PropertyDescriptionHelper, PropertyEditorParamTypes, RangeEditorParams, RelativePosition, SuppressLabelEditorParams, SyncPropertiesChangeEvent, } from "@bentley/ui-abstract"; import { CursorInformation, MenuItemProps, UiFramework } from "@bentley/ui-framework"; @@ -192,6 +192,17 @@ export class ToolWithSettings extends PrimitiveTool { PropertyDescriptionHelper.buildTextEditorDescription("coordinate", IModelApp.i18n.translate("SampleApp:tools.ToolWithSettings.Prompts.Coordinate")), "0.0, 0.0, 0.0", undefined); + // ------------- text based edit field --------------- + public numberProperty = new DialogProperty( + PropertyDescriptionHelper.buildNumberEditorDescription("numberVal", IModelApp.i18n.translate("SampleApp:tools.ToolWithSettings.Prompts.Number"), + { + type: PropertyEditorParamTypes.Range, + step: 2, + precision: 0, + minimum: 0, + maximum: 1000, + } as RangeEditorParams), 14.0 ); + // ------------- display station value as text --------------- private _stationFormatterSpec?: FormatterSpec; public get stationFormatterSpec(): FormatterSpec | undefined { @@ -384,8 +395,9 @@ export class ToolWithSettings extends PrimitiveTool { toolSettings.push(this.cityProperty.toDialogItem({ rowPriority: 10, columnIndex: 2 })); toolSettings.push(this.stateProperty.toDialogItem({ rowPriority: 10, columnIndex: 4 })); toolSettings.push(this.coordinateProperty.toDialogItem({ rowPriority: 15, columnIndex: 2 })); - toolSettings.push(this.stationProperty.toDialogItem({ rowPriority: 16, columnIndex: 2 })); - const lengthLock = this.useLengthProperty.toDialogItem({ rowPriority: 20, columnIndex: 0 }); + toolSettings.push(this.numberProperty.toDialogItem({ rowPriority: 16, columnIndex: 2 })); + toolSettings.push(this.stationProperty.toDialogItem({ rowPriority: 17, columnIndex: 2 })); + const lengthLock = this.useLengthProperty.toDialogItem({ rowPriority: 18, columnIndex: 0 }); toolSettings.push(this.lengthProperty.toDialogItem({ rowPriority: 20, columnIndex: 2 }, lengthLock)); toolSettings.push(this.surveyLengthProperty.toDialogItem({ rowPriority: 21, columnIndex: 2 })); toolSettings.push(this.angleProperty.toDialogItem({ rowPriority: 25, columnIndex: 2 })); @@ -431,6 +443,9 @@ export class ToolWithSettings extends PrimitiveTool { } else if (updatedValue.propertyName === this.lengthProperty.name) { this.lengthProperty.value = updatedValue.value.value as number; this.showInfoFromUi(updatedValue); + } else if (updatedValue.propertyName === this.numberProperty.name) { + this.numberProperty.value = updatedValue.value.value as number; + this.showInfoFromUi(updatedValue); } else if (updatedValue.propertyName === this.surveyLengthProperty.name) { this.surveyLengthProperty.value = updatedValue.value.value as number; this.showInfoFromUi(updatedValue); diff --git a/test-apps/ui-test-extension/package.json b/test-apps/ui-test-extension/package.json index 87ea616fc77..84eb4082155 100644 --- a/test-apps/ui-test-extension/package.json +++ b/test-apps/ui-test-extension/package.json @@ -28,9 +28,9 @@ }, "license": "MIT", "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/extension-webpack-tools": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/extension-webpack-tools": "2.15.0-dev.13", "@types/react": "16.9.43", "@types/classnames": "^2.2.3", "@types/react-select": "3.0.26", @@ -41,21 +41,21 @@ "svg-sprite-loader": "4.2.1" }, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-quantity": "2.15.0-dev.6", - "@bentley/ui-abstract": "2.15.0-dev.6", - "@bentley/ui-components": "2.15.0-dev.6", - "@bentley/ui-core": "2.15.0-dev.6", - "@bentley/ui-framework": "2.15.0-dev.6", - "@bentley/ui-ninezone": "2.15.0-dev.6", - "@bentley/presentation-common": "2.15.0-dev.6", - "@bentley/presentation-frontend": "2.15.0-dev.6", - "@bentley/presentation-components": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-quantity": "2.15.0-dev.13", + "@bentley/ui-abstract": "2.15.0-dev.13", + "@bentley/ui-components": "2.15.0-dev.13", + "@bentley/ui-core": "2.15.0-dev.13", + "@bentley/ui-framework": "2.15.0-dev.13", + "@bentley/ui-ninezone": "2.15.0-dev.13", + "@bentley/presentation-common": "2.15.0-dev.13", + "@bentley/presentation-frontend": "2.15.0-dev.13", + "@bentley/presentation-components": "2.15.0-dev.13", "classnames": "^2.2.5", "react": "^16.8.0", "react-compound-slider": "^2.5.0", diff --git a/tools/backend-webpack/package.json b/tools/backend-webpack/package.json index 0c2a4dc5a34..f5ed69a3b1f 100644 --- a/tools/backend-webpack/package.json +++ b/tools/backend-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/backend-webpack-tools", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "Configuration and scripts for iModel.js agents and backends.", "license": "MIT", "repository": { @@ -24,7 +24,7 @@ "backend-webpack-tools": "./bin/backend-webpack-tools.js" }, "dependencies": { - "@bentley/webpack-tools-core": "2.15.0-dev.6", + "@bentley/webpack-tools-core": "2.15.0-dev.13", "case-sensitive-paths-webpack-plugin": "^2.1.2", "chalk": "^3.0.0", "concurrently": "^3.6.1", diff --git a/tools/build/package.json b/tools/build/package.json index 1e6828f158e..9eec5452a72 100644 --- a/tools/build/package.json +++ b/tools/build/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/build-tools", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "Bentley build tools", "license": "MIT", "repository": { @@ -58,7 +58,7 @@ "yargs": "^16.0.0" }, "devDependencies": { - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/node": "10.14.1", "eslint": "^6.8.0" }, diff --git a/tools/build/scripts/extract-api-summary.js b/tools/build/scripts/extract-api-summary.js index e20f157e2c0..5d368dd93bb 100644 --- a/tools/build/scripts/extract-api-summary.js +++ b/tools/build/scripts/extract-api-summary.js @@ -28,45 +28,64 @@ if (undefined === argv.outDir) { fs.ensureDir(path.normalize(argv.outDir)); +let shouldGenerateFullReport = false; +if (undefined !== argv.gatherFullReport) + shouldGenerateFullReport = true; + // create output file const trimmedApiSignature = (argv.apiSignature.split('.'))[0]; -const sigFileName = path.basename(path.normalize(trimmedApiSignature)) -const sigFilePath = path.join(argv.outDir, sigFileName + ".exports.csv") -fs.createFileSync(sigFilePath); +const sigFileName = path.basename(path.normalize(trimmedApiSignature)); +const sigFilePath = path.join(argv.outDir, `${shouldGenerateFullReport ? "summary" : sigFileName}.exports.csv`); const outputLines = []; -outputLines.push("sep=;"); -outputLines.push("Release Tag;API Item"); +if (shouldGenerateFullReport) { + if (fs.existsSync(sigFilePath)) + outputLines.push(""); + else { + outputLines.push("sep=;"); + outputLines.push("Package Name;Release Tag;API Item"); + } +} else { + fs.createFileSync(sigFilePath); + outputLines.push("sep=;"); + outputLines.push("Release Tag;API Item"); +} // Open up the signature file - fs.readFile(argv.apiSignature, function (error, data) { if (error) { throw error; } - let previousLine = undefined; + let previousLines = []; data.toString().split("\n").forEach(function (line, index, arr) { if (index === arr.length - 1 && line === "") { return; } - if (undefined !== previousLine) { + if (previousLines.length !== 0) { const matches = line.match(/export \S*\s(.*)(\s{|;)/); if (null !== matches) { const split = matches[1].split(/(<|extends|implements)/); - outputLines.push(`${previousLine};${split[0]}`); + for (const previousLine of previousLines) + outputLines.push(shouldGenerateFullReport ? `${sigFileName};${previousLine};${split[0]}` : `${previousLine};${split[0]}`); } - previousLine = undefined; + previousLines = []; return; } - const matches = line.match(/\s@(beta|alpha|public|internal|deprecated)/); - - if (null === matches || 2 > matches.length) { - previousLine = undefined; + let match = line.match(/\s@(beta|alpha|public|internal)/); + if (null === match) { + previousLines = []; return; } - previousLine = matches[1]; + previousLines.push(match[1]); + + // handle deprecated separate since it can be used together with the other release tags + match = line.match(/\s@(deprecated)/); + if (null !== match) { + previousLines.push(match[1]); + return; + } }); - fs.writeFileSync(sigFilePath, outputLines.join("\n")); + shouldGenerateFullReport ? fs.appendFileSync(sigFilePath, outputLines.join("\n")) : fs.writeFileSync(sigFilePath, outputLines.join("\n")); }); diff --git a/tools/build/scripts/extract-api.js b/tools/build/scripts/extract-api.js index bb4ca137898..bccec0296e2 100644 --- a/tools/build/scripts/extract-api.js +++ b/tools/build/scripts/extract-api.js @@ -125,5 +125,10 @@ spawn(require.resolve(".bin/api-extractor"), args).then((code) => { spawn("node", extractSummaryArgs).then((code) => { process.exit(code); }); + + if (process.env.GENERATE_FULL_API_REPORT) + spawn("node", [...extractSummaryArgs, "--gatherFullReport"]).then((code) => { + process.exit(code); + }); }); handleInterrupts(); diff --git a/tools/certa/package.json b/tools/certa/package.json index 8626f64eee0..51e8512393a 100644 --- a/tools/certa/package.json +++ b/tools/certa/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/certa", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "A mocha-based integration test runner", "license": "MIT", "main": "bin/certa.js", @@ -44,8 +44,8 @@ "yargs": "^16.0.0" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/detect-port": "~1.1.0", "@types/express": "^4.16.1", diff --git a/tools/config-loader/package.json b/tools/config-loader/package.json index 4fc4becdbee..1b0f6c0c864 100644 --- a/tools/config-loader/package.json +++ b/tools/config-loader/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/config-loader", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "Configuration loader", "license": "MIT", "repository": { @@ -35,8 +35,8 @@ "json5": "^2.1.0" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/json5": "0.0.30", "@types/node": "10.14.1", "eslint": "^6.8.0", diff --git a/tools/ecschema2ts/package.json b/tools/ecschema2ts/package.json index 158c03a79d6..f8f601009e2 100644 --- a/tools/ecschema2ts/package.json +++ b/tools/ecschema2ts/package.json @@ -2,7 +2,7 @@ "name": "@bentley/ecschema2ts", "description": "Command line tools that takes an ECSchema xml file and outputs a typescript module", "license": "MIT", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "bin": { "ecschema2ts": "./bin/index.js" }, @@ -35,14 +35,14 @@ "url": "http://www.bentley.com" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/chai-string": "^1.4.1", "@types/fs-extra": "^4.0.7", "@types/mocha": "^5.2.5", "@types/node": "10.14.1", - "@types/xmldom": "^0.1.29", + "@types/xmldom": "^0.1.30", "chai": "^4.1.2", "chai-string": "^1.5.0", "cpx": "^1.5.0", @@ -54,22 +54,22 @@ "typescript": "~4.1.0" }, "dependencies": { - "@bentley/backend-itwin-client": "2.15.0-dev.6", - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/ecschema-locaters": "2.15.0-dev.6", - "@bentley/ecschema-metadata": "2.15.0-dev.6", - "@bentley/frontend-authorization-client": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodelhub-client": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/rbac-client": "2.15.0-dev.6", + "@bentley/backend-itwin-client": "2.15.0-dev.13", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/ecschema-locaters": "2.15.0-dev.13", + "@bentley/ecschema-metadata": "2.15.0-dev.13", + "@bentley/frontend-authorization-client": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodelhub-client": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/rbac-client": "2.15.0-dev.13", "chai-string": "^1.5.0", "chalk": "^3.0.0", "commander": "^2.14.1", "fs-extra": "^8.1.0", - "xmldom": "^0.1.27" + "xmldom": "^0.5.0" }, "nyc": { "nycrc-path": "./node_modules/@bentley/build-tools/.nycrc" diff --git a/tools/eslint-plugin/package.json b/tools/eslint-plugin/package.json index d0a616cacab..de42e1a1f1e 100644 --- a/tools/eslint-plugin/package.json +++ b/tools/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/eslint-plugin", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "ESLint plugin with default configuration and custom rules for iModel.js projects", "license": "MIT", "keywords": [ diff --git a/tools/extension-cli/package.json b/tools/extension-cli/package.json index ad676fef002..c24c6127d8c 100644 --- a/tools/extension-cli/package.json +++ b/tools/extension-cli/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/extension-cli", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "A CLI client for publishing iModel.js Extensions", "license": "MIT", "main": "bin/extension-cli.js", @@ -32,19 +32,19 @@ "url": "http://www.bentley.com" }, "dependencies": { - "@bentley/backend-itwin-client": "2.15.0-dev.6", - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/context-registry-client": "2.15.0-dev.6", - "@bentley/ecschema-metadata": "2.15.0-dev.6", - "@bentley/extension-client": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodelhub-client": "2.15.0-dev.6", - "@bentley/imodeljs-backend": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/electron-manager": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/rbac-client": "2.15.0-dev.6", - "@bentley/telemetry-client": "2.15.0-dev.6", + "@bentley/backend-itwin-client": "2.15.0-dev.13", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/context-registry-client": "2.15.0-dev.13", + "@bentley/ecschema-metadata": "2.15.0-dev.13", + "@bentley/extension-client": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodelhub-client": "2.15.0-dev.13", + "@bentley/imodeljs-backend": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/electron-manager": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/rbac-client": "2.15.0-dev.13", + "@bentley/telemetry-client": "2.15.0-dev.13", "electron": "^11.1.0", "fast-sha256": "1.3.0", "rimraf": "^3.0.2", @@ -53,8 +53,8 @@ "yargs": "^16.0.0" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/mocha": "^5.2.5", "@types/node": "10.14.1", diff --git a/tools/extension-webpack/package.json b/tools/extension-webpack/package.json index b82a7e3373b..85211c5f9eb 100644 --- a/tools/extension-webpack/package.json +++ b/tools/extension-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/extension-webpack-tools", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "Configuration and scripts for iModel.js Extensions.", "license": "MIT", "repository": { @@ -26,7 +26,7 @@ "types": "./index.d.ts", "dependencies": { "@babel/core": "7.7.4", - "@bentley/webpack-tools-core": "2.15.0-dev.6", + "@bentley/webpack-tools-core": "2.15.0-dev.13", "@svgr/webpack": "4.3.3", "babel-loader": "8.1.0", "babel-plugin-named-asset-import": "^0.3.5", diff --git a/tools/oidc-signin-tool/package.json b/tools/oidc-signin-tool/package.json index d730e64d8f0..6ac7a044c5f 100644 --- a/tools/oidc-signin-tool/package.json +++ b/tools/oidc-signin-tool/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/oidc-signin-tool", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "OIDC Signin Helper", "main": "lib/index.js", "typings": "lib/index", @@ -31,17 +31,17 @@ "url": "http://www.bentley.com" }, "dependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/certa": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/frontend-authorization-client": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/certa": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/frontend-authorization-client": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", "openid-client": "^3.15.3", "puppeteer": "chrome-86" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", "@types/mocha": "^5.2.5", diff --git a/tools/perf-tools/package.json b/tools/perf-tools/package.json index cd4f8571be5..f445bffd631 100644 --- a/tools/perf-tools/package.json +++ b/tools/perf-tools/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/perf-tools", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "Tools for collecting and reporting performance data", "license": "MIT", "repository": { @@ -11,7 +11,7 @@ "compile": "npm run build", "build": "tsc 1>&2", "clean": "rimraf lib .rush/temp/package-deps*.json", - "lint": "eslint -f visualstudio ./reporter/**/*.ts 1>&2", + "lint": "eslint -f visualstudio \"./reporter/**/*.ts\" 1>&2", "extract-api": "betools extract-api --entry=Reporter", "test": "", "docs": "", @@ -31,8 +31,8 @@ "fs-extra": "^8.1.0" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/fs-extra": "^4.0.7", "eslint": "^6.8.0", "rimraf": "^3.0.2", diff --git a/tools/webpack-core/package.json b/tools/webpack-core/package.json index 4ebf663ea8c..a4661caa346 100644 --- a/tools/webpack-core/package.json +++ b/tools/webpack-core/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/webpack-tools-core", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "Set of Webpack Plugins and Loaders used for building iModel.js applications", "license": "MIT", "repository": { @@ -37,8 +37,8 @@ "webpack": "4.42.0" }, "devDependencies": { - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", "@types/fs-extra": "^4.0.7", "@types/glob": "^5.0.35", "@types/node": "10.14.1", diff --git a/ui/abstract/package.json b/ui/abstract/package.json index aa37c99932d..6a5de845bff 100644 --- a/ui/abstract/package.json +++ b/ui/abstract/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/ui-abstract", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js UI abstractions", "main": "lib/ui-abstract.js", "typings": "lib/ui-abstract", @@ -33,20 +33,20 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/geometry-core": "^2.15.0-dev.6", - "@bentley/imodeljs-i18n": "^2.15.0-dev.6" + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/geometry-core": "^2.15.0-dev.13", + "@bentley/imodeljs-i18n": "^2.15.0-dev.13" }, "//devDependencies": [ "NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install", "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", "@types/chai": "^4.1.4", "@types/chai-as-promised": "^7", "@types/chai-jest-snapshot": "^1.3.0", diff --git a/ui/abstract/src/test/properties/PropertyDescriptionHelper.test.ts b/ui/abstract/src/test/properties/PropertyDescriptionHelper.test.ts index 7aa1576f980..8acfbdf268a 100644 --- a/ui/abstract/src/test/properties/PropertyDescriptionHelper.test.ts +++ b/ui/abstract/src/test/properties/PropertyDescriptionHelper.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; import { - EnumerationChoice, PropertyDescriptionHelper, PropertyEditorParamTypes, SuppressLabelEditorParams, + EnumerationChoice, PropertyDescriptionHelper, PropertyEditorParamTypes, RangeEditorParams, SuppressLabelEditorParams, } from "../../ui-abstract"; // cSpell:ignore Picklist @@ -67,6 +67,40 @@ describe("PropertyDescriptionHelper", () => { }); }); + describe("NumberEditor Description", () => { + const typename = "number"; + it("should build correctly", () => { + const editor = "numeric-input"; + const editorDescription = PropertyDescriptionHelper.buildNumberEditorDescription(testName, testLabel); + expect(editorDescription.name).to.eq(testName); + expect(editorDescription.typename).to.eq(typename); + expect(editorDescription.displayLabel).to.eq(testLabel); + expect(editorDescription.editor?.name).to.eq(editor); + expect(editorDescription.editor?.params?.length).to.eq(1); + expect(editorDescription.editor?.params?.[0].type).to.eq(PropertyEditorParamTypes.Range); + + }); + + it("should build with additional editor params correctly", () => { + const editor = "numeric-input"; + const numberParam = { + type: PropertyEditorParamTypes.Range, + step: 2, + precision: 0, + minimum: 0, + maximum: 1000, + } as RangeEditorParams; + const editorDescription = PropertyDescriptionHelper.buildNumberEditorDescription(testName, testLabel, numberParam, additionParam); + expect(editorDescription.name).to.eq(testName); + expect(editorDescription.typename).to.eq(typename); + expect(editorDescription.displayLabel).to.eq(testLabel); + expect(editorDescription.editor?.name).to.eq(editor); + expect(editorDescription.editor?.params?.length).to.eq(2); + expect(editorDescription.editor?.params?.[0].type).to.eq(PropertyEditorParamTypes.Range); + expect(editorDescription.editor?.params?.[1].type).to.eq(PropertyEditorParamTypes.SuppressEditorLabel); + }); + }); + describe("EnumPicklistEditor Description", () => { const choices = [ { label: "Red", value: 1 }, diff --git a/ui/abstract/src/ui-abstract/UiItemsManager.ts b/ui/abstract/src/ui-abstract/UiItemsManager.ts index 426472aaf81..18ccd99ff0a 100644 --- a/ui/abstract/src/ui-abstract/UiItemsManager.ts +++ b/ui/abstract/src/ui-abstract/UiItemsManager.ts @@ -15,7 +15,7 @@ import { AbstractWidgetProps } from "./widget/AbstractWidgetProps"; import { StagePanelLocation, StagePanelSection } from "./widget/StagePanel"; /** Action taken by the application on item provided by a UiItemsProvider - * @beta + * @public */ export enum UiItemsApplicationAction { /** Allow the change to the item */ @@ -27,7 +27,7 @@ export enum UiItemsApplicationAction { } /** Describes interface of objects that want to provide UI component to the running IModelApp. - * @beta + * @public */ export interface UiItemsProvider { /** id of provider */ @@ -55,7 +55,7 @@ export interface UiItemsProvider { } /** UIProvider Registered Event Args interface. - * @beta + * @public  */ export interface UiItemProviderRegisteredEventArgs { providerId: string; @@ -63,7 +63,7 @@ export interface UiItemProviderRegisteredEventArgs { /** * Controls registering of UiItemsProviders and calls the provider's methods when populating different parts of the User Interface. - * @beta + * @public */ export class UiItemsManager { private static _registeredUiItemsProviders: Map = new Map(); diff --git a/ui/abstract/src/ui-abstract/backstage/BackstageItem.ts b/ui/abstract/src/ui-abstract/backstage/BackstageItem.ts index 5a5e77dc9ec..976116659f5 100644 --- a/ui/abstract/src/ui-abstract/backstage/BackstageItem.ts +++ b/ui/abstract/src/ui-abstract/backstage/BackstageItem.ts @@ -12,7 +12,7 @@ import { ConditionalStringValue } from "../items/ConditionalStringValue"; import { ProvidedItem } from "../items/ProvidedItem"; /** Used to specify the item type added to the backstage menu. - * @beta + * @public */ export enum BackstageItemType { /** Item that executes an action function */ @@ -22,7 +22,7 @@ export enum BackstageItemType { } /** Describes the data needed to insert a button into the backstage menu. - * @beta + * @public */ export interface CommonBackstageItem extends ProvidedItem { /** can be used by application to store miscellaneous data. */ @@ -55,40 +55,40 @@ export interface CommonBackstageItem extends ProvidedItem { } /** Describes the data needed to insert an action button into the backstage menu. - * @beta + * @public */ export interface BackstageActionItem extends CommonBackstageItem { readonly execute: () => void; } /** Describes the data needed to insert an action button into the backstage menu. - * @beta + * @public */ export interface BackstageStageLauncher extends CommonBackstageItem { readonly stageId: string; } /** Describes the data needed to insert a button into the backstage menu. - * @beta + * @public */ export type BackstageItem = BackstageActionItem | BackstageStageLauncher; /** BackstageActionItem type guard. - * @beta + * @public */ export const isActionItem = (item: BackstageItem): item is BackstageActionItem => { return (item as BackstageActionItem).execute !== undefined; }; /** BackstageStageLauncher type guard. - * @beta + * @public */ export const isStageLauncher = (item: BackstageItem): item is BackstageStageLauncher => { return (item as BackstageStageLauncher).stageId !== undefined; }; /** Utilities for creating and maintaining backstage items - * @beta + * @public */ export class BackstageItemUtilities { /** Creates a stage launcher backstage item */ diff --git a/ui/abstract/src/ui-abstract/items/ConditionalStringValue.ts b/ui/abstract/src/ui-abstract/items/ConditionalStringValue.ts index 01ec2c1893d..d0e691efbb0 100644 --- a/ui/abstract/src/ui-abstract/items/ConditionalStringValue.ts +++ b/ui/abstract/src/ui-abstract/items/ConditionalStringValue.ts @@ -8,7 +8,7 @@ /** Class used to return a string value. The string value is refreshed by using the specified function. The syncEventIds define one or more * eventIds that would require the stringGetter function to be rerun. - * @beta + * @public */ export class ConditionalStringValue { private _value?: string; diff --git a/ui/abstract/src/ui-abstract/items/ProvidedItem.ts b/ui/abstract/src/ui-abstract/items/ProvidedItem.ts index b147515e9de..9c352d2213e 100644 --- a/ui/abstract/src/ui-abstract/items/ProvidedItem.ts +++ b/ui/abstract/src/ui-abstract/items/ProvidedItem.ts @@ -7,7 +7,7 @@ */ /** Properties for an item provided by UiItemsProvider - * @beta + * @public */ export interface ProvidedItem { /** id of UiItemsProvider */ diff --git a/ui/abstract/src/ui-abstract/properties/Description.ts b/ui/abstract/src/ui-abstract/properties/Description.ts index dd0f2d39ea2..fe2bbfc1644 100644 --- a/ui/abstract/src/ui-abstract/properties/Description.ts +++ b/ui/abstract/src/ui-abstract/properties/Description.ts @@ -6,7 +6,7 @@ * @module Properties */ -import { BasePropertyEditorParams, ColorEditorParams, ImageCheckBoxParams, PropertyEditorParams, PropertyEditorParamTypes } from "./EditorParams"; +import { BasePropertyEditorParams, ColorEditorParams, ImageCheckBoxParams, PropertyEditorParams, PropertyEditorParamTypes, RangeEditorParams } from "./EditorParams"; // cSpell:ignore Picklist @@ -106,6 +106,30 @@ export class PropertyDescriptionHelper { }; } + /** Builds an editor that uses [NumberInput]($ui-core) control + * @alpha + */ + public static buildNumberEditorDescription(name: string, label: string, overrideParams?: RangeEditorParams, additionalParams: BasePropertyEditorParams[] = []): PropertyDescription { + const editorParams = [{ + type: PropertyEditorParamTypes.Range, + step: 1, + precision: 0, + ...overrideParams, + } as RangeEditorParams, ...additionalParams]; + + const editor = { + name: "numeric-input", + params: editorParams, + }; + + return { + name, + displayLabel: label, + typename: "number", + editor, + }; + } + /** Builds a string description * @alpha */ diff --git a/ui/abstract/src/ui-abstract/statusbar/StatusBarItem.ts b/ui/abstract/src/ui-abstract/statusbar/StatusBarItem.ts index fc6d54e4507..2c2ed054857 100644 --- a/ui/abstract/src/ui-abstract/statusbar/StatusBarItem.ts +++ b/ui/abstract/src/ui-abstract/statusbar/StatusBarItem.ts @@ -12,7 +12,7 @@ import { ConditionalStringValue } from "../items/ConditionalStringValue"; import { ProvidedItem } from "../items/ProvidedItem"; /** Status bar Groups/Sections from Left to Right - * @beta + * @public */ export enum StatusBarSection { /** area for tool assistance and messages */ @@ -34,7 +34,7 @@ export enum StatusBarSection { } /** Defines which side of Icon where label is placed - * @beta + * @public */ export enum StatusBarLabelSide { /** Label is placed left side of icon. This is the default if not specified */ @@ -44,12 +44,12 @@ export enum StatusBarLabelSide { } /** Type for StatusBar Item Id - * @beta + * @public */ export type StatusBarItemId = CommonStatusBarItem["id"]; /** Describes the data needed to insert a button into the status bar. - * @beta + * @public */ export interface AbstractStatusBarItem extends ProvidedItem { /** can be used by application to store miscellaneous data. */ @@ -71,7 +71,7 @@ export interface AbstractStatusBarItem extends ProvidedItem { } /** Describes the data needed to insert an action item into the status bar. - * @beta + * @public */ export interface AbstractStatusBarActionItem extends AbstractStatusBarItem { /** method to execute when icon is pressed */ @@ -85,7 +85,7 @@ export interface AbstractStatusBarActionItem extends AbstractStatusBarItem { } /** Describes the data needed to insert a label item with an optional icon into the status bar. - * @beta + * @public */ export interface AbstractStatusBarLabelItem extends AbstractStatusBarItem { /** Name of icon WebFont entry or if specifying an SVG symbol added by plug on use "svg:" prefix to imported symbol Id. */ @@ -98,40 +98,40 @@ export interface AbstractStatusBarLabelItem extends AbstractStatusBarItem { /** Describes the data needed to insert a custom item into the status bar. This is used to allow extension * implementer to create a framework specific component. - * @beta + * @public */ export interface AbstractStatusBarCustomItem extends AbstractStatusBarItem { readonly isCustom: true; } /** Describes the data needed to insert a button into the status bar. - * @beta + * @public */ export type CommonStatusBarItem = AbstractStatusBarActionItem | AbstractStatusBarLabelItem | AbstractStatusBarCustomItem; /** AbstractStatusBarActionItem type guard. - * @beta + * @public */ export const isAbstractStatusBarActionItem = (item: CommonStatusBarItem): item is AbstractStatusBarActionItem => { return (item as AbstractStatusBarActionItem).execute !== undefined; }; /** AbstractStatusBarLabelItem type guard. - * @beta + * @public */ export const isAbstractStatusBarLabelItem = (item: CommonStatusBarItem): item is AbstractStatusBarLabelItem => { return (item as AbstractStatusBarLabelItem).label !== undefined && (item as AbstractStatusBarActionItem).execute === undefined; }; /** AbstractStatusBarCustomItem type guard. - * @beta + * @public */ export const isAbstractStatusBarCustomItem = (item: CommonStatusBarItem): item is AbstractStatusBarCustomItem => { return !!(item as AbstractStatusBarCustomItem).isCustom; }; /** Helper class to create Abstract StatusBar Item definitions. - * @beta + * @public */ export class AbstractStatusBarItemUtilities { /** Creates a StatusBar item to perform an action */ diff --git a/ui/abstract/src/ui-abstract/toolbars/ToolbarItem.ts b/ui/abstract/src/ui-abstract/toolbars/ToolbarItem.ts index 0696990c154..9ee41f02b76 100644 --- a/ui/abstract/src/ui-abstract/toolbars/ToolbarItem.ts +++ b/ui/abstract/src/ui-abstract/toolbars/ToolbarItem.ts @@ -12,7 +12,7 @@ import { ConditionalStringValue } from "../items/ConditionalStringValue"; import { ProvidedItem } from "../items/ProvidedItem"; /** Used to specify the usage of the toolbar which determine the toolbar position. - * @beta + * @public */ export enum ToolbarUsage { /** Contains tools to Create Update and Delete content - in ninezone this is in top left of content area. */ @@ -22,7 +22,7 @@ export enum ToolbarUsage { } /** Used to specify the orientation of the toolbar. - * @beta + * @public */ export enum ToolbarOrientation { /** Horizontal toolbar. */ @@ -32,7 +32,7 @@ export enum ToolbarOrientation { } /** Describes the data needed to insert a UI items into an existing set of UI items. - * @beta + * @public */ export interface ToolbarItem extends ProvidedItem { /** can be used by application to store miscellaneous data. */ @@ -65,7 +65,7 @@ export interface ToolbarItem extends ProvidedItem { } /** Describes the data needed to insert an action button into a toolbar. - * @beta + * @public */ export interface ActionButton extends ToolbarItem { /** Name of icon WebFont entry or if specifying an SVG symbol added by plug on use "svg:" prefix to imported symbol Id. */ @@ -77,7 +77,7 @@ export interface ActionButton extends ToolbarItem { } /** Describes the data needed to insert a group button into a toolbar. - * @beta + * @public */ export interface GroupButton extends ToolbarItem { /** Name of icon WebFont entry or if specifying an SVG symbol added by plug on use "svg:" prefix to imported symbol Id. */ @@ -91,7 +91,7 @@ export interface GroupButton extends ToolbarItem { } /** Describes the data needed to insert a custom button into a toolbar. - * @beta + * @public */ export interface CustomButtonDefinition extends ToolbarItem { /** Name of icon WebFont entry or if specifying an SVG symbol added by plug on use "svg:" prefix to imported symbol Id. */ @@ -103,17 +103,17 @@ export interface CustomButtonDefinition extends ToolbarItem { } /** Any Button Type that can be inserted into a toolbar. - * @beta + * @public */ export type CommonToolbarItem = ActionButton | GroupButton | CustomButtonDefinition; /** Type for Toolbar Item Id - * @beta + * @public */ export type ToolbarItemId = CommonToolbarItem["id"]; /** Helper class to create Abstract StatusBar Item definitions. - * @beta + * @public */ export class ToolbarItemUtilities { /** Creates an Action Button */ diff --git a/ui/abstract/src/ui-abstract/widget/AbstractWidgetProps.ts b/ui/abstract/src/ui-abstract/widget/AbstractWidgetProps.ts index 95fddfcf6ad..7fee977794b 100644 --- a/ui/abstract/src/ui-abstract/widget/AbstractWidgetProps.ts +++ b/ui/abstract/src/ui-abstract/widget/AbstractWidgetProps.ts @@ -12,7 +12,7 @@ import { ProvidedItem } from "../items/ProvidedItem"; import { WidgetState } from "./WidgetState"; /** Properties for a Widget. - * @beta + * @public  */ export interface AbstractWidgetProps extends ProvidedItem { /** Gets the widget content */ diff --git a/ui/abstract/src/ui-abstract/widget/StagePanel.ts b/ui/abstract/src/ui-abstract/widget/StagePanel.ts index 97b04fafff7..989bfb54ca6 100644 --- a/ui/abstract/src/ui-abstract/widget/StagePanel.ts +++ b/ui/abstract/src/ui-abstract/widget/StagePanel.ts @@ -7,7 +7,7 @@ */ /** Available Stage Panel locations. - * @beta + * @public */ export enum StagePanelLocation { Top = 101, @@ -19,7 +19,7 @@ export enum StagePanelLocation { } /** Enum for Stage Panel Sections - * @beta + * @public */ export enum StagePanelSection { Start, diff --git a/ui/abstract/src/ui-abstract/widget/WidgetState.ts b/ui/abstract/src/ui-abstract/widget/WidgetState.ts index a1c3168c426..c614a4a93c5 100644 --- a/ui/abstract/src/ui-abstract/widget/WidgetState.ts +++ b/ui/abstract/src/ui-abstract/widget/WidgetState.ts @@ -7,7 +7,7 @@ */ /** Widget state enum. - * @beta + * @public  */ export enum WidgetState { /** Widget tab is visible and active and its contents are visible */ diff --git a/ui/components/package.json b/ui/components/package.json index da3bba95b69..a4ed8020e13 100644 --- a/ui/components/package.json +++ b/ui/components/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/ui-components", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js UI complex components", "main": "lib/ui-components.js", "typings": "lib/ui-components", @@ -33,14 +33,14 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/geometry-core": "^2.15.0-dev.6", - "@bentley/imodeljs-common": "^2.15.0-dev.6", - "@bentley/imodeljs-frontend": "^2.15.0-dev.6", - "@bentley/imodeljs-i18n": "^2.15.0-dev.6", - "@bentley/imodeljs-quantity": "^2.15.0-dev.6", - "@bentley/ui-abstract": "2.15.0-dev.6", - "@bentley/ui-core": "^2.15.0-dev.6", + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/geometry-core": "^2.15.0-dev.13", + "@bentley/imodeljs-common": "^2.15.0-dev.13", + "@bentley/imodeljs-frontend": "^2.15.0-dev.13", + "@bentley/imodeljs-i18n": "^2.15.0-dev.13", + "@bentley/imodeljs-quantity": "^2.15.0-dev.13", + "@bentley/ui-abstract": "2.15.0-dev.13", + "@bentley/ui-core": "^2.15.0-dev.13", "react": "^16.8.0", "react-dom": "^16.8.0" }, @@ -49,16 +49,16 @@ "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", - "@bentley/imodeljs-quantity": "2.15.0-dev.6", - "@bentley/ui-abstract": "2.15.0-dev.6", - "@bentley/ui-core": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", + "@bentley/imodeljs-quantity": "2.15.0-dev.13", + "@bentley/ui-abstract": "2.15.0-dev.13", + "@bentley/ui-core": "2.15.0-dev.13", "@testing-library/react": "^8.0.1", "@testing-library/react-hooks": "^3.2.1", "@types/chai": "^4.1.4", @@ -124,7 +124,7 @@ "callable-instance2": "1.0.0", "classnames": "^2.2.5", "eventemitter2": "^5.0.1", - "immer": "8.0.1", + "immer": "^9.0.1", "immutable": "^3.8.2", "inspire-tree": "^5.0.1", "linkify-it": "~2.2.0", diff --git a/ui/components/src/test/color/ColorPickerButton.test.tsx b/ui/components/src/test/color/ColorPickerButton.test.tsx index 713d797ac31..23e67f490a3 100644 --- a/ui/components/src/test/color/ColorPickerButton.test.tsx +++ b/ui/components/src/test/color/ColorPickerButton.test.tsx @@ -80,6 +80,11 @@ describe("", () => { expect(spyOnColorPick).to.be.calledOnce; expect(button.getAttribute("data-value")).to.eq("rgb(255,0,0)"); // red } + + // ensure update prop is handled + const newColorDef = ColorDef.create(ColorByName.green); // green = 0x008000, + renderedComponent.rerender(); + expect(button.getAttribute("data-value")).to.eq("rgb(0,128,0)"); // green }); it("readonly - button press should not open popup", async () => { diff --git a/ui/components/src/test/editors/NumericInputEditor.test.tsx b/ui/components/src/test/editors/NumericInputEditor.test.tsx index 214d3f7b468..0d633358660 100644 --- a/ui/components/src/test/editors/NumericInputEditor.test.tsx +++ b/ui/components/src/test/editors/NumericInputEditor.test.tsx @@ -152,6 +152,27 @@ describe("", () => { expect(spyOnCommit.calledOnce).to.be.true; }); + it("calls onCommit on increment click", async () => { + const propertyRecord = TestUtils.createNumericProperty("Test", 123, StandardEditorNames.NumericInput); + const spyOnCommit = sinon.spy(); + function handleCommit(_commit: PropertyUpdatedArgs): void { + spyOnCommit(); + } + const wrapper = render( { }} />); + const inputNode = wrapper.container.querySelector("input"); + expect(inputNode).not.to.be.null; + + const input = wrapper.container.querySelector("input") as HTMLInputElement; + const incrementor = wrapper.container.querySelectorAll(".core-number-input-button"); + expect(incrementor.length).to.eq(2); + fireEvent.click(incrementor[0]); + + await TestUtils.flushAsyncOperations(); + expect(spyOnCommit.calledOnce).to.be.true; + + expect(input.value).to.eq("124"); + }); + class MineDataController extends DataControllerBase { public async validateValue(_newValue: PropertyValue, _record: PropertyRecord): Promise { return { encounteredError: true, errorMessage: { priority: OutputMessagePriority.Error, briefMessage: "Test"} }; diff --git a/ui/components/src/test/table/component/Table.test.tsx b/ui/components/src/test/table/component/Table.test.tsx index b438dfe69fc..b58150eebbb 100644 --- a/ui/components/src/test/table/component/Table.test.tsx +++ b/ui/components/src/test/table/component/Table.test.tsx @@ -13,7 +13,7 @@ import * as moq from "typemoq"; import { BeDuration } from "@bentley/bentleyjs-core"; import { PrimitiveValue, PropertyConverterInfo, PropertyDescription, PropertyRecord, PropertyValue, PropertyValueFormat, SpecialKey } from "@bentley/ui-abstract"; -import { HorizontalAlignment, LocalUiSettings } from "@bentley/ui-core"; +import { HorizontalAlignment, LocalSettingsStorage } from "@bentley/ui-core"; import { CellItem, ColumnDescription, PropertyUpdatedArgs, PropertyValueRendererManager, RowItem, SelectionMode, Table, TableDataChangeEvent, @@ -1355,7 +1355,7 @@ describe("Table", () => { reorderableColumns={true} ref={ref} settingsIdentifier="test" - uiSettings={new LocalUiSettings({ localStorage: storageMock() } as Window)} + settingsStorage={new LocalSettingsStorage({ localStorage: storageMock() } as Window)} />); await waitForSpy(onRowsLoaded); table.update(); @@ -1378,7 +1378,7 @@ describe("Table", () => { onRowsLoaded={onRowsLoaded} settingsIdentifier="test" showHideColumns={true} - uiSettings={new LocalUiSettings({ localStorage: storageMock() } as Window)} + settingsStorage={new LocalSettingsStorage({ localStorage: storageMock() } as Window)} />); await waitForSpy(onRowsLoaded); table.update(); diff --git a/ui/components/src/test/tree/controlled/internal/TreeSelectionManager.test.ts b/ui/components/src/test/tree/controlled/internal/TreeSelectionManager.test.ts index d1ffced290e..bef69ea07f5 100644 --- a/ui/components/src/test/tree/controlled/internal/TreeSelectionManager.test.ts +++ b/ui/components/src/test/tree/controlled/internal/TreeSelectionManager.test.ts @@ -11,7 +11,7 @@ import { SelectionMode } from "../../../../ui-components/common/selection/Select import { IndividualSelection, isRangeSelection, RangeSelection, TreeSelectionManager, } from "../../../../ui-components/tree/controlled/internal/TreeSelectionManager"; -import { MutableTreeModelNode, TreeModel, VisibleTreeNodes } from "../../../../ui-components/tree/controlled/TreeModel"; +import { isTreeModelNode, TreeModel, TreeModelNode, TreeModelNodePlaceholder, VisibleTreeNodes } from "../../../../ui-components/tree/controlled/TreeModel"; import { createRandomMutableTreeModelNode } from "../RandomTreeNodesHelpers"; import { SpecialKey } from "@bentley/ui-abstract"; import { TreeActions } from "../../../../ui-components/tree/controlled/TreeActions"; @@ -27,8 +27,6 @@ describe("TreeSelectionManager", () => { const keyEventMock = moq.Mock.ofType(); const treeActionsMock = moq.Mock.ofType(); let selectionHandler: SelectionHandler; - let node: MutableTreeModelNode; - let node2: MutableTreeModelNode; beforeEach(() => { visibleNodesMock.reset(); @@ -37,19 +35,31 @@ describe("TreeSelectionManager", () => { keyEventMock.reset(); multipleSelectionManager = new TreeSelectionManager(SelectionMode.Multiple, () => visibleNodesMock.object); selectionHandler = (multipleSelectionManager as any)._selectionHandler; + }); + + function createTreeModelNode(props?: Partial) { + return { + ...createRandomMutableTreeModelNode(), + isLoading: false, + isSelected: false, + ...props, + }; + } - node = { ...createRandomMutableTreeModelNode(), isSelected: false, numChildren: 0 }; - node2 = { ...createRandomMutableTreeModelNode(), isSelected: false, numChildren: undefined }; + function setupModelWithNodes(nodes: Array) { + visibleNodesMock.reset(); + treeModelMock.reset(); visibleNodesMock.setup((x) => x.getModel()).returns(() => treeModelMock.object); - visibleNodesMock.setup((x) => x.getNumNodes()).returns(() => 2); - visibleNodesMock.setup((x) => x.getAtIndex(0)).returns(() => node); - visibleNodesMock.setup((x) => x.getAtIndex(1)).returns(() => node2); - visibleNodesMock.setup((x) => x.getIndexOfNode(node.id)).returns(() => 0); - visibleNodesMock.setup((x) => x.getIndexOfNode(node2.id)).returns(() => 1); - treeModelMock.setup((x) => x.getNode(node.id)).returns(() => node); - treeModelMock.setup((x) => x.getNode(node2.id)).returns(() => node2); - }); + visibleNodesMock.setup((x) => x.getNumNodes()).returns(() => nodes.length); + nodes.forEach((node, index) => { + visibleNodesMock.setup((x) => x.getAtIndex(index)).returns(() => node); + if (isTreeModelNode(node)) { + visibleNodesMock.setup((x) => x.getIndexOfNode(node.id)).returns(() => index); + treeModelMock.setup((x) => x.getNode(node.id)).returns(() => node); + } + }); + } describe("constructor", () => { @@ -69,47 +79,85 @@ describe("TreeSelectionManager", () => { }); it("selects node", () => { + const node = createTreeModelNode(); + setupModelWithNodes([node]); const spy = sinon.spy(extendedSelectionManager.onSelectionReplaced, "emit"); eventMock.setup((x) => x.shiftKey).returns(() => false); eventMock.setup((x) => x.ctrlKey).returns(() => false); extendedSelectionManager.onNodeClicked(node.id, eventMock.object); - expect(spy).to.be.called; + expect(spy).to.be.calledOnce; + expect(spy).to.be.calledWithExactly({ + selectedNodeIds: [node.id], + }); }); it("ctrl deselects node", () => { + const node = createTreeModelNode({ isSelected: true }); + setupModelWithNodes([node]); const spy = sinon.spy(extendedSelectionManager.onSelectionChanged, "emit"); - node = { ...node, isSelected: true }; eventMock.setup((x) => x.shiftKey).returns(() => false); eventMock.setup((x) => x.ctrlKey).returns(() => true); extendedSelectionManager.onNodeClicked(node.id, eventMock.object); - expect(spy).to.be.called; + expect(spy).to.be.calledOnce; + expect(spy).to.be.calledWithExactly({ + selectedNodes: [], + deselectedNodes: [node.id], + }); }); it("ctrl selects nodes", () => { + const nodes = [createTreeModelNode(), createTreeModelNode()]; + setupModelWithNodes(nodes); const spy = sinon.spy(extendedSelectionManager.onSelectionChanged, "emit"); eventMock.setup((x) => x.shiftKey).returns(() => false); eventMock.setup((x) => x.ctrlKey).returns(() => true); - extendedSelectionManager.onNodeClicked(node.id, eventMock.object); - extendedSelectionManager.onNodeClicked(node2.id, eventMock.object); + extendedSelectionManager.onNodeClicked(nodes[0].id, eventMock.object); + extendedSelectionManager.onNodeClicked(nodes[1].id, eventMock.object); expect(spy).to.be.calledTwice; + expect(spy.firstCall).to.be.calledWithExactly({ + selectedNodes: [nodes[0].id], + deselectedNodes: [], + }); + expect(spy.secondCall).to.be.calledWithExactly({ + selectedNodes: [nodes[1].id], + deselectedNodes: [], + }); }); it("shift selects nodes", () => { + const nodes = [createTreeModelNode(), createTreeModelNode()]; + setupModelWithNodes(nodes); const spy = sinon.spy(extendedSelectionManager.onSelectionReplaced, "emit"); eventMock.setup((x) => x.shiftKey).returns(() => true); eventMock.setup((x) => x.ctrlKey).returns(() => false); - extendedSelectionManager.onNodeClicked(node.id, eventMock.object); - extendedSelectionManager.onNodeClicked(node2.id, eventMock.object); + extendedSelectionManager.onNodeClicked(nodes[0].id, eventMock.object); + extendedSelectionManager.onNodeClicked(nodes[1].id, eventMock.object); expect(spy).to.be.calledTwice; + expect(spy.firstCall).to.be.calledWithExactly({ + selectedNodeIds: [nodes[0].id], + }); + expect(spy.secondCall).to.be.calledWithExactly({ + selectedNodeIds: { from: nodes[0].id, to: nodes[1].id }, + }); }); it("shift + ctrl selects nodes", () => { + const nodes = [createTreeModelNode(), createTreeModelNode()]; + setupModelWithNodes(nodes); const spy = sinon.spy(extendedSelectionManager.onSelectionChanged, "emit"); eventMock.setup((x) => x.shiftKey).returns(() => true); eventMock.setup((x) => x.ctrlKey).returns(() => true); - extendedSelectionManager.onNodeClicked(node.id, eventMock.object); - extendedSelectionManager.onNodeClicked(node2.id, eventMock.object); + extendedSelectionManager.onNodeClicked(nodes[0].id, eventMock.object); + extendedSelectionManager.onNodeClicked(nodes[1].id, eventMock.object); expect(spy).to.be.calledTwice; + expect(spy.firstCall).to.be.calledWithExactly({ + selectedNodes: [nodes[0].id], + deselectedNodes: [], + }); + expect(spy.secondCall).to.be.calledWithExactly({ + selectedNodes: { from: nodes[0].id, to: nodes[1].id }, + deselectedNodes: [], + }); }); }); @@ -117,31 +165,56 @@ describe("TreeSelectionManager", () => { describe("onNodeMouseDown", () => { it("selects nodes by dragging", () => { + const nodes = [createTreeModelNode(), createTreeModelNode()]; + setupModelWithNodes(nodes); const spy = sinon.spy(selectionHandler, "completeDragAction"); const changeSpy = sinon.spy(multipleSelectionManager.onSelectionChanged, "emit"); - multipleSelectionManager.onNodeMouseDown(node.id); - multipleSelectionManager.onNodeMouseMove(node2.id); + multipleSelectionManager.onNodeMouseDown(nodes[0].id); + multipleSelectionManager.onNodeMouseMove(nodes[1].id); window.dispatchEvent(new Event("mouseup")); expect(spy).to.be.called; - expect(changeSpy).to.be.called; + expect(changeSpy).to.be.calledWithExactly({ + selectedNodes: [nodes[0].id, nodes[1].id], + deselectedNodes: [], + }); }); it("does not select nodes if visible nodes are not set", () => { - multipleSelectionManager.setVisibleNodes(undefined!); + const nodes = [createTreeModelNode(), createTreeModelNode()]; + setupModelWithNodes(nodes); + multipleSelectionManager.setVisibleNodes(undefined); const spy = sinon.spy(selectionHandler, "completeDragAction"); const changeSpy = sinon.spy(multipleSelectionManager.onSelectionChanged, "emit"); - multipleSelectionManager.onNodeMouseDown(node.id); - multipleSelectionManager.onNodeMouseMove(node2.id); + multipleSelectionManager.onNodeMouseDown(nodes[0].id); + multipleSelectionManager.onNodeMouseMove(nodes[1].id); window.dispatchEvent(new Event("mouseup")); expect(spy).to.be.called; expect(changeSpy).to.not.be.called; }); + it("selects nodes when there are placeholder visible nodes", () => { + const placeholder = { childIndex: 0, depth: 0 }; + const node = createTreeModelNode(); + setupModelWithNodes([placeholder, node]); + const spy = sinon.spy(selectionHandler, "completeDragAction"); + const changeSpy = sinon.spy(multipleSelectionManager.onSelectionChanged, "emit"); + multipleSelectionManager.onNodeMouseDown(node.id); + window.dispatchEvent(new Event("mouseup")); + multipleSelectionManager.onNodeClicked(node.id, eventMock.object); + expect(spy).to.be.called; + expect(changeSpy).to.be.calledWithExactly({ + selectedNodes: [node.id], + deselectedNodes: [], + }); + }); + }); describe("onNodeMouseMove", () => { it("updates drag action", () => { + const node = createTreeModelNode(); + setupModelWithNodes([node]); const spy = sinon.spy(selectionHandler, "updateDragAction"); multipleSelectionManager.onNodeMouseMove(node.id); expect(spy).to.be.called; @@ -155,15 +228,14 @@ describe("TreeSelectionManager", () => { beforeEach(() => { extendedSelectionManager = new TreeSelectionManager(SelectionMode.Extended, () => visibleNodesMock.object); - eventMock.setup((x) => x.shiftKey).returns(() => false); eventMock.setup((x) => x.ctrlKey).returns(() => false); - treeActionsMock.reset(); - node2.isLoading = false; }); it("does nothing on non-navigation key", () => { + const node = createTreeModelNode(); + setupModelWithNodes([node]); extendedSelectionManager.onNodeClicked(node.id, eventMock.object); const spy = sinon.spy(extendedSelectionManager.onSelectionReplaced, "emit"); keyEventMock.setup((x) => x.key).returns(() => SpecialKey.Divide); @@ -175,52 +247,61 @@ describe("TreeSelectionManager", () => { }); it("selects node", () => { - extendedSelectionManager.onNodeClicked(node.id, eventMock.object); + const nodes = [createTreeModelNode({ isSelected: false }), createTreeModelNode({ isSelected: false })]; + setupModelWithNodes(nodes); + extendedSelectionManager.onNodeClicked(nodes[0].id, eventMock.object); const spy = sinon.spy(extendedSelectionManager.onSelectionReplaced, "emit"); keyEventMock.setup((x) => x.key).returns(() => SpecialKey.ArrowDown); keyEventMock.setup((x) => x.shiftKey).returns(() => false); keyEventMock.setup((x) => x.ctrlKey).returns(() => false); extendedSelectionManager.onTreeKeyDown(keyEventMock.object, treeActionsMock.object); extendedSelectionManager.onTreeKeyUp(keyEventMock.object, treeActionsMock.object); - expect(spy).to.be.called; + expect(spy).to.be.calledWithExactly({ selectedNodeIds: [nodes[1].id] }); }); it("shift selects nodes", () => { - extendedSelectionManager.onNodeClicked(node.id, eventMock.object); + const nodes = [createTreeModelNode({ isSelected: false }), createTreeModelNode({ isSelected: false })]; + setupModelWithNodes(nodes); + extendedSelectionManager.onNodeClicked(nodes[0].id, eventMock.object); const spy = sinon.spy(extendedSelectionManager.onSelectionReplaced, "emit"); keyEventMock.setup((x) => x.key).returns(() => SpecialKey.ArrowDown); keyEventMock.setup((x) => x.shiftKey).returns(() => true); keyEventMock.setup((x) => x.ctrlKey).returns(() => false); extendedSelectionManager.onTreeKeyDown(keyEventMock.object, treeActionsMock.object); extendedSelectionManager.onTreeKeyUp(keyEventMock.object, treeActionsMock.object); - expect(spy).to.be.called; + expect(spy).to.be.calledWithExactly({ selectedNodeIds: { from: nodes[0].id, to: nodes[1].id } }); }); it("Home should select top node", () => { - extendedSelectionManager.onNodeClicked(node2.id, eventMock.object); + const nodes = [createTreeModelNode(), createTreeModelNode()]; + setupModelWithNodes(nodes); + extendedSelectionManager.onNodeClicked(nodes[1].id, eventMock.object); const spy = sinon.spy(extendedSelectionManager.onSelectionReplaced, "emit"); keyEventMock.setup((x) => x.key).returns(() => SpecialKey.Home); keyEventMock.setup((x) => x.shiftKey).returns(() => false); keyEventMock.setup((x) => x.ctrlKey).returns(() => false); extendedSelectionManager.onTreeKeyDown(keyEventMock.object, treeActionsMock.object); extendedSelectionManager.onTreeKeyUp(keyEventMock.object, treeActionsMock.object); - expect(spy).to.be.called; + expect(spy).to.be.calledWithExactly({ selectedNodeIds: [nodes[0].id] }); }); it("End should select bottom node", () => { - extendedSelectionManager.onNodeClicked(node.id, eventMock.object); + const nodes = [createTreeModelNode(), createTreeModelNode()]; + setupModelWithNodes(nodes); + extendedSelectionManager.onNodeClicked(nodes[0].id, eventMock.object); const spy = sinon.spy(extendedSelectionManager.onSelectionReplaced, "emit"); keyEventMock.setup((x) => x.key).returns(() => SpecialKey.End); keyEventMock.setup((x) => x.shiftKey).returns(() => false); keyEventMock.setup((x) => x.ctrlKey).returns(() => false); extendedSelectionManager.onTreeKeyDown(keyEventMock.object, treeActionsMock.object); extendedSelectionManager.onTreeKeyUp(keyEventMock.object, treeActionsMock.object); - expect(spy).to.be.called; + expect(spy).to.be.calledWithExactly({ selectedNodeIds: [nodes[1].id] }); }); it("Right should expand node", () => { - node2.isExpanded = false; - extendedSelectionManager.onNodeClicked(node2.id, eventMock.object); + const node = createTreeModelNode({ numChildren: 1, isExpanded: false }); + setupModelWithNodes([node]); + extendedSelectionManager.onNodeClicked(node.id, eventMock.object); keyEventMock.setup((x) => x.key).returns(() => SpecialKey.ArrowRight); keyEventMock.setup((x) => x.shiftKey).returns(() => false); keyEventMock.setup((x) => x.ctrlKey).returns(() => false); @@ -228,12 +309,13 @@ describe("TreeSelectionManager", () => { treeActionsMock.setup((x) => x.onNodeExpanded).returns(() => spy); extendedSelectionManager.onTreeKeyDown(keyEventMock.object, treeActionsMock.object); extendedSelectionManager.onTreeKeyUp(keyEventMock.object, treeActionsMock.object); - expect(spy).to.be.called; + expect(spy).to.be.calledWithExactly(node.id); }); it("Right should not expand node if already expanded", () => { - node2.isExpanded = true; - extendedSelectionManager.onNodeClicked(node2.id, eventMock.object); + const node = createTreeModelNode({ numChildren: 1, isExpanded: true }); + setupModelWithNodes([node]); + extendedSelectionManager.onNodeClicked(node.id, eventMock.object); keyEventMock.setup((x) => x.key).returns(() => SpecialKey.ArrowRight); keyEventMock.setup((x) => x.shiftKey).returns(() => false); keyEventMock.setup((x) => x.ctrlKey).returns(() => false); @@ -245,8 +327,9 @@ describe("TreeSelectionManager", () => { }); it("Left should collapse node", () => { - node2.isExpanded = true; - extendedSelectionManager.onNodeClicked(node2.id, eventMock.object); + const node = createTreeModelNode({ numChildren: 1, isExpanded: true }); + setupModelWithNodes([node]); + extendedSelectionManager.onNodeClicked(node.id, eventMock.object); keyEventMock.setup((x) => x.key).returns(() => SpecialKey.ArrowLeft); keyEventMock.setup((x) => x.shiftKey).returns(() => false); keyEventMock.setup((x) => x.ctrlKey).returns(() => false); @@ -254,12 +337,13 @@ describe("TreeSelectionManager", () => { treeActionsMock.setup((x) => x.onNodeCollapsed).returns(() => spy); extendedSelectionManager.onTreeKeyDown(keyEventMock.object, treeActionsMock.object); extendedSelectionManager.onTreeKeyUp(keyEventMock.object, treeActionsMock.object); - expect(spy).to.be.called; + expect(spy).to.be.calledWithExactly(node.id); }); it("Left should not collapse node if already collapsed", () => { - node2.isExpanded = false; - extendedSelectionManager.onNodeClicked(node2.id, eventMock.object); + const node = createTreeModelNode({ numChildren: 1, isExpanded: false }); + setupModelWithNodes([node]); + extendedSelectionManager.onNodeClicked(node.id, eventMock.object); keyEventMock.setup((x) => x.key).returns(() => SpecialKey.ArrowLeft); keyEventMock.setup((x) => x.shiftKey).returns(() => false); keyEventMock.setup((x) => x.ctrlKey).returns(() => false); @@ -271,8 +355,9 @@ describe("TreeSelectionManager", () => { }); it("Space should expand node if collapsed", () => { - node2.isExpanded = false; - extendedSelectionManager.onNodeClicked(node2.id, eventMock.object); + const node = createTreeModelNode({ numChildren: 1, isExpanded: false }); + setupModelWithNodes([node]); + extendedSelectionManager.onNodeClicked(node.id, eventMock.object); keyEventMock.setup((x) => x.key).returns(() => SpecialKey.Space); keyEventMock.setup((x) => x.shiftKey).returns(() => false); keyEventMock.setup((x) => x.ctrlKey).returns(() => false); @@ -280,12 +365,13 @@ describe("TreeSelectionManager", () => { treeActionsMock.setup((x) => x.onNodeExpanded).returns(() => spy); extendedSelectionManager.onTreeKeyDown(keyEventMock.object, treeActionsMock.object); extendedSelectionManager.onTreeKeyUp(keyEventMock.object, treeActionsMock.object); - expect(spy).to.be.called; + expect(spy).to.be.calledWithExactly(node.id); }); it("Space should collapse node if expanded", () => { - node2.isExpanded = true; - extendedSelectionManager.onNodeClicked(node2.id, eventMock.object); + const node = createTreeModelNode({ numChildren: 1, isExpanded: true }); + setupModelWithNodes([node]); + extendedSelectionManager.onNodeClicked(node.id, eventMock.object); keyEventMock.setup((x) => x.key).returns(() => SpecialKey.Space); keyEventMock.setup((x) => x.shiftKey).returns(() => false); keyEventMock.setup((x) => x.ctrlKey).returns(() => false); @@ -293,10 +379,12 @@ describe("TreeSelectionManager", () => { treeActionsMock.setup((x) => x.onNodeCollapsed).returns(() => spy); extendedSelectionManager.onTreeKeyDown(keyEventMock.object, treeActionsMock.object); extendedSelectionManager.onTreeKeyUp(keyEventMock.object, treeActionsMock.object); - expect(spy).to.be.called; + expect(spy).to.be.calledWithExactly(node.id); }); it("Right should not do anything on a leaf node", () => { + const node = createTreeModelNode({ numChildren: 0, isExpanded: false }); + setupModelWithNodes([node]); extendedSelectionManager.onNodeClicked(node.id, eventMock.object); keyEventMock.setup((x) => x.key).returns(() => SpecialKey.ArrowRight); keyEventMock.setup((x) => x.shiftKey).returns(() => false); @@ -312,6 +400,8 @@ describe("TreeSelectionManager", () => { }); it("Space should start editing on a leaf node", () => { + const node = createTreeModelNode({ numChildren: 0, isExpanded: false }); + setupModelWithNodes([node]); extendedSelectionManager.onNodeClicked(node.id, eventMock.object); keyEventMock.setup((x) => x.key).returns(() => SpecialKey.Space); keyEventMock.setup((x) => x.shiftKey).returns(() => false); @@ -320,7 +410,7 @@ describe("TreeSelectionManager", () => { treeActionsMock.setup((x) => x.onNodeEditorActivated).returns(() => spyEditorActivated); extendedSelectionManager.onTreeKeyDown(keyEventMock.object, treeActionsMock.object); extendedSelectionManager.onTreeKeyUp(keyEventMock.object, treeActionsMock.object); - expect(spyEditorActivated).to.be.called; + expect(spyEditorActivated).to.be.calledWithExactly(node.id); }); }); diff --git a/ui/components/src/ui-components/color/ColorPickerButton.tsx b/ui/components/src/ui-components/color/ColorPickerButton.tsx index 9ffc1065008..5246671c361 100644 --- a/ui/components/src/ui-components/color/ColorPickerButton.tsx +++ b/ui/components/src/ui-components/color/ColorPickerButton.tsx @@ -63,14 +63,9 @@ const ForwardRefColorPickerButton = React.forwardRef { - // istanbul ignore else - if (initialColor !== initialColorRef.current) { - initialColorRef.current = initialColor; - } setColorDef(initialColor); }, [initialColor]); diff --git a/ui/components/src/ui-components/color/HueSlider.scss b/ui/components/src/ui-components/color/HueSlider.scss index 358804e95dd..d9e92c85ddd 100644 --- a/ui/components/src/ui-components/color/HueSlider.scss +++ b/ui/components/src/ui-components/color/HueSlider.scss @@ -28,7 +28,6 @@ height: 100%; border-radius: 1em; background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%); - background: -webkit-linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%); } .components-hue-pointer { @@ -52,8 +51,6 @@ border-radius: 1em; background: linear-gradient(to top, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%); - background: -webkit-linear-gradient(to top, #f00 0%, #ff0 17%, - #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%); } .components-hue-pointer { diff --git a/ui/components/src/ui-components/color/HueSlider.tsx b/ui/components/src/ui-components/color/HueSlider.tsx index aa306920c5a..937cd6b839a 100644 --- a/ui/components/src/ui-components/color/HueSlider.tsx +++ b/ui/components/src/ui-components/color/HueSlider.tsx @@ -17,10 +17,10 @@ import { SpecialKey } from "@bentley/ui-abstract"; // hue is a value from 0 to 360 function calculateHue(currentPos: number, high: number, isVertical: boolean) { // istanbul ignore next - if (currentPos < 0) { - return 359; - } else if (currentPos > high) { - return 0; + if (currentPos <= 0) { + return isVertical?359:0; + } else if (currentPos >= high) { + return isVertical?0:359; } else { let percent = ((currentPos * 100) / high); if (isVertical) diff --git a/ui/components/src/ui-components/datepicker/DatePickerPopupButton.tsx b/ui/components/src/ui-components/datepicker/DatePickerPopupButton.tsx index d21bc91716e..64c7417e57f 100644 --- a/ui/components/src/ui-components/datepicker/DatePickerPopupButton.tsx +++ b/ui/components/src/ui-components/datepicker/DatePickerPopupButton.tsx @@ -47,20 +47,12 @@ export function DatePickerPopupButton({ displayEditField, timeDisplay, selected, const timeLabelRef = React.useRef(UiComponents.translate("datepicker.time")); const toolTipLabelRef = React.useRef(UiComponents.translate("datepicker.selectDate")); const toolTipLabel = React.useMemo(() => buttonToolTip ? buttonToolTip : toolTipLabelRef.current, [buttonToolTip]); - const initialDateRef = React.useRef(selected); - // See if new initialDate props have changed since component mounted + // See if props have changed since component mounted React.useEffect(() => { - // istanbul ignore else - if (selected.getTime() !== initialDateRef.current.getTime()) { - const newWorkingDate = new Date(selected.getTime()); - // istanbul ignore else - if (workingDate.getTime() !== newWorkingDate.getTime()) { - setWorkingDate(newWorkingDate); - } - initialDateRef.current = selected; - } - }, [selected, workingDate]); + const newWorkingDate = new Date(selected.getTime()); + setWorkingDate(newWorkingDate); + }, [selected]); const buttonRef = React.useRef(null); const togglePopupDisplay = React.useCallback((event: React.MouseEvent) => { diff --git a/ui/components/src/ui-components/editors/NumericInputEditor.tsx b/ui/components/src/ui-components/editors/NumericInputEditor.tsx index b7d76e03883..d5cecd03f8d 100644 --- a/ui/components/src/ui-components/editors/NumericInputEditor.tsx +++ b/ui/components/src/ui-components/editors/NumericInputEditor.tsx @@ -37,6 +37,7 @@ interface NumericInputEditorState { export class NumericInputEditor extends React.PureComponent implements TypeEditor { private _isMounted = false; private _inputElement: React.RefObject = React.createRef(); + public hasFocus = false; // hot used since containerHandlesEnter is false /** @internal */ public readonly state: Readonly = { @@ -64,9 +65,16 @@ export class NumericInputEditor extends React.PureComponent => { + // istanbul ignore else + if (this.props.propertyRecord && this.props.onCommit) { + const propertyValue = await this.getPropertyValue(); + // istanbul ignore else + if (propertyValue !== undefined) { + this.props.onCommit({ propertyRecord: this.props.propertyRecord, newValue: propertyValue }); + } + } + }; private _updateValue = (value: number | undefined, _stringValue: string): void => { const newValue = value !== undefined ? value : /* istanbul ignore next */ 0; @@ -75,6 +83,8 @@ export class NumericInputEditor extends React.PureComponent{ + await this._handleCommit (); }); }; @@ -178,6 +188,10 @@ export class NumericInputPropertyEditor extends PropertyEditorBase { public get reactNode(): React.ReactNode { return ; } + // istanbul ignore next + public get containerHandlesEnter(): boolean { // let input editor process enter key + return false; + } } PropertyEditorManager.registerEditor(StandardTypeNames.Number, NumericInputPropertyEditor, StandardEditorNames.NumericInput); diff --git a/ui/components/src/ui-components/oidc/SignIn.scss b/ui/components/src/ui-components/oidc/SignIn.scss index dd06191c464..b91b7552759 100644 --- a/ui/components/src/ui-components/oidc/SignIn.scss +++ b/ui/components/src/ui-components/oidc/SignIn.scss @@ -54,18 +54,12 @@ $signin-hyperlink-hover-color: $buic-foreground-primary-tone; .components-signin-prompt { margin-top: 3em; text-align: center; + height: 7em; } /* sign in button */ .components-signin-button { @include uicore-buttons-primary-large; - margin-top: auto; - } - - /* signing-in message */ - .components-signingin-message { - margin-top: 3em; - text-align: center; } /* "Register" container */ diff --git a/ui/components/src/ui-components/oidc/SignIn.tsx b/ui/components/src/ui-components/oidc/SignIn.tsx index a71af464164..7e1284710fe 100644 --- a/ui/components/src/ui-components/oidc/SignIn.tsx +++ b/ui/components/src/ui-components/oidc/SignIn.tsx @@ -91,7 +91,10 @@ export class SignIn extends React.PureComponent {
- {this.state.prompt} + {(this.state.isSigningIn && this.props.signingInMessage !== undefined) ? + {this.props.signingInMessage} : + {this.state.prompt} + }
); diff --git a/ui/components/src/ui-components/quantityformat/FormatPanel.tsx b/ui/components/src/ui-components/quantityformat/FormatPanel.tsx index 025d3a1a196..8525aaba3a2 100644 --- a/ui/components/src/ui-components/quantityformat/FormatPanel.tsx +++ b/ui/components/src/ui-components/quantityformat/FormatPanel.tsx @@ -46,15 +46,11 @@ export function FormatPanel(props: FormatPanelProps) { const [formatSpec, setFormatSpec] = React.useState(); const { initialFormat, showSample, initialMagnitude, unitsProvider, persistenceUnit, onFormatChange, provideFormatSpec, enableMinimumProperties } = props; const [formatProps, setFormatProps] = React.useState(initialFormat); - const initialFormatRef = React.useRef(initialFormat); const [showOptions, setShowOptions] = React.useState(false); React.useEffect(() => { - if (initialFormatRef.current !== initialFormat) { - initialFormatRef.current = initialFormat; - setFormatProps(initialFormat); - setFormatSpec(undefined); // this will trigger the new spec to be created in the useEffect hook - } + setFormatProps(initialFormat); + setFormatSpec(undefined); // this will trigger the new spec to be created in the useEffect hook }, [initialFormat]); const handleUserFormatChanges = React.useCallback((newProps: FormatProps) => { diff --git a/ui/components/src/ui-components/quantityformat/FormatSample.tsx b/ui/components/src/ui-components/quantityformat/FormatSample.tsx index e5dc796c616..22ff36de509 100644 --- a/ui/components/src/ui-components/quantityformat/FormatSample.tsx +++ b/ui/components/src/ui-components/quantityformat/FormatSample.tsx @@ -26,16 +26,14 @@ export interface FormatSampleProps extends CommonProps { */ export function FormatSample(props: FormatSampleProps) { const { initialMagnitude, formatSpec, hideLabels } = props; - const initialMagnitudeRef = React.useRef(initialMagnitude ?? 0); - const [magnitude, setMagnitude] = React.useState(initialMagnitudeRef.current); - const [sampleValue, setSampleValue] = React.useState(magnitude.toString()); + const initialValue = initialMagnitude??0; + const [magnitude, setMagnitude] = React.useState(initialValue); + const [sampleValue, setSampleValue] = React.useState(initialValue.toString()); React.useEffect(() => { - if (initialMagnitudeRef.current !== initialMagnitude) { - initialMagnitudeRef.current = initialMagnitude ?? 0; - setMagnitude(initialMagnitudeRef.current); - setSampleValue(initialMagnitudeRef.current.toString()); - } + const value = initialMagnitude ?? 0; + setMagnitude(value); + setSampleValue(value.toString()); }, [initialMagnitude]); const handleOnValueBlur = React.useCallback(() => { diff --git a/ui/components/src/ui-components/table/component/Table.tsx b/ui/components/src/ui-components/table/component/Table.tsx index 6c550fa784d..a6204e7c30d 100644 --- a/ui/components/src/ui-components/table/component/Table.tsx +++ b/ui/components/src/ui-components/table/component/Table.tsx @@ -15,7 +15,8 @@ import ReactResizeDetector from "react-resize-detector"; import { DisposableList, Guid, GuidString } from "@bentley/bentleyjs-core"; import { PropertyValueFormat } from "@bentley/ui-abstract"; import { - CommonProps, Dialog, isNavigationKey, ItemKeyboardNavigator, LocalUiSettings, Orientation, SortDirection, UiSettings, UiSettingsStatus, + CommonProps, Dialog, isNavigationKey, ItemKeyboardNavigator, LocalSettingsStorage, + Orientation, SortDirection, UiSettings, UiSettingsStatus, UiSettingsStorage, } from "@bentley/ui-core"; import { MultiSelectionHandler, OnItemsDeselectedCallback, OnItemsSelectedCallback, SelectionHandler, SingleSelectionHandler, @@ -125,6 +126,9 @@ export interface TableProps extends CommonProps { /** Indicates whether the Table columns are reorderable */ reorderableColumns?: boolean; /** Optional parameter for persistent UI settings. Used for column reordering and show persistency. */ + settingsStorage?: UiSettingsStorage; + /** Optional parameter for persistent UI settings. Used for column reordering and show persistency. + * @deprecated use settingsStorage property */ uiSettings?: UiSettings; /** Identifying string used for persistent state. */ settingsIdentifier?: string; @@ -495,8 +499,9 @@ export class Table extends React.Component { let dataGridColumns = columnDescriptions.map(this._columnDescriptionToReactDataGridColumn); if (this.props.settingsIdentifier) { - const uiSettings: UiSettings = this.props.uiSettings || /* istanbul ignore next */ new LocalUiSettings(); - const reorderResult = await uiSettings.getSetting(this.props.settingsIdentifier, "ColumnReorder"); + // eslint-disable-next-line deprecation/deprecation + const settingsStorage: UiSettingsStorage = this.props.settingsStorage || /* istanbul ignore next */ this.props.uiSettings || /* istanbul ignore next */ new LocalSettingsStorage(); + const reorderResult = await settingsStorage.getSetting(this.props.settingsIdentifier, "ColumnReorder"); // istanbul ignore next if (reorderResult.status === UiSettingsStatus.Success) { const setting = reorderResult.setting as string[]; @@ -504,9 +509,9 @@ export class Table extends React.Component { dataGridColumns = setting.map((key) => dataGridColumns.filter((col) => col.key === key)[0]); } else if (reorderResult.status === UiSettingsStatus.NotFound) { const keys = columnDescriptions.map((col) => col.key); - await uiSettings.saveSetting(this.props.settingsIdentifier, "ColumnReorder", keys); + await settingsStorage.saveSetting(this.props.settingsIdentifier, "ColumnReorder", keys); } - const showhideResult = await uiSettings.getSetting(this.props.settingsIdentifier, "ColumnShowHideHiddenColumns"); + const showhideResult = await settingsStorage.getSetting(this.props.settingsIdentifier, "ColumnShowHideHiddenColumns"); // istanbul ignore next if (showhideResult.status === UiSettingsStatus.Success) { const hiddenColumns = showhideResult.setting as string[]; @@ -1216,9 +1221,9 @@ export class Table extends React.Component { cols.splice(columnTargetIndex, 0, cols.splice(columnSourceIndex, 1)[0]); // istanbul ignore else if (this.props.settingsIdentifier) { - const uiSettings: UiSettings = this.props.uiSettings || /* istanbul ignore next */ new LocalUiSettings(); + const settingsStorage: UiSettingsStorage = this.props.settingsStorage || /* istanbul ignore next */ new LocalSettingsStorage(); const keys = cols.map((col) => col.key); - uiSettings.saveSetting(this.props.settingsIdentifier, "ColumnReorder", keys); // eslint-disable-line @typescript-eslint/no-floating-promises + settingsStorage.saveSetting(this.props.settingsIdentifier, "ColumnReorder", keys); // eslint-disable-line @typescript-eslint/no-floating-promises } this.setState({ columns: [] }, () => { // fix react-data-grid update issues this.setState({ columns: cols }); @@ -1347,8 +1352,8 @@ export class Table extends React.Component { private _handleShowHideChange = (cols: string[]) => { this.setState({ hiddenColumns: cols }); if (this.props.settingsIdentifier) { - const uiSettings: UiSettings = this.props.uiSettings || new LocalUiSettings(); - uiSettings.saveSetting(this.props.settingsIdentifier, "ColumnShowHideHiddenColumns", cols); // eslint-disable-line @typescript-eslint/no-floating-promises + const settingsStorage: UiSettingsStorage = this.props.settingsStorage || new LocalSettingsStorage(); + settingsStorage.saveSetting(this.props.settingsIdentifier, "ColumnShowHideHiddenColumns", cols); // eslint-disable-line @typescript-eslint/no-floating-promises } return true; }; diff --git a/ui/components/src/ui-components/tree/controlled/Observable.ts b/ui/components/src/ui-components/tree/controlled/Observable.ts index 6e5d20e0894..17ef4d18f1f 100644 --- a/ui/components/src/ui-components/tree/controlled/Observable.ts +++ b/ui/components/src/ui-components/tree/controlled/Observable.ts @@ -7,6 +7,7 @@ */ import { from as rxjsFrom } from "rxjs/internal/observable/from"; +import { Observable as RxjsObservable } from "rxjs/internal/Observable"; /** * Helper method that creates an Observable from Iterable or Promise. @@ -16,6 +17,11 @@ export function from(iterable: Iterable | PromiseLike): Observable { return rxjsFrom(iterable); } +/** @internal */ +export function toRxjsObservable(observable: Observable): RxjsObservable { + return new RxjsObservable((subscriber) => { observable.subscribe(subscriber); }); +} + /** * Observable interface compatible with [rxjs](https://github.com/ReactiveX/rxjs) * This interface ensures that consumers are not required to have rxjs as dependency. diff --git a/ui/components/src/ui-components/tree/controlled/TreeEventHandler.ts b/ui/components/src/ui-components/tree/controlled/TreeEventHandler.ts index 1b03c51e57a..31401f32597 100644 --- a/ui/components/src/ui-components/tree/controlled/TreeEventHandler.ts +++ b/ui/components/src/ui-components/tree/controlled/TreeEventHandler.ts @@ -6,12 +6,11 @@ * @module Tree */ -import { from } from "rxjs/internal/observable/from"; import { takeUntil } from "rxjs/internal/operators/takeUntil"; import { Subject } from "rxjs/internal/Subject"; import { IDisposable } from "@bentley/bentleyjs-core"; import { TreeModelMutator } from "./internal/TreeModelMutator"; -import { Subscription } from "./Observable"; +import { Subscription, toRxjsObservable } from "./Observable"; import { TreeCheckboxStateChangeEventArgs, TreeEvents, TreeNodeEventArgs, TreeSelectionModificationEventArgs, TreeSelectionReplacementEventArgs, } from "./TreeEvents"; @@ -68,7 +67,7 @@ export class TreeEventHandler implements TreeEvents, IDisposable { /** Expands node and starts loading children. */ public onNodeExpanded({ nodeId }: TreeNodeEventArgs) { - from(this._modelMutator.expandNode(nodeId)).pipe(takeUntil(this._disposed)).subscribe(); + toRxjsObservable(this._modelMutator.expandNode(nodeId)).pipe(takeUntil(this._disposed)).subscribe(); } /** Collapses node */ @@ -78,7 +77,7 @@ export class TreeEventHandler implements TreeEvents, IDisposable { /** Selects and deselects nodes until event is handled, handler is disposed or selection replaced event occurs. */ public onSelectionModified({ modifications }: TreeSelectionModificationEventArgs): Subscription | undefined { - return from(modifications) + return toRxjsObservable(modifications) .pipe( takeUntil(this._disposed), takeUntil(this._selectionReplaced), @@ -95,7 +94,7 @@ export class TreeEventHandler implements TreeEvents, IDisposable { this._selectionReplaced.next(); let firstEmission = true; - return from(replacements) + return toRxjsObservable(replacements) .pipe( takeUntil(this._disposed), takeUntil(this._selectionReplaced), diff --git a/ui/components/src/ui-components/tree/controlled/TreeNodeLoader.ts b/ui/components/src/ui-components/tree/controlled/TreeNodeLoader.ts index 168177bc1bb..d24b2297eae 100644 --- a/ui/components/src/ui-components/tree/controlled/TreeNodeLoader.ts +++ b/ui/components/src/ui-components/tree/controlled/TreeNodeLoader.ts @@ -24,7 +24,7 @@ import { ImmediatelyLoadedTreeNodeItem, isTreeDataProviderInterface, isTreeDataProviderMethod, isTreeDataProviderPromise, isTreeDataProviderRaw, TreeDataChangesListener, TreeDataProvider, TreeDataProviderRaw, TreeNodeItem, } from "../TreeDataProvider"; -import { Observable } from "./Observable"; +import { Observable, toRxjsObservable } from "./Observable"; import { isTreeModelNode, MutableTreeModel, TreeModelNode, TreeModelNodeInput, TreeModelRootNode, TreeNodeItemData } from "./TreeModel"; import { TreeModelSource } from "./TreeModelSource"; @@ -74,7 +74,7 @@ export abstract class AbstractTreeNodeLoader implements ITreeNodeLoader { /** Do not override this method. @see `load` */ public loadNode(parent: TreeModelNode | TreeModelRootNode, childIndex: number): Observable { - return from(this.load(parent, childIndex)).pipe( + return toRxjsObservable(this.load(parent, childIndex)).pipe( map((loadedHierarchy) => { this.updateModel(loadedHierarchy); return { loadedNodes: collectTreeNodeItems(loadedHierarchy.hierarchyItems) }; diff --git a/ui/components/src/ui-components/tree/controlled/component/ControlledTree.tsx b/ui/components/src/ui-components/tree/controlled/component/ControlledTree.tsx index 87c3d9d38c9..ab88fcbf7be 100644 --- a/ui/components/src/ui-components/tree/controlled/component/ControlledTree.tsx +++ b/ui/components/src/ui-components/tree/controlled/component/ControlledTree.tsx @@ -7,19 +7,19 @@ */ import * as React from "react"; -import { from } from "rxjs/internal/observable/from"; import { CommonProps, FillCentered, SpinnerSize } from "@bentley/ui-core"; +import { DelayedSpinner } from "../../../common/DelayedSpinner"; import { SelectionMode } from "../../../common/selection/SelectionModes"; import { UiComponents } from "../../../UiComponents"; import { HighlightableTreeProps } from "../../HighlightingEngine"; import { TreeImageLoader } from "../../ImageLoader"; +import { toRxjsObservable } from "../Observable"; import { TreeEventDispatcher } from "../TreeEventDispatcher"; import { TreeEvents } from "../TreeEvents"; import { isTreeModelNode, TreeModelNode, TreeModelNodePlaceholder, VisibleTreeNodes } from "../TreeModel"; import { ITreeNodeLoader } from "../TreeNodeLoader"; import { TreeNodeRenderer, TreeNodeRendererProps } from "./TreeNodeRenderer"; import { RenderedItemsRange, TreeRenderer, TreeRendererProps } from "./TreeRenderer"; -import { DelayedSpinner } from "../../../common/DelayedSpinner"; /** * Properties for [[ControlledTree]] @@ -101,7 +101,7 @@ export function ControlledTree(props: ControlledTreeProps) { function useRootNodeLoader(visibleNodes: VisibleTreeNodes, nodeLoader: ITreeNodeLoader): boolean { React.useEffect(() => { if (visibleNodes.getNumRootNodes() === undefined) { - const subscription = from(nodeLoader.loadNode(visibleNodes.getModel().getRootNode(), 0)).subscribe(); + const subscription = toRxjsObservable(nodeLoader.loadNode(visibleNodes.getModel().getRootNode(), 0)).subscribe(); return () => subscription.unsubscribe(); } diff --git a/ui/components/src/ui-components/tree/controlled/internal/TreeSelectionManager.ts b/ui/components/src/ui-components/tree/controlled/internal/TreeSelectionManager.ts index abb4241d600..11a120f5768 100644 --- a/ui/components/src/ui-components/tree/controlled/internal/TreeSelectionManager.ts +++ b/ui/components/src/ui-components/tree/controlled/internal/TreeSelectionManager.ts @@ -110,18 +110,13 @@ export class TreeSelectionManager implements Pick>; this._itemHandlers = [itemHandlers]; @@ -129,10 +124,10 @@ export class TreeSelectionManager implements Pick VisibleTreeNodes) { + public setVisibleNodes(visibleNodes: (() => VisibleTreeNodes) | undefined) { this._getVisibleNodes = visibleNodes; } @@ -303,9 +298,9 @@ function isIndividualSelection(selection: Selection | undefined): selection is s } class ItemHandler implements SingleSelectionHandler { - private _node: TreeModelNode; + private _node: TreeModelNode | undefined; - constructor(node: TreeModelNode) { + constructor(node: TreeModelNode | undefined) { this._node = node; } @@ -320,10 +315,11 @@ class ItemHandler implements SingleSelectionHandler { // eslint-disable-next-line @bentley/prefer-get public isSelected(): boolean { - return this._node.isSelected; + // istanbul ignore next + return !!this._node?.isSelected; } public item(): string { - return this._node.id; + return this._node?.id ?? ""; } } diff --git a/ui/core/package.json b/ui/core/package.json index 283d19f5169..308efbf3b92 100644 --- a/ui/core/package.json +++ b/ui/core/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/ui-core", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "iModel.js UI core components", "main": "lib/ui-core.js", "typings": "lib/ui-core", @@ -18,7 +18,7 @@ "clean": "rimraf lib .rush/temp/package-deps*.json", "cover": "nyc npm test", "docs": "betools docs --includes=../../generated-docs/extract --json=../../generated-docs/ui/ui-core/file.json --tsIndexFile=./ui-core.ts --onlyJson", - "lint": "eslint -f visualstudio ./src/**/*.{ts,tsx} 1>&2", + "lint": "eslint -f visualstudio \"./src/**/*.{ts,tsx}\" 1>&2", "extract-api": "betools extract-api --entry=ui-core", "test": "mocha --opts ../mocha.opts \"./lib/test/**/*.test.js\"", "test:watch": "npm test -- --reporter min --watch-extensions ts,tsx --watch" @@ -35,10 +35,10 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/geometry-core": "^2.15.0-dev.6", - "@bentley/imodeljs-i18n": "^2.15.0-dev.6", - "@bentley/ui-abstract": "^2.15.0-dev.6", + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/geometry-core": "^2.15.0-dev.13", + "@bentley/imodeljs-i18n": "^2.15.0-dev.13", + "@bentley/ui-abstract": "^2.15.0-dev.13", "react": "^16.8.0", "react-dom": "^16.8.0" }, @@ -47,13 +47,13 @@ "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", "@bentley/react-scripts": "3.4.9", - "@bentley/ui-abstract": "2.15.0-dev.6", + "@bentley/ui-abstract": "2.15.0-dev.13", "@testing-library/react": "^8.0.1", "@testing-library/react-hooks": "^3.2.1", "@types/chai": "^4.1.4", diff --git a/ui/core/src/test/settings/SettingsManager.test.tsx b/ui/core/src/test/settings/SettingsManager.test.tsx index 5529b58e2ad..f61ddbd39d8 100644 --- a/ui/core/src/test/settings/SettingsManager.test.tsx +++ b/ui/core/src/test/settings/SettingsManager.test.tsx @@ -8,7 +8,7 @@ import { render } from "@testing-library/react"; import { expect } from "chai"; import * as sinon from "sinon"; import { SettingsContainer, useSaveBeforeActivatingNewSettingsTab, useSaveBeforeClosingSettingsContainer } from "../../ui-core/settings/SettingsContainer"; -import { SettingsManager, SettingsProvider, SettingsTabEntry } from "../../ui-core/settings/SettingsManager"; +import { SettingsManager, SettingsTabEntry, SettingsTabsProvider } from "../../ui-core/settings/SettingsManager"; function TestModalSettingsPage({ settingsManager, title }: { settingsManager: SettingsManager, title: string }) { @@ -25,7 +25,7 @@ function TestModalSettingsPage({ settingsManager, title }: { settingsManager: Se describe("", () => { const settingsManager = new SettingsManager(); - class TestSettingsProvider implements SettingsProvider { + class TestSettingsProvider implements SettingsTabsProvider { public readonly id = "AppSettingsProvider"; public getSettingEntries(_stageId: string, _stageUsage: string): ReadonlyArray | undefined { diff --git a/ui/core/src/test/uisettings/LocalUiSettings.test.ts b/ui/core/src/test/uisettings/LocalUiSettings.test.ts index cfcaa0c5aa0..5aa792d1ecf 100644 --- a/ui/core/src/test/uisettings/LocalUiSettings.test.ts +++ b/ui/core/src/test/uisettings/LocalUiSettings.test.ts @@ -2,25 +2,30 @@ * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ + import { expect } from "chai"; -import { LocalUiSettings, UiSettingsStatus } from "../../ui-core"; +import { LocalSettingsStorage, LocalUiSettings, UiSettingsStatus } from "../../ui-core"; import { storageMock } from "../TestUtils"; describe("LocalUiSettings", () => { it("default constructor executes successfully", () => { - const initialLocalUiSettings = new LocalUiSettings(); + const initialLocalUiSettings = new LocalUiSettings(); // eslint-disable-line deprecation/deprecation + expect(initialLocalUiSettings).to.not.be.undefined; + }); + it("default LocalSettingsStorage constructor executes successfully", () => { + const initialLocalUiSettings = new LocalSettingsStorage(); // eslint-disable-line deprecation/deprecation expect(initialLocalUiSettings).to.not.be.undefined; }); describe("saveSetting", () => { - const localUiSettings = new LocalUiSettings({ localStorage: storageMock() } as Window); + const localUiSettings = new LocalSettingsStorage({ localStorage: storageMock() } as Window); it("Should save setting correctly", async () => { const result = await localUiSettings.saveSetting("Testing", "TestData", { test123: "4567" }); expect(result.status).to.equal(UiSettingsStatus.Success); }); }); describe("getSetting", async () => { - const localUiSettings = new LocalUiSettings({ localStorage: storageMock() } as Window); + const localUiSettings = new LocalSettingsStorage({ localStorage: storageMock() } as Window); await localUiSettings.saveSetting("Testing", "TestData", { test123: "4567" }); it("Should load setting correctly", async () => { const result = await localUiSettings.getSetting("Testing", "TestData"); @@ -34,7 +39,7 @@ describe("LocalUiSettings", () => { }); }); describe("deleteSetting", async () => { - const localUiSettings = new LocalUiSettings({ localStorage: storageMock() } as Window); + const localUiSettings = new LocalSettingsStorage({ localStorage: storageMock() } as Window); await localUiSettings.saveSetting("Testing", "TestData", { test123: "4567" }); it("Should remove setting correctly", async () => { const result = await localUiSettings.deleteSetting("Testing", "TestData"); diff --git a/ui/core/src/test/uisettings/SessionUiSettings.test.ts b/ui/core/src/test/uisettings/SessionUiSettings.test.ts index df060b1d79e..994bcfc0636 100644 --- a/ui/core/src/test/uisettings/SessionUiSettings.test.ts +++ b/ui/core/src/test/uisettings/SessionUiSettings.test.ts @@ -3,24 +3,28 @@ * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; -import { SessionUiSettings, UiSettingsStatus } from "../../ui-core"; +import { SessionSettingsStorage, SessionUiSettings, UiSettingsStatus } from "../../ui-core"; import { storageMock } from "../TestUtils"; describe("SessionUiSettings", () => { it("default constructor executes successfully", () => { - const initialSessionUiSettings = new SessionUiSettings(); + const initialSessionUiSettings = new SessionUiSettings(); // eslint-disable-line deprecation/deprecation + expect(initialSessionUiSettings).to.not.be.undefined; + }); + it("default SessionSettingsStorage constructor executes successfully", () => { + const initialSessionUiSettings = new SessionSettingsStorage(); // eslint-disable-line deprecation/deprecation expect(initialSessionUiSettings).to.not.be.undefined; }); describe("saveSetting", () => { - const sessionSettings = new SessionUiSettings({ sessionStorage: storageMock() } as Window); + const sessionSettings = new SessionSettingsStorage({ sessionStorage: storageMock() } as Window); it("Should save setting correctly", async () => { const result = await sessionSettings.saveSetting("Testing", "TestData", { test123: "4567" }); expect(result.status).to.equal(UiSettingsStatus.Success); }); }); describe("getSetting", async () => { - const sessionSettings = new SessionUiSettings({ sessionStorage: storageMock() } as Window); + const sessionSettings = new SessionSettingsStorage({ sessionStorage: storageMock() } as Window); await sessionSettings.saveSetting("Testing", "TestData", { test123: "4567" }); it("Should load setting correctly", async () => { @@ -35,7 +39,7 @@ describe("SessionUiSettings", () => { }); }); describe("deleteSetting", async () => { - const sessionSettings = new SessionUiSettings({ sessionStorage: storageMock() } as Window); + const sessionSettings = new SessionSettingsStorage({ sessionStorage: storageMock() } as Window); await sessionSettings.saveSetting("Testing", "TestData", { test123: "4567" }); it("Should remove setting correctly", async () => { diff --git a/ui/core/src/test/uisettings/UiSetting.test.ts b/ui/core/src/test/uisettings/UiSetting.test.ts index 4c75de1dedb..8b3f3f57e76 100644 --- a/ui/core/src/test/uisettings/UiSetting.test.ts +++ b/ui/core/src/test/uisettings/UiSetting.test.ts @@ -3,7 +3,7 @@ * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; -import { LocalUiSettings, UiSetting, UiSettingsStatus } from "../../ui-core"; +import { LocalSettingsStorage, LocalUiSettings, UiSetting, UiSettingsStatus } from "../../ui-core"; import { storageMock } from "../TestUtils"; function getBoolean(): boolean { return true; } @@ -20,7 +20,7 @@ describe("UiSetting", () => { }); describe("saveSetting", () => { - const localUiSettings = new LocalUiSettings({ localStorage: storageMock() } as Window); + const localUiSettings = new LocalUiSettings({ localStorage: storageMock() } as Window); // eslint-disable-line deprecation/deprecation it("Should save setting correctly", async () => { const uiSetting = new UiSetting("Namespace", "Setting", getBoolean); @@ -29,21 +29,8 @@ describe("UiSetting", () => { }); }); - describe("getSetting", async () => { - const localUiSettings = new LocalUiSettings({ localStorage: storageMock() } as Window); - const uiSetting = new UiSetting("Namespace", "Setting", getString); - await uiSetting.saveSetting(localUiSettings); - - it("Should load setting correctly", async () => { - const result = await uiSetting.getSetting(localUiSettings); - expect(result.status).to.equal(UiSettingsStatus.Success); - expect(result.setting).to.not.be.null; - expect(result.setting).to.equal(getString()); - }); - }); - describe("deleteSetting", async () => { - const localUiSettings = new LocalUiSettings({ localStorage: storageMock() } as Window); + const localUiSettings = new LocalSettingsStorage({ localStorage: storageMock() } as Window); const uiSetting = new UiSetting("Namespace", "Setting", getString); await uiSetting.saveSetting(localUiSettings); @@ -62,7 +49,7 @@ describe("UiSetting", () => { let value = 100; function applyNumber(v: number) { value = v; } - const localUiSettings = new LocalUiSettings({ localStorage: storageMock() } as Window); + const localUiSettings = new LocalSettingsStorage({ localStorage: storageMock() } as Window); const uiSetting = new UiSetting("Namespace", "Setting", getNumber, applyNumber); await uiSetting.saveSetting(localUiSettings); @@ -79,6 +66,16 @@ describe("UiSetting", () => { expect(result.status).to.eq(UiSettingsStatus.Uninitialized); }); + it("Should use default value if no applyValue", async () => { + const defaultValue = 999; + // make sure testing with a new key not yet in mock storage + const uiSetting2 = new UiSetting("Namespace", "TEST-XYZ", getNumber, applyNumber, defaultValue); + const result = await uiSetting2.getSettingAndApplyValue(localUiSettings); + expect(result.status).to.eq(UiSettingsStatus.Success); + expect(result.setting).to.eq(defaultValue); + expect(value).to.eq(defaultValue); + }); + it("Should return NotFound if not saved", async () => { const uiSetting3 = new UiSetting("Namespace", "XYZ", getNumber, applyNumber); const result = await uiSetting3.getSettingAndApplyValue(localUiSettings); diff --git a/ui/core/src/ui-core.ts b/ui/core/src/ui-core.ts index ac65bc3ac51..0e165c9bbf4 100644 --- a/ui/core/src/ui-core.ts +++ b/ui/core/src/ui-core.ts @@ -153,9 +153,9 @@ export { Tree, TreeProps } from "./ui-core/tree/Tree"; export { TreeNodePlaceholder, TreeNodePlaceholderProps } from "./ui-core/tree/Placeholder"; export * from "./ui-core/uisettings/UiSetting"; -export * from "./ui-core/uisettings/UiSettings"; -export * from "./ui-core/uisettings/LocalUiSettings"; -export * from "./ui-core/uisettings/SessionUiSettings"; +export * from "./ui-core/uisettings/UiSettingsStorage"; +export * from "./ui-core/uisettings/LocalSettingsStorage"; +export * from "./ui-core/uisettings/SessionSettingsStorage"; export * from "./ui-core/utils/IconHelper"; export * from "./ui-core/utils/Point"; diff --git a/ui/core/src/ui-core/listbox/Listbox.tsx b/ui/core/src/ui-core/listbox/Listbox.tsx index 9f7d87fa26e..3d584d2e1e4 100644 --- a/ui/core/src/ui-core/listbox/Listbox.tsx +++ b/ui/core/src/ui-core/listbox/Listbox.tsx @@ -115,21 +115,16 @@ function processKeyboardNavigation(optionValues: ListboxItemProps[], itemIndex: export function Listbox(props: ListboxProps) { const { ariaLabel, ariaLabelledBy, id, children, selectedValue, className, onListboxValueChange, onKeyPress, ...otherProps } = props; const listRef = React.useRef(null); - const [listId] = React.useState(id ?? Guid.createValue()); + const [listId] = React.useState(()=>{return id ?? Guid.createValue();}); const optionValues = React.useMemo(() => getOptionValueArray(children), [children]); const classes = React.useMemo(() => classnames("core-listbox", className), [className]); const [currentValue, setCurrentValue] = React.useState(selectedValue); const [focusValue, setFocusValue] = React.useState(currentValue); - const initialSelectedValueRef = React.useRef(selectedValue); React.useEffect(() => { - // istanbul ignore else - if (initialSelectedValueRef.current !== selectedValue) { - initialSelectedValueRef.current = selectedValue; - setCurrentValue(selectedValue); - setFocusValue(selectedValue); - } - }, [currentValue, selectedValue]); + setCurrentValue(selectedValue); + setFocusValue(selectedValue); + }, [selectedValue]); const scrollTopRef = React.useRef(0); const handleValueChange = React.useCallback((newValue: ListboxValue, isControlOrCommandPressed?: boolean) => { diff --git a/ui/core/src/ui-core/settings/SettingsManager.tsx b/ui/core/src/ui-core/settings/SettingsManager.tsx index 62e199993cb..704bf1fdefd 100644 --- a/ui/core/src/ui-core/settings/SettingsManager.tsx +++ b/ui/core/src/ui-core/settings/SettingsManager.tsx @@ -33,7 +33,7 @@ export interface SettingsTabEntry { readonly isDisabled?: boolean | ConditionalBooleanValue; } -/** Event class for [[this.onSettingsProvidersChanged]] which is emitted when a new SettingsProvider is added or removed. +/** Event class for [[this.onSettingsProvidersChanged]] which is emitted when a new SettingsTabsProvider is added or removed. * @beta */ export class SettingsProvidersChangedEvent extends BeUiEvent { } @@ -42,7 +42,7 @@ export class SettingsProvidersChangedEvent extends BeUiEvent; + readonly providers: ReadonlyArray; } /** Event class for [[this.onProcessSettingsTabActivation]] which is emitted when a new Tab needs to be activated. This allows the current @@ -94,7 +94,7 @@ export interface ActivateSettingsTabEventArgs { * classes that implement this interface need to be registered with the [[SettingsManager]]. * @beta */ -export interface SettingsProvider { +export interface SettingsTabsProvider { /** Id of provider, used to remove registration. */ readonly id: string; getSettingEntries(stageId: string, stageUsage: string): ReadonlyArray | undefined; @@ -104,7 +104,7 @@ export interface SettingsProvider { * @beta */ export class SettingsManager { - private _providers: ReadonlyArray = []; + private _providers: ReadonlyArray = []; /** Event raised when SettingsProviders are changed. */ @@ -131,8 +131,8 @@ export class SettingsManager { public readonly onCloseSettingsContainer = new CloseSettingsContainerEvent(); /** @beta */ - public get providers(): ReadonlyArray { return this._providers; } - public set providers(p: ReadonlyArray) { + public get providers(): ReadonlyArray { return this._providers; } + public set providers(p: ReadonlyArray) { this._providers = p; this.onSettingsProvidersChanged.emit({ providers: p }); } @@ -151,7 +151,7 @@ export class SettingsManager { this.onCloseSettingsContainer.emit({ closeFunc, closeFuncArgs }); } - public addSettingsProvider(settingsProvider: SettingsProvider): void { + public addSettingsProvider(settingsProvider: SettingsTabsProvider): void { const foundProvider = this._providers.find((p) => p.id === settingsProvider.id); if (!foundProvider) { const updatedProviders = [ diff --git a/ui/core/src/ui-core/tabs/tabs.scss b/ui/core/src/ui-core/tabs/tabs.scss index fd2f9e2b937..a5b5abc2d53 100644 --- a/ui/core/src/ui-core/tabs/tabs.scss +++ b/ui/core/src/ui-core/tabs/tabs.scss @@ -46,7 +46,10 @@ } .uicore-tabs-icon { - width: ( $uicore-icons-small + 10px ); + width: ( $uicore-icons-small + 12px ); + height: ( $uicore-icons-small + 12px ); + display: flex; + align-items: center; } } diff --git a/ui/core/src/ui-core/uisettings/LocalUiSettings.ts b/ui/core/src/ui-core/uisettings/LocalSettingsStorage.ts similarity index 76% rename from ui/core/src/ui-core/uisettings/LocalUiSettings.ts rename to ui/core/src/ui-core/uisettings/LocalSettingsStorage.ts index 584599f1af7..e82a8212358 100644 --- a/ui/core/src/ui-core/uisettings/LocalUiSettings.ts +++ b/ui/core/src/ui-core/uisettings/LocalSettingsStorage.ts @@ -6,13 +6,13 @@ * @module UiSettings */ -import { UiSettings, UiSettingsResult, UiSettingsStatus } from "./UiSettings"; +import { UiSettingsResult, UiSettingsStatus, UiSettingsStorage } from "./UiSettingsStorage"; /** - * Implementation of [[UiSettings]] using Window.localStorage. - * @beta + * Implementation of [[UiSettingsStorage]] using Window.localStorage. + * @public */ -export class LocalUiSettings implements UiSettings { +export class LocalSettingsStorage implements UiSettingsStorage { constructor(public w: Window = window) { } @@ -38,3 +38,11 @@ export class LocalUiSettings implements UiSettings { return { status: UiSettingsStatus.Success }; } } + +/** Alias for [[LocalSettingsStorage]] + * @beta @deprecated use LocalSettingsStorage + */ +export class LocalUiSettings extends LocalSettingsStorage { + constructor(w: Window = window) { super (w);} +} + diff --git a/ui/core/src/ui-core/uisettings/SessionUiSettings.ts b/ui/core/src/ui-core/uisettings/SessionSettingsStorage.ts similarity index 80% rename from ui/core/src/ui-core/uisettings/SessionUiSettings.ts rename to ui/core/src/ui-core/uisettings/SessionSettingsStorage.ts index 018ba446b2a..c98dbc38757 100644 --- a/ui/core/src/ui-core/uisettings/SessionUiSettings.ts +++ b/ui/core/src/ui-core/uisettings/SessionSettingsStorage.ts @@ -6,13 +6,13 @@ * @module UiSettings */ -import { UiSettings, UiSettingsResult, UiSettingsStatus } from "./UiSettings"; +import { UiSettingsResult, UiSettingsStatus, UiSettingsStorage } from "./UiSettingsStorage"; /** * Implementation of [[UiSettings]] using Window.sessionStorage. - * @beta + * @public */ -export class SessionUiSettings implements UiSettings { +export class SessionSettingsStorage implements UiSettingsStorage { constructor(public w: Window = window) { } @@ -38,3 +38,10 @@ export class SessionUiSettings implements UiSettings { return { status: UiSettingsStatus.Success }; } } + +/** Alias for [[SessionSettingsStorage]] + * @beta @deprecated use SessionSettingsStorage + */ +export class SessionUiSettings extends SessionSettingsStorage { + constructor(w: Window = window) { super (w);} +} diff --git a/ui/core/src/ui-core/uisettings/UiSetting.ts b/ui/core/src/ui-core/uisettings/UiSetting.ts index 0d73740f490..c699da52f4e 100644 --- a/ui/core/src/ui-core/uisettings/UiSetting.ts +++ b/ui/core/src/ui-core/uisettings/UiSetting.ts @@ -6,10 +6,10 @@ * @module UiSettings */ -import { UiSettings, UiSettingsResult, UiSettingsStatus } from "./UiSettings"; +import { UiSettingsResult, UiSettingsStatus, UiSettingsStorage } from "./UiSettingsStorage"; /** A Ui Setting with namespace and setting name. - * @beta + * @public */ export class UiSetting { /** Constructor @@ -17,31 +17,36 @@ export class UiSetting { * @param settingName Name for the setting, passed to UiSettings. * @param getValue Function for getting the value from the application. * @param applyValue Function for applying the setting value to the application. + * @param defaultValue Optional default value if not already stored. */ - public constructor(public settingNamespace: string, public settingName: string, public getValue: () => T, public applyValue?: (v: T) => void) { + public constructor(public settingNamespace: string, public settingName: string, public getValue: () => T, public applyValue?: (v: T) => void, public defaultValue?: T) { } - /** Gets the setting from UiSettings */ - public async getSetting(uiSettings: UiSettings): Promise { - return uiSettings.getSetting(this.settingNamespace, this.settingName); + /** Gets the setting from [[UiSettingsStorage]] */ + public async getSetting(storage: UiSettingsStorage): Promise { + return storage.getSetting(this.settingNamespace, this.settingName); } /** Saves the setting value from the `getValue` function to UiSettings */ - public async saveSetting(uiSettings: UiSettings): Promise { - return uiSettings.saveSetting(this.settingNamespace, this.settingName, this.getValue()); + public async saveSetting(storage: UiSettingsStorage): Promise { + return storage.saveSetting(this.settingNamespace, this.settingName, this.getValue()); } /** Deletes the setting from UiSettings */ - public async deleteSetting(uiSettings: UiSettings): Promise { - return uiSettings.deleteSetting(this.settingNamespace, this.settingName); + public async deleteSetting(storage: UiSettingsStorage): Promise { + return storage.deleteSetting(this.settingNamespace, this.settingName); } /** Gets the setting from UiSettings and applies the value using the `applyValue` function */ - public async getSettingAndApplyValue(uiSettings: UiSettings): Promise { + public async getSettingAndApplyValue(storage: UiSettingsStorage): Promise { if (this.applyValue) { - const result = await this.getSetting(uiSettings); - if (result !== undefined && result.status === UiSettingsStatus.Success) { + const result = await this.getSetting(storage); + if (result.status === UiSettingsStatus.Success) { this.applyValue(result.setting); + } else if (undefined !== this.defaultValue) { + this.applyValue(this.defaultValue); + result.setting = this.defaultValue; + result.status = UiSettingsStatus.Success; } return result; } diff --git a/ui/core/src/ui-core/uisettings/UiSettings.ts b/ui/core/src/ui-core/uisettings/UiSettingsStorage.ts similarity index 80% rename from ui/core/src/ui-core/uisettings/UiSettings.ts rename to ui/core/src/ui-core/uisettings/UiSettingsStorage.ts index 76d912005c6..bddb86cae00 100644 --- a/ui/core/src/ui-core/uisettings/UiSettings.ts +++ b/ui/core/src/ui-core/uisettings/UiSettingsStorage.ts @@ -9,13 +9,18 @@ /** Interface for getting, saving and deleting settings. * @public */ -export interface UiSettings { +export interface UiSettingsStorage { getSetting(settingNamespace: string, settingName: string): Promise; saveSetting(settingNamespace: string, settingName: string, setting: any): Promise; deleteSetting(settingNamespace: string, settingName: string): Promise; } -/** Enum for [[UiSettings]] status. +/** Alias for [[UiSettingsStorage]] + * @public + */ +export type UiSettings = UiSettingsStorage; + +/** Enum for [[UiSettingsStorage]] status. * @public */ export enum UiSettingsStatus { @@ -26,7 +31,7 @@ export enum UiSettingsStatus { AuthorizationError = 4, } -/** Interface for [[UiSettings]] result. +/** Interface for result of accessing setting in [[UiSettingsStorage]]. * @public */ export interface UiSettingsResult { diff --git a/ui/framework/package.json b/ui/framework/package.json index 7947d2cd46d..5422259f99a 100644 --- a/ui/framework/package.json +++ b/ui/framework/package.json @@ -1,6 +1,6 @@ { "name": "@bentley/ui-framework", - "version": "2.15.0-dev.6", + "version": "2.15.0-dev.13", "description": "UI framework", "main": "lib/ui-framework.js", "typings": "lib/ui-framework", @@ -34,24 +34,24 @@ "url": "http://www.bentley.com" }, "peerDependencies": { - "@bentley/bentleyjs-core": "^2.15.0-dev.6", - "@bentley/frontend-authorization-client": "^2.15.0-dev.6", - "@bentley/geometry-core": "^2.15.0-dev.6", - "@bentley/imodelhub-client": "^2.15.0-dev.6", - "@bentley/imodeljs-common": "^2.15.0-dev.6", - "@bentley/imodeljs-frontend": "^2.15.0-dev.6", - "@bentley/imodeljs-quantity": "^2.15.0-dev.6", - "@bentley/imodeljs-i18n": "^2.15.0-dev.6", - "@bentley/imodeljs-markup": "^2.15.0-dev.6", - "@bentley/itwin-client": "^2.15.0-dev.6", - "@bentley/presentation-common": "^2.15.0-dev.6", - "@bentley/presentation-frontend": "^2.15.0-dev.6", - "@bentley/product-settings-client": "^2.15.0-dev.6", - "@bentley/rbac-client": "^2.15.0-dev.6", - "@bentley/ui-abstract": "^2.15.0-dev.6", - "@bentley/ui-components": "^2.15.0-dev.6", - "@bentley/ui-core": "^2.15.0-dev.6", - "@bentley/ui-ninezone": "^2.15.0-dev.6", + "@bentley/bentleyjs-core": "^2.15.0-dev.13", + "@bentley/frontend-authorization-client": "^2.15.0-dev.13", + "@bentley/geometry-core": "^2.15.0-dev.13", + "@bentley/imodelhub-client": "^2.15.0-dev.13", + "@bentley/imodeljs-common": "^2.15.0-dev.13", + "@bentley/imodeljs-frontend": "^2.15.0-dev.13", + "@bentley/imodeljs-quantity": "^2.15.0-dev.13", + "@bentley/imodeljs-i18n": "^2.15.0-dev.13", + "@bentley/imodeljs-markup": "^2.15.0-dev.13", + "@bentley/itwin-client": "^2.15.0-dev.13", + "@bentley/presentation-common": "^2.15.0-dev.13", + "@bentley/presentation-frontend": "^2.15.0-dev.13", + "@bentley/product-settings-client": "^2.15.0-dev.13", + "@bentley/rbac-client": "^2.15.0-dev.13", + "@bentley/ui-abstract": "^2.15.0-dev.13", + "@bentley/ui-components": "^2.15.0-dev.13", + "@bentley/ui-core": "^2.15.0-dev.13", + "@bentley/ui-ninezone": "^2.15.0-dev.13", "react": "^16.8.0", "react-dom": "^16.8.0", "react-redux": "^7.2.0", @@ -62,30 +62,30 @@ "NOTE: All tools used by scripts in this package must be listed as devDependencies" ], "devDependencies": { - "@bentley/bentleyjs-core": "2.15.0-dev.6", - "@bentley/build-tools": "2.15.0-dev.6", - "@bentley/config-loader": "2.15.0-dev.6", - "@bentley/eslint-plugin": "2.15.0-dev.6", - "@bentley/frontend-authorization-client": "2.15.0-dev.6", - "@bentley/geometry-core": "2.15.0-dev.6", - "@bentley/imodelhub-client": "2.15.0-dev.6", - "@bentley/imodeljs-common": "2.15.0-dev.6", - "@bentley/imodeljs-frontend": "2.15.0-dev.6", - "@bentley/imodeljs-quantity": "2.15.0-dev.6", - "@bentley/imodeljs-i18n": "2.15.0-dev.6", - "@bentley/imodeljs-markup": "2.15.0-dev.6", - "@bentley/itwin-client": "2.15.0-dev.6", - "@bentley/presentation-common": "2.15.0-dev.6", - "@bentley/presentation-frontend": "2.15.0-dev.6", - "@bentley/presentation-testing": "2.15.0-dev.6", - "@bentley/product-settings-client": "2.15.0-dev.6", - "@bentley/rbac-client": "2.15.0-dev.6", + "@bentley/bentleyjs-core": "2.15.0-dev.13", + "@bentley/build-tools": "2.15.0-dev.13", + "@bentley/config-loader": "2.15.0-dev.13", + "@bentley/eslint-plugin": "2.15.0-dev.13", + "@bentley/frontend-authorization-client": "2.15.0-dev.13", + "@bentley/geometry-core": "2.15.0-dev.13", + "@bentley/imodelhub-client": "2.15.0-dev.13", + "@bentley/imodeljs-common": "2.15.0-dev.13", + "@bentley/imodeljs-frontend": "2.15.0-dev.13", + "@bentley/imodeljs-quantity": "2.15.0-dev.13", + "@bentley/imodeljs-i18n": "2.15.0-dev.13", + "@bentley/imodeljs-markup": "2.15.0-dev.13", + "@bentley/itwin-client": "2.15.0-dev.13", + "@bentley/presentation-common": "2.15.0-dev.13", + "@bentley/presentation-frontend": "2.15.0-dev.13", + "@bentley/presentation-testing": "2.15.0-dev.13", + "@bentley/product-settings-client": "2.15.0-dev.13", + "@bentley/rbac-client": "2.15.0-dev.13", "@bentley/react-scripts": "3.4.9", - "@bentley/telemetry-client": "2.15.0-dev.6", - "@bentley/ui-abstract": "2.15.0-dev.6", - "@bentley/ui-components": "2.15.0-dev.6", - "@bentley/ui-core": "2.15.0-dev.6", - "@bentley/ui-ninezone": "2.15.0-dev.6", + "@bentley/telemetry-client": "2.15.0-dev.13", + "@bentley/ui-abstract": "2.15.0-dev.13", + "@bentley/ui-components": "2.15.0-dev.13", + "@bentley/ui-core": "2.15.0-dev.13", + "@bentley/ui-ninezone": "2.15.0-dev.13", "@testing-library/react": "^8.0.1", "@testing-library/react-hooks": "^3.2.1", "@types/chai": "^4.1.4", @@ -137,12 +137,12 @@ "NOTE: imodeljs-frontend should remain UI technology agnostic, so no react/angular dependencies are allowed" ], "dependencies": { - "@bentley/context-registry-client": "2.15.0-dev.6", + "@bentley/context-registry-client": "2.15.0-dev.13", "@bentley/icons-generic": "^1.0.15", "@bentley/icons-generic-webfont": "^1.0.15", - "@bentley/presentation-components": "2.15.0-dev.6", + "@bentley/presentation-components": "2.15.0-dev.13", "classnames": "^2.2.5", - "immer": "8.0.1", + "immer": "^9.0.1", "lodash": "^4.17.10", "react-dnd": "^11.1.3", "react-dnd-html5-backend": "^11.1.3", diff --git a/ui/framework/public/locales/en/UiFramework.json b/ui/framework/public/locales/en/UiFramework.json index 75262a3e64e..949a4a3bdca 100644 --- a/ui/framework/public/locales/en/UiFramework.json +++ b/ui/framework/public/locales/en/UiFramework.json @@ -143,6 +143,29 @@ "formatSectionLabel": "Quantity Formats", "setButtonLabel": "Set", "clearButtonLabel": "Clear" + }, + "uiSettingsPage": { + "label": "UI Settings", + "tooltip": "UI Settings", + "light": "Light", + "dark": "Dark", + "systemPreferred": "System preferred", + "themeTitle": "Theme", + "themeDescription": "Toggle the theme between light and dark", + "autoHideTitle": "Auto-Hide UI", + "autoHideDescription": "Toggle the auto-hide of UI after inactivity", + "dragInteractionTitle": "Toolbar Group Buttons show child action item", + "dragInteractionDescription": "Requires long press to open pop-up panel. Single click executes displayed action.", + "newUiTitle": "Use 2.0 Ui", + "newUiDescription": "Use new 2.0 UI layout", + "useProximityOpacityTitle": "Change Toolbar Opacity", + "useProximityOpacityDescription": "Change the toolbar opacity as the mouse gets closer or farther away", + "snapWidgetOpacityTitle": "Snap Toolbar Opacity", + "snapWidgetOpacityDescription": "Immediately change the toolbar opacity when the mouse gets close", + "accuDrawNotificationsTitle": "Display AccuDraw Notifications", + "accuDrawNotificationsDescription": "Display toast notifications when AccuDraw status changes", + "widgetOpacityTitle": "Widget Opacity", + "widgetOpacityDescription": "Opacity when mouse is not hovering for 2.0 floating widgets and all 1.0 widgets" } }, "tools": { diff --git a/ui/framework/src/test/UiFramework.test.ts b/ui/framework/src/test/UiFramework.test.ts index 03e655f0fbd..898697e992d 100644 --- a/ui/framework/src/test/UiFramework.test.ts +++ b/ui/framework/src/test/UiFramework.test.ts @@ -2,6 +2,8 @@ * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ +// cSpell:ignore typemoq, tabid + import { expect } from "chai"; import * as moq from "typemoq"; import * as sinon from "sinon"; @@ -9,231 +11,284 @@ import { Id64String, Logger } from "@bentley/bentleyjs-core"; import { IModelApp, IModelConnection, MockRender, ViewState } from "@bentley/imodeljs-frontend"; import { Presentation } from "@bentley/presentation-frontend"; import { initialize as initializePresentationTesting, terminate as terminatePresentationTesting } from "@bentley/presentation-testing"; -import { ColorTheme, CursorMenuData, SettingsModalFrontstage, UiFramework } from "../ui-framework"; +import { ColorTheme, CursorMenuData, SettingsModalFrontstage, UiFramework, UserSettingsProvider } from "../ui-framework"; import { DefaultIModelServices } from "../ui-framework/clientservices/DefaultIModelServices"; import { DefaultProjectServices } from "../ui-framework/clientservices/DefaultProjectServices"; -import TestUtils, { mockUserInfo } from "./TestUtils"; -import { UiSettings } from "@bentley/ui-core"; +import TestUtils, { mockUserInfo, storageMock } from "./TestUtils"; +import { LocalSettingsStorage, UiSettingsStorage } from "@bentley/ui-core"; import { OpenSettingsTool } from "../ui-framework/tools/OpenSettingsTool"; -describe("UiFramework", () => { +describe("UiFramework localStorage Wrapper", () => { - beforeEach(() => { - TestUtils.terminateUiFramework(); - }); + const localStorageToRestore = Object.getOwnPropertyDescriptor(window, "localStorage")!; + const localStorageMock = storageMock(); - afterEach(() => { - TestUtils.terminateUiFramework(); + before(async () => { + Object.defineProperty(window, "localStorage", { + get: () => localStorageMock, + }); }); - it("store should throw Error without initialize", () => { - expect(() => UiFramework.store).to.throw(Error); + after(() => { + Object.defineProperty(window, "localStorage", localStorageToRestore); }); - it("i18n should throw Error without initialize", () => { - expect(() => UiFramework.i18n).to.throw(Error); - }); + describe("UiFramework", () => { - it("i18nNamespace should return UiFramework", () => { - expect(UiFramework.i18nNamespace).to.eq("UiFramework"); - }); + beforeEach(() => { + TestUtils.terminateUiFramework(); + }); - it("packageName should return ui-framework", () => { - expect(UiFramework.packageName).to.eq("ui-framework"); - }); + afterEach(() => { + TestUtils.terminateUiFramework(); + }); - it("translate should return the key (in test environment)", async () => { - await TestUtils.initializeUiFramework(true); - expect(UiFramework.translate("test1.test2")).to.eq("test1.test2"); - }); + it("store should throw Error without initialize", () => { + expect(() => UiFramework.store).to.throw(Error); + }); - it("test OpenSettingsTool", async () => { - await TestUtils.initializeUiFramework(true); + it("i18n should throw Error without initialize", () => { + expect(() => UiFramework.i18n).to.throw(Error); + }); - const spy = sinon.spy(); - const tabName = "page1"; - const handleOpenSetting = (settingsCategory: string)=> { - expect (settingsCategory).to.eql(tabName); - spy(); - }; + it("i18nNamespace should return UiFramework", () => { + expect(UiFramework.i18nNamespace).to.eq("UiFramework"); + }); - const handleOpenSetting2 = (_settingsCategory: string)=> { - spy(); - }; + it("packageName should return ui-framework", () => { + expect(UiFramework.packageName).to.eq("ui-framework"); + }); - const showSettingsStageToRestore = Object.getOwnPropertyDescriptor(SettingsModalFrontstage, "showSettingsStage")!; - Object.defineProperty(SettingsModalFrontstage, "showSettingsStage", { - get: () => handleOpenSetting, + it("translate should return the key (in test environment)", async () => { + await TestUtils.initializeUiFramework(true); + expect(UiFramework.translate("test1.test2")).to.eq("test1.test2"); }); - const tool = new OpenSettingsTool(); - // tabid arg - tool.parseAndRun(tabName); - spy.calledOnce.should.true; - spy.resetHistory(); - // No tabid arg - Object.defineProperty(SettingsModalFrontstage, "showSettingsStage", { - get: () => handleOpenSetting2, + it("test OpenSettingsTool", async () => { + await TestUtils.initializeUiFramework(true); + + const spy = sinon.spy(); + const tabName = "page1"; + const handleOpenSetting = (settingsCategory: string)=> { + expect (settingsCategory).to.eql(tabName); + spy(); + }; + + const handleOpenSetting2 = (_settingsCategory: string)=> { + spy(); + }; + + const showSettingsStageToRestore = Object.getOwnPropertyDescriptor(SettingsModalFrontstage, "showSettingsStage")!; + Object.defineProperty(SettingsModalFrontstage, "showSettingsStage", { + get: () => handleOpenSetting, + }); + const tool = new OpenSettingsTool(); + // tabid arg + tool.parseAndRun(tabName); + spy.calledOnce.should.true; + spy.resetHistory(); + + // No tabid arg + Object.defineProperty(SettingsModalFrontstage, "showSettingsStage", { + get: () => handleOpenSetting2, + }); + tool.parseAndRun(); + spy.calledOnce.should.true; + spy.resetHistory(); + + Object.defineProperty(SettingsModalFrontstage, "showSettingsStage", showSettingsStageToRestore); }); - tool.parseAndRun(); - spy.calledOnce.should.true; - spy.resetHistory(); - Object.defineProperty(SettingsModalFrontstage, "showSettingsStage", showSettingsStageToRestore); - }); + it("loggerCategory should correctly handle null or undefined object", () => { + expect(UiFramework.loggerCategory(null)).to.eq(UiFramework.packageName); + expect(UiFramework.loggerCategory(undefined)).to.eq(UiFramework.packageName); + }); - it("loggerCategory should correctly handle null or undefined object", () => { - expect(UiFramework.loggerCategory(null)).to.eq(UiFramework.packageName); - expect(UiFramework.loggerCategory(undefined)).to.eq(UiFramework.packageName); - }); + it("calling initialize twice should log", async () => { + const spyLogger = sinon.spy(Logger, "logInfo"); + expect(UiFramework.initialized).to.be.false; + await UiFramework.initialize(TestUtils.store, TestUtils.i18n); + expect(UiFramework.initialized).to.be.true; + await UiFramework.initialize(TestUtils.store, TestUtils.i18n); + spyLogger.calledOnce.should.true; + }); - it("calling initialize twice should log", async () => { - const spyLogger = sinon.spy(Logger, "logInfo"); - expect(UiFramework.initialized).to.be.false; - await UiFramework.initialize(TestUtils.store, TestUtils.i18n); - expect(UiFramework.initialized).to.be.true; - await UiFramework.initialize(TestUtils.store, TestUtils.i18n); - spyLogger.calledOnce.should.true; - }); + it("calling initialize without I18N will use IModelApp.i18n", async () => { + await MockRender.App.startup(); - it("calling initialize without I18N will use IModelApp.i18n", async () => { - await MockRender.App.startup(); + await UiFramework.initialize(TestUtils.store); + expect(UiFramework.i18n).to.eq(IModelApp.i18n); - await UiFramework.initialize(TestUtils.store); - expect(UiFramework.i18n).to.eq(IModelApp.i18n); + await MockRender.App.shutdown(); + }); - await MockRender.App.shutdown(); - }); + it("projectServices should throw Error without initialize", () => { + expect(() => UiFramework.projectServices).to.throw(Error); + }); - it("projectServices should throw Error without initialize", () => { - expect(() => UiFramework.projectServices).to.throw(Error); - }); + it("iModelServices should throw Error without initialize", () => { + expect(() => UiFramework.iModelServices).to.throw(Error); + }); - it("iModelServices should throw Error without initialize", () => { - expect(() => UiFramework.iModelServices).to.throw(Error); - }); + it("projectServices & iModelServices should return defaults", async () => { + await TestUtils.initializeUiFramework(true); + expect(UiFramework.projectServices).to.be.instanceOf(DefaultProjectServices); + expect(UiFramework.iModelServices).to.be.instanceOf(DefaultIModelServices); + expect(UiFramework.frameworkStateKey).to.equal("testDifferentFrameworkKey"); + }); - it("projectServices & iModelServices should return defaults", async () => { - await TestUtils.initializeUiFramework(true); - expect(UiFramework.projectServices).to.be.instanceOf(DefaultProjectServices); - expect(UiFramework.iModelServices).to.be.instanceOf(DefaultIModelServices); - expect(UiFramework.frameworkStateKey).to.equal("testDifferentFrameworkKey"); - }); + it("test default frameworkState key", async () => { + await TestUtils.initializeUiFramework(); + expect(UiFramework.projectServices).to.be.instanceOf(DefaultProjectServices); + expect(UiFramework.iModelServices).to.be.instanceOf(DefaultIModelServices); + expect(UiFramework.frameworkStateKey).to.equal("frameworkState"); + TestUtils.terminateUiFramework(); + }); - it("test default frameworkState key", async () => { - await TestUtils.initializeUiFramework(); - expect(UiFramework.projectServices).to.be.instanceOf(DefaultProjectServices); - expect(UiFramework.iModelServices).to.be.instanceOf(DefaultIModelServices); - expect(UiFramework.frameworkStateKey).to.equal("frameworkState"); - TestUtils.terminateUiFramework(); - }); + it("IsUiVisible", async () => { + await TestUtils.initializeUiFramework(); + UiFramework.setIsUiVisible(false); + expect(UiFramework.getIsUiVisible()).to.be.false; + TestUtils.terminateUiFramework(); + }); - it("IsUiVisible", async () => { - await TestUtils.initializeUiFramework(); - UiFramework.setIsUiVisible(false); - expect(UiFramework.getIsUiVisible()).to.be.false; - TestUtils.terminateUiFramework(); - }); + it("ColorTheme", async () => { + await TestUtils.initializeUiFramework(); + UiFramework.setColorTheme(ColorTheme.Dark); + expect(UiFramework.getColorTheme()).to.eq(ColorTheme.Dark); + TestUtils.terminateUiFramework(); + }); - it("ColorTheme", async () => { - await TestUtils.initializeUiFramework(); - UiFramework.setColorTheme(ColorTheme.Dark); - expect(UiFramework.getColorTheme()).to.eq(ColorTheme.Dark); - TestUtils.terminateUiFramework(); - }); + it("test selection scope state data", async () => { + await TestUtils.initializeUiFramework(); + expect(UiFramework.getActiveSelectionScope()).to.equal("element"); + const scopes = UiFramework.getAvailableSelectionScopes(); + expect(scopes.length).to.be.greaterThan(0); - it("test selection scope state data", async () => { - await TestUtils.initializeUiFramework(); - expect(UiFramework.getActiveSelectionScope()).to.equal("element"); - const scopes = UiFramework.getAvailableSelectionScopes(); - expect(scopes.length).to.be.greaterThan(0); + // since "file" is not a valid scope the active scope should still be element + UiFramework.setActiveSelectionScope("file"); + expect(UiFramework.getActiveSelectionScope()).to.equal("element"); + TestUtils.terminateUiFramework(); + }); - // since "file" is not a valid scope the active scope should still be element - UiFramework.setActiveSelectionScope("file"); - expect(UiFramework.getActiveSelectionScope()).to.equal("element"); - TestUtils.terminateUiFramework(); - }); + it("WidgetOpacity", async () => { + await TestUtils.initializeUiFramework(); + const testValue = 0.50; + UiFramework.setWidgetOpacity(testValue); + expect(UiFramework.getWidgetOpacity()).to.eq(testValue); + TestUtils.terminateUiFramework(); + }); - it("WidgetOpacity", async () => { - await TestUtils.initializeUiFramework(); - const testValue = 0.50; - UiFramework.setWidgetOpacity(testValue); - expect(UiFramework.getWidgetOpacity()).to.eq(testValue); - TestUtils.terminateUiFramework(); - }); + it("ActiveIModelId", async () => { + await TestUtils.initializeUiFramework(); + const testValue = "Test"; + UiFramework.setActiveIModelId(testValue); + expect(UiFramework.getActiveIModelId()).to.eq(testValue); + TestUtils.terminateUiFramework(); + }); - it("ActiveIModelId", async () => { - await TestUtils.initializeUiFramework(); - const testValue = "Test"; - UiFramework.setActiveIModelId(testValue); - expect(UiFramework.getActiveIModelId()).to.eq(testValue); - TestUtils.terminateUiFramework(); - }); + class testSettingsProvider implements UserSettingsProvider { + public readonly providerId = "testSettingsProvider"; + public settingsLoaded = false; + public async loadUserSettings(storage: UiSettingsStorage) { + if (storage) + this.settingsLoaded = true; + } + } - it("SessionState setters/getters", async () => { - await TestUtils.initializeUiFramework(); + it("SessionState setters/getters", async () => { + await TestUtils.initializeUiFramework(); + const settingsProvider = new testSettingsProvider(); + UiFramework.registerUserSettingsProvider(settingsProvider); - const userInfo = mockUserInfo(); + const userInfo = mockUserInfo(); - UiFramework.setUserInfo(userInfo); - expect(UiFramework.getUserInfo()!.id).to.eq(userInfo.id); + UiFramework.setUserInfo(userInfo); + expect(UiFramework.getUserInfo()!.id).to.eq(userInfo.id); - UiFramework.setDefaultIModelViewportControlId("DefaultIModelViewportControlId"); - expect(UiFramework.getDefaultIModelViewportControlId()).to.eq("DefaultIModelViewportControlId"); + UiFramework.setDefaultIModelViewportControlId("DefaultIModelViewportControlId"); + expect(UiFramework.getDefaultIModelViewportControlId()).to.eq("DefaultIModelViewportControlId"); - const testViewId: Id64String = "0x12345678"; - UiFramework.setDefaultViewId(testViewId); - expect(UiFramework.getDefaultViewId()).to.eq(testViewId); + const testViewId: Id64String = "0x12345678"; + UiFramework.setDefaultViewId(testViewId); + expect(UiFramework.getDefaultViewId()).to.eq(testViewId); - const imodelMock = moq.Mock.ofType(); - UiFramework.setIModelConnection(imodelMock.object); - expect(UiFramework.getIModelConnection()).to.eq(imodelMock.object); + const imodelMock = moq.Mock.ofType(); + UiFramework.setIModelConnection(imodelMock.object); + expect(UiFramework.getIModelConnection()).to.eq(imodelMock.object); - const uisettingsMock = moq.Mock.ofType(); - UiFramework.setUiSettings(uisettingsMock.object); - expect(UiFramework.getUiSettings()).to.eq(uisettingsMock.object); + expect(settingsProvider.settingsLoaded).to.be.false; - UiFramework.closeCursorMenu(); - expect(UiFramework.getCursorMenuData()).to.be.undefined; + const uisettings = new LocalSettingsStorage(); + await UiFramework.setUiSettingsStorage(uisettings); + expect(UiFramework.getUiSettingsStorage()).to.eq(uisettings); + expect(settingsProvider.settingsLoaded).to.be.true; + settingsProvider.settingsLoaded = false; + // if we try to set storage to same object this should be a noop and the settingsLoaded property should remain false; + await UiFramework.setUiSettingsStorage(uisettings); + expect(settingsProvider.settingsLoaded).to.be.false; - const menuData: CursorMenuData = { items: [], position: { x: 100, y: 100 } }; - UiFramework.openCursorMenu(menuData); - expect(UiFramework.getCursorMenuData()).not.to.be.undefined; + const uiVersion1 = "1"; + UiFramework.setUiVersion (uiVersion1); + expect(UiFramework.uiVersion).to.eql(uiVersion1); - const viewState = moq.Mock.ofType(); - UiFramework.setDefaultViewState(viewState.object); - expect(UiFramework.getDefaultViewState()).not.to.be.undefined; - }); + const uiVersion = "2"; + UiFramework.setUiVersion (uiVersion); + expect(UiFramework.uiVersion).to.eql(uiVersion); + UiFramework.setUiVersion (""); + expect(UiFramework.uiVersion).to.eql(uiVersion); -}); + const useDragInteraction = true; + UiFramework.setUseDragInteraction (useDragInteraction); + expect(UiFramework.useDragInteraction).to.eql(useDragInteraction); -// before we can test setting scope to a valid scope id we must make sure Presentation Manager is initialized. -describe("Requires Presentation", () => { - const shutdownIModelApp = async () => { - if (IModelApp.initialized) - await IModelApp.shutdown(); - }; - - beforeEach(async () => { - await shutdownIModelApp(); - Presentation.terminate(); - await initializePresentationTesting(); - }); + UiFramework.closeCursorMenu(); + expect(UiFramework.getCursorMenuData()).to.be.undefined; - afterEach(async () => { - await terminatePresentationTesting(); - }); + const menuData: CursorMenuData = { items: [], position: { x: 100, y: 100 } }; + UiFramework.openCursorMenu(menuData); + expect(UiFramework.getCursorMenuData()).not.to.be.undefined; - describe("initialize and setActiveSelectionScope", () => { + const viewState = moq.Mock.ofType(); + UiFramework.setDefaultViewState(viewState.object); + expect(UiFramework.getDefaultViewState()).not.to.be.undefined; - it("creates manager instances", async () => { - await TestUtils.initializeUiFramework(); - UiFramework.setActiveSelectionScope("element"); TestUtils.terminateUiFramework(); - Presentation.terminate(); - await shutdownIModelApp(); + // try again when store is not defined + expect(UiFramework.useDragInteraction).to.eql(false); }); + }); + // before we can test setting scope to a valid scope id we must make sure Presentation Manager is initialized. + describe("Requires Presentation", () => { + const shutdownIModelApp = async () => { + if (IModelApp.initialized) + await IModelApp.shutdown(); + }; + + beforeEach(async () => { + await shutdownIModelApp(); + Presentation.terminate(); + await initializePresentationTesting(); + }); + + afterEach(async () => { + await terminatePresentationTesting(); + }); + + describe("initialize and setActiveSelectionScope", () => { + + it("creates manager instances", async () => { + await TestUtils.initializeUiFramework(); + UiFramework.setActiveSelectionScope("element"); + TestUtils.terminateUiFramework(); + + Presentation.terminate(); + await shutdownIModelApp(); + }); + }); + + }); }); diff --git a/ui/framework/src/test/accudraw/FrameworkAccuDraw.test.ts b/ui/framework/src/test/accudraw/FrameworkAccuDraw.test.ts index a730cc199f3..cf75225fa5a 100644 --- a/ui/framework/src/test/accudraw/FrameworkAccuDraw.test.ts +++ b/ui/framework/src/test/accudraw/FrameworkAccuDraw.test.ts @@ -5,179 +5,209 @@ import * as sinon from "sinon"; import { expect } from "chai"; import { BeButtonEvent, CompassMode, CurrentState, IModelApp, IModelAppOptions, ItemField, MockRender, RotationMode } from "@bentley/imodeljs-frontend"; -import TestUtils from "../TestUtils"; +import TestUtils, { storageMock } from "../TestUtils"; import { FrameworkAccuDraw } from "../../ui-framework/accudraw/FrameworkAccuDraw"; import { AccuDrawUiAdmin, ConditionalBooleanValue } from "@bentley/ui-abstract"; import { FrameworkUiAdmin } from "../../ui-framework/uiadmin/FrameworkUiAdmin"; +import { UiFramework } from "../../ui-framework/UiFramework"; // cspell:ignore dont uiadmin -describe("FrameworkAccuDraw", () => { - before(async () => { - await TestUtils.initializeUiFramework(); - - const opts: IModelAppOptions = {}; - opts.accuDraw = new FrameworkAccuDraw(); - opts.uiAdmin = new FrameworkUiAdmin(); - await MockRender.App.startup(opts); - }); - - after(async () => { - await MockRender.App.shutdown(); - TestUtils.terminateUiFramework(); - }); - - it("FrameworkAccuDraw.displayNotifications should set & return correctly", () => { - FrameworkAccuDraw.displayNotifications = false; - expect(FrameworkAccuDraw.displayNotifications).to.be.false; - FrameworkAccuDraw.displayNotifications = true; - expect(FrameworkAccuDraw.displayNotifications).to.be.true; - }); - - it("should call onCompassModeChange & emit onAccuDrawSetModeEvent & set conditionals", () => { - FrameworkAccuDraw.displayNotifications = true; - const spy = sinon.spy(); - const spyMessage = sinon.spy(IModelApp.notifications, "outputMessage"); - const remove = AccuDrawUiAdmin.onAccuDrawSetModeEvent.addListener(spy); - - IModelApp.accuDraw.setCompassMode(CompassMode.Polar); - FrameworkAccuDraw.isPolarModeConditional.refresh(); - expect(ConditionalBooleanValue.getValue(FrameworkAccuDraw.isPolarModeConditional)).to.be.true; - spy.calledOnce.should.true; - spyMessage.calledOnce.should.true; - spyMessage.resetHistory(); - - IModelApp.accuDraw.setCompassMode(CompassMode.Rectangular); - FrameworkAccuDraw.isRectangularModeConditional.refresh(); - expect(ConditionalBooleanValue.getValue(FrameworkAccuDraw.isRectangularModeConditional)).to.be.true; - spy.calledTwice.should.true; - spyMessage.calledOnce.should.true; - spyMessage.resetHistory(); - - FrameworkAccuDraw.displayNotifications = false; - IModelApp.accuDraw.setCompassMode(CompassMode.Polar); - spyMessage.called.should.false; - spyMessage.resetHistory(); - - remove(); - }); +describe("FrameworkAccuDraw localStorage Wrapper", () => { - it("should call onFieldLockChange & emit onAccuDrawSetFieldLockEvent", () => { - const spy = sinon.spy(); - const remove = AccuDrawUiAdmin.onAccuDrawSetFieldLockEvent.addListener(spy); - IModelApp.accuDraw.setFieldLock(ItemField.X_Item, true); - spy.calledOnce.should.true; - spy.resetHistory(); - IModelApp.accuDraw.setFieldLock(ItemField.Y_Item, true); - spy.calledOnce.should.true; - spy.resetHistory(); - IModelApp.accuDraw.setFieldLock(ItemField.Z_Item, true); - spy.calledOnce.should.true; - spy.resetHistory(); - IModelApp.accuDraw.setFieldLock(ItemField.ANGLE_Item, true); - spy.calledOnce.should.true; - spy.resetHistory(); - IModelApp.accuDraw.setFieldLock(ItemField.DIST_Item, true); - spy.calledOnce.should.true; - spy.resetHistory(); - remove(); - }); - - it("should set rotation & conditionals correctly & notify", () => { - FrameworkAccuDraw.displayNotifications = true; - const spyMessage = sinon.spy(IModelApp.notifications, "outputMessage"); - - IModelApp.accuDraw.setRotationMode(RotationMode.Top); - FrameworkAccuDraw.isTopRotationConditional.refresh(); - expect(ConditionalBooleanValue.getValue(FrameworkAccuDraw.isTopRotationConditional)).to.be.true; - spyMessage.calledOnce.should.true; - spyMessage.resetHistory(); - IModelApp.accuDraw.setRotationMode(RotationMode.Front); - FrameworkAccuDraw.isFrontRotationConditional.refresh(); - expect(ConditionalBooleanValue.getValue(FrameworkAccuDraw.isFrontRotationConditional)).to.be.true; - spyMessage.calledOnce.should.true; - spyMessage.resetHistory(); - IModelApp.accuDraw.setRotationMode(RotationMode.Side); - FrameworkAccuDraw.isSideRotationConditional.refresh(); - expect(ConditionalBooleanValue.getValue(FrameworkAccuDraw.isSideRotationConditional)).to.be.true; - spyMessage.calledOnce.should.true; - spyMessage.resetHistory(); - IModelApp.accuDraw.setRotationMode(RotationMode.View); - FrameworkAccuDraw.isViewRotationConditional.refresh(); - expect(ConditionalBooleanValue.getValue(FrameworkAccuDraw.isViewRotationConditional)).to.be.true; - spyMessage.calledOnce.should.true; - spyMessage.resetHistory(); - IModelApp.accuDraw.setRotationMode(RotationMode.ACS); - FrameworkAccuDraw.isACSRotationConditional.refresh(); - expect(ConditionalBooleanValue.getValue(FrameworkAccuDraw.isACSRotationConditional)).to.be.true; - spyMessage.calledOnce.should.true; - spyMessage.resetHistory(); - IModelApp.accuDraw.setRotationMode(RotationMode.Context); - FrameworkAccuDraw.isContextRotationConditional.refresh(); - expect(ConditionalBooleanValue.getValue(FrameworkAccuDraw.isContextRotationConditional)).to.be.true; - spyMessage.calledOnce.should.true; - spyMessage.resetHistory(); - - FrameworkAccuDraw.displayNotifications = false; - IModelApp.accuDraw.setRotationMode(RotationMode.Top); - spyMessage.calledOnce.should.false; - spyMessage.resetHistory(); - }); - - it("should call onFieldValueChange & emit onAccuDrawSetFieldValueToUiEvent", () => { - const spy = sinon.spy(); - const remove = AccuDrawUiAdmin.onAccuDrawSetFieldValueToUiEvent.addListener(spy); - IModelApp.accuDraw.setValueByIndex(ItemField.X_Item, 1.0); - IModelApp.accuDraw.onFieldValueChange(ItemField.X_Item); - spy.calledOnce.should.true; - remove(); - }); + const localStorageToRestore = Object.getOwnPropertyDescriptor(window, "localStorage")!; + const localStorageMock = storageMock(); - it("should emit onAccuDrawSetFieldFocusEvent", () => { - const spy = sinon.spy(); - const remove = AccuDrawUiAdmin.onAccuDrawSetFieldFocusEvent.addListener(spy); - IModelApp.accuDraw.setFocusItem(ItemField.X_Item); - spy.calledOnce.should.true; - remove(); - }); - - it("should emit onAccuDrawGrabInputFocusEvent", () => { - const spy = sinon.spy(); - const remove = AccuDrawUiAdmin.onAccuDrawGrabInputFocusEvent.addListener(spy); - IModelApp.accuDraw.grabInputFocus(); - spy.calledOnce.should.true; - remove(); + before(async () => { + Object.defineProperty(window, "localStorage", { + get: () => localStorageMock, + }); }); - it("hasInputFocus should return false", () => { - expect(IModelApp.accuDraw.hasInputFocus).to.be.false; + after(() => { + Object.defineProperty(window, "localStorage", localStorageToRestore); }); - it("should emit onAccuDrawSetFieldValueToUiEvent & onAccuDrawSetFieldFocusEvent", () => { - const spyValue = sinon.spy(); - const remove = AccuDrawUiAdmin.onAccuDrawSetFieldValueToUiEvent.addListener(spyValue); - const spyFocus = sinon.spy(); - const removeFocusSpy = AccuDrawUiAdmin.onAccuDrawSetFieldFocusEvent.addListener(spyFocus); - - IModelApp.accuDraw.currentState = CurrentState.Deactivated; - IModelApp.accuDraw.onMotion(new BeButtonEvent()); - spyValue.called.should.false; - spyValue.resetHistory(); - - IModelApp.accuDraw.currentState = CurrentState.Active; - IModelApp.accuDraw.onMotion(new BeButtonEvent()); - spyValue.called.should.true; - spyFocus.called.should.true; - spyValue.resetHistory(); - spyFocus.resetHistory(); - - IModelApp.accuDraw.dontMoveFocus = true; - IModelApp.accuDraw.onMotion(new BeButtonEvent()); - spyValue.called.should.true; - spyFocus.called.should.false; - - remove(); - removeFocusSpy(); + describe("FrameworkAccuDraw", () => { + before(async () => { + await TestUtils.initializeUiFramework(); + + const opts: IModelAppOptions = {}; + opts.accuDraw = new FrameworkAccuDraw(); + opts.uiAdmin = new FrameworkUiAdmin(); + await MockRender.App.startup(opts); + }); + + after(async () => { + await MockRender.App.shutdown(); + TestUtils.terminateUiFramework(); + }); + + it("FrameworkAccuDraw.displayNotifications should set & return correctly", () => { + FrameworkAccuDraw.displayNotifications = false; + expect(FrameworkAccuDraw.displayNotifications).to.be.false; + FrameworkAccuDraw.displayNotifications = true; + expect(FrameworkAccuDraw.displayNotifications).to.be.true; + }); + + it("should call onCompassModeChange & emit onAccuDrawSetModeEvent & set conditionals", () => { + FrameworkAccuDraw.displayNotifications = true; + const spy = sinon.spy(); + const spyMessage = sinon.spy(IModelApp.notifications, "outputMessage"); + const remove = AccuDrawUiAdmin.onAccuDrawSetModeEvent.addListener(spy); + + IModelApp.accuDraw.setCompassMode(CompassMode.Polar); + FrameworkAccuDraw.isPolarModeConditional.refresh(); + expect(ConditionalBooleanValue.getValue(FrameworkAccuDraw.isPolarModeConditional)).to.be.true; + spy.calledOnce.should.true; + spyMessage.calledOnce.should.true; + spyMessage.resetHistory(); + + IModelApp.accuDraw.setCompassMode(CompassMode.Rectangular); + FrameworkAccuDraw.isRectangularModeConditional.refresh(); + expect(ConditionalBooleanValue.getValue(FrameworkAccuDraw.isRectangularModeConditional)).to.be.true; + spy.calledTwice.should.true; + spyMessage.calledOnce.should.true; + spyMessage.resetHistory(); + + FrameworkAccuDraw.displayNotifications = false; + IModelApp.accuDraw.setCompassMode(CompassMode.Polar); + spyMessage.called.should.false; + spyMessage.resetHistory(); + + remove(); + }); + + it("should call onFieldLockChange & emit onAccuDrawSetFieldLockEvent", () => { + const spy = sinon.spy(); + const remove = AccuDrawUiAdmin.onAccuDrawSetFieldLockEvent.addListener(spy); + IModelApp.accuDraw.setFieldLock(ItemField.X_Item, true); + spy.calledOnce.should.true; + spy.resetHistory(); + IModelApp.accuDraw.setFieldLock(ItemField.Y_Item, true); + spy.calledOnce.should.true; + spy.resetHistory(); + IModelApp.accuDraw.setFieldLock(ItemField.Z_Item, true); + spy.calledOnce.should.true; + spy.resetHistory(); + IModelApp.accuDraw.setFieldLock(ItemField.ANGLE_Item, true); + spy.calledOnce.should.true; + spy.resetHistory(); + IModelApp.accuDraw.setFieldLock(ItemField.DIST_Item, true); + spy.calledOnce.should.true; + spy.resetHistory(); + remove(); + }); + + it("should set rotation & conditionals correctly & notify", () => { + FrameworkAccuDraw.displayNotifications = true; + const spyMessage = sinon.spy(IModelApp.notifications, "outputMessage"); + + IModelApp.accuDraw.setRotationMode(RotationMode.Top); + FrameworkAccuDraw.isTopRotationConditional.refresh(); + expect(ConditionalBooleanValue.getValue(FrameworkAccuDraw.isTopRotationConditional)).to.be.true; + spyMessage.calledOnce.should.true; + spyMessage.resetHistory(); + IModelApp.accuDraw.setRotationMode(RotationMode.Front); + FrameworkAccuDraw.isFrontRotationConditional.refresh(); + expect(ConditionalBooleanValue.getValue(FrameworkAccuDraw.isFrontRotationConditional)).to.be.true; + spyMessage.calledOnce.should.true; + spyMessage.resetHistory(); + IModelApp.accuDraw.setRotationMode(RotationMode.Side); + FrameworkAccuDraw.isSideRotationConditional.refresh(); + expect(ConditionalBooleanValue.getValue(FrameworkAccuDraw.isSideRotationConditional)).to.be.true; + spyMessage.calledOnce.should.true; + spyMessage.resetHistory(); + IModelApp.accuDraw.setRotationMode(RotationMode.View); + FrameworkAccuDraw.isViewRotationConditional.refresh(); + expect(ConditionalBooleanValue.getValue(FrameworkAccuDraw.isViewRotationConditional)).to.be.true; + spyMessage.calledOnce.should.true; + spyMessage.resetHistory(); + IModelApp.accuDraw.setRotationMode(RotationMode.ACS); + FrameworkAccuDraw.isACSRotationConditional.refresh(); + expect(ConditionalBooleanValue.getValue(FrameworkAccuDraw.isACSRotationConditional)).to.be.true; + spyMessage.calledOnce.should.true; + spyMessage.resetHistory(); + IModelApp.accuDraw.setRotationMode(RotationMode.Context); + FrameworkAccuDraw.isContextRotationConditional.refresh(); + expect(ConditionalBooleanValue.getValue(FrameworkAccuDraw.isContextRotationConditional)).to.be.true; + spyMessage.calledOnce.should.true; + spyMessage.resetHistory(); + + FrameworkAccuDraw.displayNotifications = false; + IModelApp.accuDraw.setRotationMode(RotationMode.Top); + spyMessage.calledOnce.should.false; + spyMessage.resetHistory(); + }); + + it("should call onFieldValueChange & emit onAccuDrawSetFieldValueToUiEvent", () => { + const spy = sinon.spy(); + const remove = AccuDrawUiAdmin.onAccuDrawSetFieldValueToUiEvent.addListener(spy); + IModelApp.accuDraw.setValueByIndex(ItemField.X_Item, 1.0); + IModelApp.accuDraw.onFieldValueChange(ItemField.X_Item); + spy.calledOnce.should.true; + remove(); + }); + + it("should emit onAccuDrawSetFieldFocusEvent", () => { + const spy = sinon.spy(); + const remove = AccuDrawUiAdmin.onAccuDrawSetFieldFocusEvent.addListener(spy); + IModelApp.accuDraw.setFocusItem(ItemField.X_Item); + spy.calledOnce.should.true; + remove(); + }); + + it("should emit onAccuDrawGrabInputFocusEvent", () => { + const spy = sinon.spy(); + const remove = AccuDrawUiAdmin.onAccuDrawGrabInputFocusEvent.addListener(spy); + IModelApp.accuDraw.grabInputFocus(); + spy.calledOnce.should.true; + remove(); + }); + + it("hasInputFocus should return false", () => { + expect(IModelApp.accuDraw.hasInputFocus).to.be.false; + }); + + it("should emit onAccuDrawSetFieldValueToUiEvent & onAccuDrawSetFieldFocusEvent", () => { + const spyValue = sinon.spy(); + const remove = AccuDrawUiAdmin.onAccuDrawSetFieldValueToUiEvent.addListener(spyValue); + const spyFocus = sinon.spy(); + const removeFocusSpy = AccuDrawUiAdmin.onAccuDrawSetFieldFocusEvent.addListener(spyFocus); + + IModelApp.accuDraw.currentState = CurrentState.Deactivated; + IModelApp.accuDraw.onMotion(new BeButtonEvent()); + spyValue.called.should.false; + spyValue.resetHistory(); + + IModelApp.accuDraw.currentState = CurrentState.Active; + IModelApp.accuDraw.onMotion(new BeButtonEvent()); + spyValue.called.should.true; + spyFocus.called.should.true; + spyValue.resetHistory(); + spyFocus.resetHistory(); + + IModelApp.accuDraw.dontMoveFocus = true; + IModelApp.accuDraw.onMotion(new BeButtonEvent()); + spyValue.called.should.true; + spyFocus.called.should.false; + + remove(); + removeFocusSpy(); + }); + + it("should save/retrieve displayNotifications to/from user storage", async () => { + FrameworkAccuDraw.displayNotifications = true; + await TestUtils.flushAsyncOperations(); + expect(FrameworkAccuDraw.displayNotifications).to.be.true; + FrameworkAccuDraw.displayNotifications = false; + await TestUtils.flushAsyncOperations(); + expect(FrameworkAccuDraw.displayNotifications).to.be.false; + + const instance = new FrameworkAccuDraw(); + await instance.loadUserSettings (UiFramework.getUiSettingsStorage()); + await TestUtils.flushAsyncOperations(); + expect(FrameworkAccuDraw.displayNotifications).to.be.false; + }); }); - }); diff --git a/ui/framework/src/test/frontstage/FrontstageDef.test.tsx b/ui/framework/src/test/frontstage/FrontstageDef.test.tsx index d9786a4db03..5ec08182cdb 100644 --- a/ui/framework/src/test/frontstage/FrontstageDef.test.tsx +++ b/ui/framework/src/test/frontstage/FrontstageDef.test.tsx @@ -6,9 +6,9 @@ import { expect } from "chai"; import * as React from "react"; import * as sinon from "sinon"; import { MockRender } from "@bentley/imodeljs-frontend"; -import { ContentLayoutDef, CoreTools, Frontstage, FrontstageDef, FrontstageManager, FrontstageProps, FrontstageProvider, StagePanelDef, UiFramework, WidgetDef, WidgetState } from "../../ui-framework"; +import { ContentLayoutDef, CoreTools, Frontstage, FrontstageDef, FrontstageManager, FrontstageProps, FrontstageProvider, StagePanelDef, UiFramework, WidgetDef } from "../../ui-framework"; import TestUtils from "../TestUtils"; -import { AbstractWidgetProps, StagePanelLocation, StagePanelSection, UiItemsManager, UiItemsProvider } from "@bentley/ui-abstract"; +import { AbstractWidgetProps, StagePanelLocation, StagePanelSection, UiItemsManager, UiItemsProvider, WidgetState } from "@bentley/ui-abstract"; import { addFloatingWidget, addPanelWidget, addTab, createNineZoneState } from "@bentley/ui-ninezone"; describe("FrontstageDef", () => { @@ -173,7 +173,7 @@ describe("FrontstageDef", () => { describe("float and dock widget", () => { it("should float", () => { - let state = createNineZoneState({size: {height: 1000, width: 1600}}); + let state = createNineZoneState({ size: { height: 1000, width: 1600 } }); state = addPanelWidget(state, "right", "rightStart", ["t1"], { minimized: true }); state = addPanelWidget(state, "right", "rightMiddle", ["t2"]); state = addPanelWidget(state, "right", "rightEnd", ["t3"]); @@ -187,7 +187,7 @@ describe("float and dock widget", () => { sinon.stub(UiFramework, "uiVersion").get(() => "2"); sinon.stub(frontstageDef, "nineZoneState").get(() => state).set(nineZoneStateSetter); sinon.stub(frontstageDef, "nineZoneState").get(() => state); - frontstageDef.floatWidget("t1", {x:55, y:105}); + frontstageDef.floatWidget("t1", { x: 55, y: 105 }); nineZoneStateSetter.calledOnce.should.true; frontstageDef.floatWidget("ta"); @@ -195,7 +195,7 @@ describe("float and dock widget", () => { }); it("should not float if v1", () => { - let state = createNineZoneState({size: {height: 1000, width: 1600}}); + let state = createNineZoneState({ size: { height: 1000, width: 1600 } }); state = addPanelWidget(state, "right", "rightStart", ["t1"], { minimized: true }); state = addPanelWidget(state, "right", "rightMiddle", ["t2"]); state = addPanelWidget(state, "right", "rightEnd", ["t3"]); @@ -208,7 +208,7 @@ describe("float and dock widget", () => { sinon.stub(UiFramework, "uiVersion").get(() => "1"); sinon.stub(frontstageDef, "nineZoneState").get(() => state).set(nineZoneStateSetter); - frontstageDef.floatWidget("t1", {x:55, y:105}); + frontstageDef.floatWidget("t1", { x: 55, y: 105 }); nineZoneStateSetter.calledOnce.should.not.be.true; sinon.stub(UiFramework, "uiVersion").get(() => ""); @@ -217,7 +217,7 @@ describe("float and dock widget", () => { }); it("should dock", () => { - let state = createNineZoneState({size: {height: 1000, width: 1600}}); + let state = createNineZoneState({ size: { height: 1000, width: 1600 } }); state = addFloatingWidget(state, "fw1", ["t1"]); state = addPanelWidget(state, "right", "rightMiddle", ["t2"]); state = addPanelWidget(state, "right", "rightEnd", ["t3"]); @@ -241,7 +241,7 @@ describe("float and dock widget", () => { }); it("should not dock if V1", () => { - let state = createNineZoneState({size: {height: 1000, width: 1600}}); + let state = createNineZoneState({ size: { height: 1000, width: 1600 } }); state = addFloatingWidget(state, "fw1", ["t1"]); state = addPanelWidget(state, "right", "rightMiddle", ["t2"]); state = addPanelWidget(state, "right", "rightEnd", ["t3"]); diff --git a/ui/framework/src/test/frontstage/ModalSettingsStage.test.tsx b/ui/framework/src/test/frontstage/ModalSettingsStage.test.tsx index 21629aceb64..f9bdb0267d3 100644 --- a/ui/framework/src/test/frontstage/ModalSettingsStage.test.tsx +++ b/ui/framework/src/test/frontstage/ModalSettingsStage.test.tsx @@ -9,7 +9,7 @@ import { render } from "@testing-library/react"; import { CoreTools, FrontstageDef, FrontstageManager, FrontstageProps, ModalFrontstage, ModalFrontstageInfo, SettingsModalFrontstage } from "../../ui-framework"; import TestUtils from "../TestUtils"; import { UiFramework } from "../../ui-framework/UiFramework"; -import { SettingsManager, SettingsProvider, SettingsTabEntry, useSaveBeforeActivatingNewSettingsTab, useSaveBeforeClosingSettingsContainer } from "@bentley/ui-core"; +import { SettingsManager, SettingsTabEntry, SettingsTabsProvider, useSaveBeforeActivatingNewSettingsTab, useSaveBeforeClosingSettingsContainer } from "@bentley/ui-core"; import { IModelApp, MockRender } from "@bentley/imodeljs-frontend"; import { ConditionalBooleanValue } from "@bentley/ui-abstract"; @@ -88,7 +88,7 @@ describe("ModalSettingsStage", () => { expect (ConditionalBooleanValue.getValue(backstageActionItem.isHidden)).to.be.true; }); - class TestSettingsProvider implements SettingsProvider { + class TestSettingsProvider implements SettingsTabsProvider { public readonly id = "AppSettingsProvider"; public getSettingEntries(_stageId: string, _stageUsage: string): ReadonlyArray | undefined { diff --git a/ui/framework/src/test/popup/KeyinPalettePanel.test.tsx b/ui/framework/src/test/popup/KeyinPalettePanel.test.tsx index 2318a6179c4..8116defafa7 100644 --- a/ui/framework/src/test/popup/KeyinPalettePanel.test.tsx +++ b/ui/framework/src/test/popup/KeyinPalettePanel.test.tsx @@ -51,13 +51,13 @@ describe("", () => { }); it("test clearKeyinPaletteHistory", async () => { - const uiSettings = UiFramework.getUiSettings(); - if (uiSettings) { - await uiSettings.saveSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY, ["keyin1", "keyin2"]); - let settingsResult = await uiSettings.getSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY); + const uiSettingsStorage = UiFramework.getUiSettingsStorage(); + if (uiSettingsStorage) { + await uiSettingsStorage.saveSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY, ["keyin1", "keyin2"]); + let settingsResult = await uiSettingsStorage.getSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY); expect(UiSettingsStatus.Success === settingsResult.status); clearKeyinPaletteHistory(); - settingsResult = await uiSettings.getSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY); + settingsResult = await uiSettingsStorage.getSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY); expect(UiSettingsStatus.NotFound === settingsResult.status); } }); @@ -197,9 +197,9 @@ describe("", () => { }); it("Renders and filters out bogus history entry", async () => { - const uiSettings = UiFramework.getUiSettings(); - if (uiSettings) { - await uiSettings.saveSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY, ["history1", "history2", "bogus"]); + const uiSettingsStorage = UiFramework.getUiSettingsStorage(); + if (uiSettingsStorage) { + await uiSettingsStorage.saveSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY, ["history1", "history2", "bogus"]); } const keyins: KeyinEntry[] = [{ value: "keyin one" }, { value: "keyin two" }]; const renderedComponent = render(); @@ -212,9 +212,9 @@ describe("", () => { }); it("handles key presses in select input ", async () => { - const uiSettings = UiFramework.getUiSettings(); - if (uiSettings) { - await uiSettings.saveSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY, ["history1", "history2", "bogus"]); + const uiSettingsStorage = UiFramework.getUiSettingsStorage(); + if (uiSettingsStorage) { + await uiSettingsStorage.saveSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY, ["history1", "history2", "bogus"]); } const keyins: KeyinEntry[] = [{ value: "keyin one" }, { value: "keyin two" }]; const renderedComponent = render(); @@ -233,9 +233,9 @@ describe("", () => { }); it("handles ctrl+key presses in select input ", async () => { - const uiSettings = UiFramework.getUiSettings(); - if (uiSettings) { - await uiSettings.saveSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY, ["history1", "history2", "bogus"]); + const uiSettingsStorage = UiFramework.getUiSettingsStorage(); + if (uiSettingsStorage) { + await uiSettingsStorage.saveSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY, ["history1", "history2", "bogus"]); } const keyins: KeyinEntry[] = [{ value: "keyin one" }, { value: "keyin two" }]; const renderedComponent = render(); @@ -272,9 +272,9 @@ describe("", () => { }); it("Handles listbox click processing", async () => { - const uiSettings = UiFramework.getUiSettings(); - if (uiSettings) { - await uiSettings.saveSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY, ["history1", "history2", "bogus"]); + const uiSettingsStorage = UiFramework.getUiSettingsStorage(); + if (uiSettingsStorage) { + await uiSettingsStorage.saveSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY, ["history1", "history2", "bogus"]); } const keyins: KeyinEntry[] = [{ value: "keyin one" }, { value: "keyin two" }]; const renderedComponent = render(); @@ -292,9 +292,9 @@ describe("", () => { }); it("Handles listbox CTRL+click processing", async () => { - const uiSettings = UiFramework.getUiSettings(); - if (uiSettings) { - await uiSettings.saveSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY, ["history1", "history2", "bogus"]); + const uiSettingsStorage = UiFramework.getUiSettingsStorage(); + if (uiSettingsStorage) { + await uiSettingsStorage.saveSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY, ["history1", "history2", "bogus"]); } const keyins: KeyinEntry[] = [{ value: "keyin one" }, { value: "keyin two" }]; const renderedComponent = render(); diff --git a/ui/framework/src/test/redux/StateManager.test.ts b/ui/framework/src/test/redux/StateManager.test.ts index a72958a66f1..80f236db4ef 100644 --- a/ui/framework/src/test/redux/StateManager.test.ts +++ b/ui/framework/src/test/redux/StateManager.test.ts @@ -5,8 +5,10 @@ import { expect } from "chai"; import { UiError } from "@bentley/ui-abstract"; -import { ActionCreatorsObject, ActionsUnion, createAction, FrameworkReducer, ReducerRegistryInstance } from "../../ui-framework"; +import { ActionCreatorsObject, ActionsUnion, createAction, FrameworkReducer, ReducerRegistryInstance, SYSTEM_PREFERRED_COLOR_THEME, WIDGET_OPACITY_DEFAULT } from "../../ui-framework"; import { StateManager } from "../../ui-framework/redux/StateManager"; +import { ConfigurableUiActions, ConfigurableUiReducer, ConfigurableUiState } from "../../ui-framework/configurableui/state"; +import { SnapMode } from "@bentley/imodeljs-frontend"; // Fake state for the host app interface IAppState { @@ -179,3 +181,35 @@ describe("StateManager", () => { }); }); + +describe("ConfigurableUiReducer", () => { + it("should process actions", () => { + // exercise the ConfigurableUiActions + const initialState: ConfigurableUiState = { + snapMode: SnapMode.NearestKeypoint as number, + toolPrompt: "", + theme: SYSTEM_PREFERRED_COLOR_THEME, + widgetOpacity: WIDGET_OPACITY_DEFAULT, + useDragInteraction: false, + frameworkVersion: "2", + }; + + let outState = ConfigurableUiReducer(initialState, ConfigurableUiActions.setDragInteraction(true)); + expect(outState.useDragInteraction).to.be.true; + + outState = ConfigurableUiReducer(initialState, ConfigurableUiActions.setToolPrompt("Hello-From-Tool")); + expect(outState.toolPrompt).to.be.eql("Hello-From-Tool"); + + outState = ConfigurableUiReducer(initialState, ConfigurableUiActions.setTheme("dark")); + expect(outState.theme).to.be.eql("dark"); + + outState = ConfigurableUiReducer(initialState, ConfigurableUiActions.setWidgetOpacity(.75)); + expect(outState.widgetOpacity).to.be.eql(.75); + + outState = ConfigurableUiReducer(initialState, ConfigurableUiActions.setSnapMode(SnapMode.Center)); + expect(outState.snapMode).to.be.eql(SnapMode.Center); + + outState = ConfigurableUiReducer(initialState, ConfigurableUiActions.setFrameworkVersion("1")); + expect(outState.frameworkVersion).to.be.eql("1"); + }); +}); diff --git a/ui/framework/src/test/settings/QuantityFormat.test.tsx b/ui/framework/src/test/settings/QuantityFormat.test.tsx index 38f57cfb2c0..c8397d31863 100644 --- a/ui/framework/src/test/settings/QuantityFormat.test.tsx +++ b/ui/framework/src/test/settings/QuantityFormat.test.tsx @@ -19,7 +19,7 @@ import { ModalDialogRenderer } from "../../ui-framework/dialog/ModalDialogManage import { FormatProps } from "@bentley/imodeljs-quantity"; import { UiFramework } from "../../ui-framework/UiFramework"; -describe("QuantityFormatSettingsPanel", () => { +describe("QuantityFormatSettingsPage", () => { let presentationManagerMock: moq.IMock; const sandbox = sinon.createSandbox(); diff --git a/ui/framework/src/test/settings/UiSettingsPage.test.tsx b/ui/framework/src/test/settings/UiSettingsPage.test.tsx new file mode 100644 index 00000000000..3eccdeafa98 --- /dev/null +++ b/ui/framework/src/test/settings/UiSettingsPage.test.tsx @@ -0,0 +1,183 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Bentley Systems, Incorporated. All rights reserved. +* See LICENSE.md in the project root for license terms and full copyright notice. +*--------------------------------------------------------------------------------------------*/ +import { expect } from "chai"; +import * as React from "react"; +import { fireEvent, render } from "@testing-library/react"; +import { getUiSettingsManagerEntry, UiSettingsPage } from "../../ui-framework/settings/ui/UiSettingsPage"; +import TestUtils, { storageMock } from "../TestUtils"; +import { UiFramework } from "../../ui-framework/UiFramework"; + +describe("UiSettingsPage", () => { + const localStorageToRestore = Object.getOwnPropertyDescriptor(window, "localStorage")!; + let localStorageMock = storageMock(); + + beforeEach(async () => { + // create a new mock each run so there are no "stored values" + localStorageMock = storageMock(); + await TestUtils.initializeUiFramework(); + Object.defineProperty(window, "localStorage", { + get: () => localStorageMock, + }); + }); + + afterEach(() => { + TestUtils.terminateUiFramework(); + Object.defineProperty(window, "localStorage", localStorageToRestore); + }); + + function getInputBySpanTitle(titleSpan: HTMLElement) { + const settingsItemDiv = titleSpan.parentElement?.parentElement; + expect(settingsItemDiv).not.to.be.undefined; + return settingsItemDiv!.querySelector("input"); + } + + it("renders using getUiSettingsManagerEntry (V1)", async () => { + const tabEntry = getUiSettingsManagerEntry (10, false); + const wrapper = render(tabEntry.page); + expect(wrapper).not.to.be.undefined; + expect(wrapper.container.querySelectorAll("span.title").length).to.eq(3); + wrapper.unmount(); + }); + + function getSelectBySpanTitle(titleSpan: HTMLElement) { + const settingsItemDiv = titleSpan.parentElement?.parentElement; + expect(settingsItemDiv).not.to.be.undefined; + return settingsItemDiv!.querySelector("select"); + } + + it("renders without version option (V1) set theme", async () => { + const wrapper = render(); + expect(wrapper).not.to.be.undefined; + + const themeSpan = wrapper.getByText("settings.uiSettingsPage.themeTitle"); + const themeSelect = getSelectBySpanTitle (themeSpan); + expect(themeSelect).not.to.be.null; + await TestUtils.flushAsyncOperations(); + fireEvent.change(themeSelect!, { target: {value: "dark"}}); + await TestUtils.flushAsyncOperations(); + expect(themeSelect!.value).to.be.eq("dark"); + fireEvent.change(themeSelect!, { target: {value: "light"}}); + await TestUtils.flushAsyncOperations(); + expect(themeSelect!.value).to.be.eq("light"); + wrapper.unmount(); + }); + + it("renders without version option (V1) set widget opacity", async () => { + const wrapper = render(); + expect(wrapper).not.to.be.undefined; + const handles = wrapper.container.querySelectorAll(".core-slider-handle"); + expect(handles.length).to.eq(1); + fireEvent.mouseDown(handles[0]); + fireEvent.mouseUp(handles[0]); + await TestUtils.flushAsyncOperations(); + // trigger sync event processing + UiFramework.setWidgetOpacity(.5); + await TestUtils.flushAsyncOperations(); + wrapper.unmount(); + }); + + it("renders without version option (V1) toggle auto-hide", async () => { + const wrapper = render(); + expect(wrapper).not.to.be.undefined; + const autoHideSpan = wrapper.getByText("settings.uiSettingsPage.autoHideTitle"); + const checkbox = getInputBySpanTitle (autoHideSpan); + expect(checkbox).not.to.be.null; + fireEvent.click(checkbox!); + await TestUtils.flushAsyncOperations(); + expect(checkbox?.checked).to.be.true; + fireEvent.click(checkbox!); + await TestUtils.flushAsyncOperations(); + expect(checkbox?.checked).to.be.false; + expect(wrapper.container.querySelectorAll("span.title").length).to.eq(3); + wrapper.unmount(); + }); + + it("renders with version option (V1)", async () => { + const wrapper = render(); + expect(wrapper).not.to.be.undefined; + expect(wrapper.container.querySelectorAll("span.title").length).to.eq(4); + + const titleSpan = wrapper.getByText("settings.uiSettingsPage.newUiTitle"); + const checkbox = getInputBySpanTitle (titleSpan); + expect(checkbox).not.to.be.null; + fireEvent.click(checkbox!); + await TestUtils.flushAsyncOperations(); + expect(wrapper.container.querySelectorAll("span.title").length).to.eq(7); + + wrapper.unmount(); + }); + + it("renders without version option (V2) toggle drag interaction", async () => { + UiFramework.setUiVersion("2"); + await TestUtils.flushAsyncOperations(); + const wrapper = render(); + expect(wrapper).not.to.be.undefined; + + const titleSpan = wrapper.getByText("settings.uiSettingsPage.dragInteractionTitle"); + const checkbox = getInputBySpanTitle (titleSpan); + fireEvent.click(checkbox!); + await TestUtils.flushAsyncOperations(); + expect(checkbox?.checked).to.be.true; + fireEvent.click(checkbox!); + await TestUtils.flushAsyncOperations(); + expect(checkbox?.checked).to.be.false; + wrapper.unmount(); + }); + + it("renders without version option (V2) toggle useProximityOpacity", async () => { + UiFramework.setUiVersion("2"); + await TestUtils.flushAsyncOperations(); + const wrapper = render(); + expect(wrapper).not.to.be.undefined; + + const titleSpan = wrapper.getByText("settings.uiSettingsPage.useProximityOpacityTitle"); + const checkbox = getInputBySpanTitle (titleSpan); + fireEvent.click(checkbox!); + await TestUtils.flushAsyncOperations(); + expect(checkbox?.checked).to.be.false; + fireEvent.click(checkbox!); + await TestUtils.flushAsyncOperations(); + expect(checkbox?.checked).to.be.true; + wrapper.unmount(); + }); + + it("renders without version option (V2) toggle snapWidgetOpacity", async () => { + UiFramework.setUiVersion("2"); + await TestUtils.flushAsyncOperations(); + const wrapper = render(); + expect(wrapper).not.to.be.undefined; + + const titleSpan = wrapper.getByText("settings.uiSettingsPage.snapWidgetOpacityTitle"); + const checkbox = getInputBySpanTitle (titleSpan); + fireEvent.click(checkbox!); + await TestUtils.flushAsyncOperations(); + expect(checkbox?.checked).to.be.true; + fireEvent.click(checkbox!); + await TestUtils.flushAsyncOperations(); + expect(checkbox?.checked).to.be.false; + wrapper.unmount(); + }); + + it("renders with version option (V2) toggle ui-version", async () => { + UiFramework.setUiVersion("2"); + await TestUtils.flushAsyncOperations(); + const wrapper = render(); + expect(wrapper).not.to.be.undefined; + expect(wrapper.container.querySelectorAll("span.title").length).to.eq(7); + const uiVersionSpan = wrapper.getByText("settings.uiSettingsPage.newUiTitle"); + const checkbox = getInputBySpanTitle (uiVersionSpan); + + fireEvent.click(checkbox!); + await TestUtils.flushAsyncOperations(); + expect(wrapper.container.querySelectorAll("span.title").length).to.eq(4); + + fireEvent.click(checkbox!); + await TestUtils.flushAsyncOperations(); + expect(wrapper.container.querySelectorAll("span.title").length).to.eq(7); + + wrapper.unmount(); + }); + +}); diff --git a/ui/framework/src/test/statusbar/StatusBarComposer.test.tsx b/ui/framework/src/test/statusbar/StatusBarComposer.test.tsx index 11e2d3b007b..c93f25b6c48 100644 --- a/ui/framework/src/test/statusbar/StatusBarComposer.test.tsx +++ b/ui/framework/src/test/statusbar/StatusBarComposer.test.tsx @@ -8,12 +8,12 @@ import * as sinon from "sinon"; import { IModelApp, NoRenderApp } from "@bentley/imodeljs-frontend"; import { AbstractStatusBarItemUtilities, CommonStatusBarItem, ConditionalBooleanValue, StageUsage, StatusBarLabelSide, StatusBarSection, UiItemsManager, - UiItemsProvider, + UiItemsProvider, WidgetState, } from "@bentley/ui-abstract"; import { fireEvent, render } from "@testing-library/react"; import { ActivityCenterField, ConfigurableCreateInfo, ConfigurableUiControlType, MessageCenterField, StatusBar, StatusBarComposer, StatusBarItem, - StatusBarItemUtilities, StatusBarWidgetControl, SyncUiEventDispatcher, WidgetDef, WidgetState, withMessageCenterFieldProps, withStatusFieldProps, + StatusBarItemUtilities, StatusBarWidgetControl, SyncUiEventDispatcher, WidgetDef, withMessageCenterFieldProps, withStatusFieldProps, } from "../../ui-framework"; import TestUtils, { mount } from "../TestUtils"; import { createDOMRect } from "../Utils"; diff --git a/ui/framework/src/test/statusfields/toolassistance/ToolAssistanceField.test.tsx b/ui/framework/src/test/statusfields/toolassistance/ToolAssistanceField.test.tsx index eb3caf2cf44..611869c45ac 100644 --- a/ui/framework/src/test/statusfields/toolassistance/ToolAssistanceField.test.tsx +++ b/ui/framework/src/test/statusfields/toolassistance/ToolAssistanceField.test.tsx @@ -9,7 +9,7 @@ import * as sinon from "sinon"; import { Logger } from "@bentley/bentleyjs-core"; import { MockRender, ToolAssistance, ToolAssistanceImage, ToolAssistanceInputMethod } from "@bentley/imodeljs-frontend"; import { WidgetState } from "@bentley/ui-abstract"; -import { LocalUiSettings, Toggle } from "@bentley/ui-core"; +import { LocalSettingsStorage, Toggle } from "@bentley/ui-core"; import { FooterPopup, TitleBarButton } from "@bentley/ui-ninezone"; import { AppNotificationManager, ConfigurableCreateInfo, ConfigurableUiControlType, CursorPopupManager, FrontstageManager, StatusBar, StatusBarWidgetControl, @@ -18,11 +18,11 @@ import { import TestUtils, { mount, storageMock } from "../../TestUtils"; describe("ToolAssistanceField", () => { - const uiSettings = new LocalUiSettings({ localStorage: storageMock() } as Window); + const uiSettingsStorage = new LocalSettingsStorage({ localStorage: storageMock() } as Window); before(async () => { - await uiSettings.saveSetting("ToolAssistance", "showPromptAtCursor", true); - await uiSettings.saveSetting("ToolAssistance", "mouseTouchTabIndex", 0); + await uiSettingsStorage.saveSetting("ToolAssistance", "showPromptAtCursor", true); + await uiSettingsStorage.saveSetting("ToolAssistance", "mouseTouchTabIndex", 0); }); class AppStatusBarWidgetControl extends StatusBarWidgetControl { @@ -35,7 +35,7 @@ describe("ToolAssistanceField", () => { <> + uiSettings={uiSettingsStorage} /> ); } diff --git a/ui/framework/src/test/uisettings/AppUiSettings.test.ts b/ui/framework/src/test/uisettings/AppUiSettings.test.ts new file mode 100644 index 00000000000..9bc1e23c932 --- /dev/null +++ b/ui/framework/src/test/uisettings/AppUiSettings.test.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Bentley Systems, Incorporated. All rights reserved. +* See LICENSE.md in the project root for license terms and full copyright notice. +*--------------------------------------------------------------------------------------------*/ +import { expect } from "chai"; +import { storageMock, TestUtils } from "../TestUtils"; +import { UiFramework } from "../../ui-framework/UiFramework"; +import { AppUiSettings } from "../../ui-framework/uisettings/AppUiSettings"; +import { SYSTEM_PREFERRED_COLOR_THEME } from "../../ui-framework/theme/ThemeManager"; + +describe("AppUiSettings", () => { + const localStorageToRestore = Object.getOwnPropertyDescriptor(window, "localStorage")!; + let localStorageMock = storageMock(); + + beforeEach(async () => { + // create a new mock each run so there are no "stored values" + localStorageMock = storageMock(); + await TestUtils.initializeUiFramework(); + Object.defineProperty(window, "localStorage", { + get: () => localStorageMock, + }); + }); + + afterEach(() => { + TestUtils.terminateUiFramework(); + Object.defineProperty(window, "localStorage", localStorageToRestore); + }); + + it("should get/set settings", async () => { + + const uiSetting = new AppUiSettings({}); + await uiSetting.loadUserSettings(UiFramework.getUiSettingsStorage()); + const uiVersion = "2"; + const opacity = 0.5; + const colorTheme = "dark"; + const useDragInteraction = true; + UiFramework.setUiVersion (uiVersion); + UiFramework.setWidgetOpacity(opacity); + UiFramework.setUseDragInteraction(true); + UiFramework.setColorTheme(colorTheme); + UiFramework.setUseDragInteraction(useDragInteraction); + await TestUtils.flushAsyncOperations(); + expect(UiFramework.uiVersion).to.eql(uiVersion); + expect(UiFramework.getWidgetOpacity()).to.eql(opacity); + expect(UiFramework.getColorTheme()).to.eql(colorTheme); + expect(UiFramework.useDragInteraction).to.eql(useDragInteraction); + }); + + it("should used default settings", async () => { + const defaults = { + colorTheme: SYSTEM_PREFERRED_COLOR_THEME, + dragInteraction: false, + frameworkVersion: "2", + widgetOpacity: 0.8, + }; + + const uiSetting = new AppUiSettings(defaults); + await uiSetting.loadUserSettings(UiFramework.getUiSettingsStorage()); + await TestUtils.flushAsyncOperations(); + expect(UiFramework.uiVersion).to.eql(defaults.frameworkVersion); + expect(UiFramework.getWidgetOpacity()).to.eql(defaults.widgetOpacity); + expect(UiFramework.getColorTheme()).to.eql(defaults.colorTheme); + expect(UiFramework.useDragInteraction).to.eql(defaults.dragInteraction); + }); + +}); diff --git a/ui/framework/src/test/uisettings/IModelAppUiSettings.test.ts b/ui/framework/src/test/uisettings/UserSettingsStorage.test.ts similarity index 92% rename from ui/framework/src/test/uisettings/IModelAppUiSettings.test.ts rename to ui/framework/src/test/uisettings/UserSettingsStorage.test.ts index f75c609a6e0..1c6eb00937a 100644 --- a/ui/framework/src/test/uisettings/IModelAppUiSettings.test.ts +++ b/ui/framework/src/test/uisettings/UserSettingsStorage.test.ts @@ -7,10 +7,10 @@ import { expect } from "chai"; import { AuthorizedFrontendRequestContext, IModelApp, MockRender } from "@bentley/imodeljs-frontend"; import { SettingsAdmin, SettingsResult, SettingsStatus } from "@bentley/product-settings-client"; import { UiSettingsStatus } from "@bentley/ui-core"; -import { IModelAppUiSettings, settingsStatusToUiSettingsStatus } from "../../ui-framework"; +import { settingsStatusToUiSettingsStatus, UserSettingsStorage } from "../../ui-framework"; import { TestUtils } from "../TestUtils"; -describe("IModelAppUiSettings", () => { +describe("UserSettingsStorage", () => { before(async () => { await TestUtils.initializeUiFramework(); await MockRender.App.startup(); @@ -30,7 +30,7 @@ describe("IModelAppUiSettings", () => { saveUserSetting, })); sinon.stub(IModelApp, "authorizationClient").get(() => ({ hasSignedIn: true })); - const sut = new IModelAppUiSettings(); + const sut = new UserSettingsStorage(); await sut.saveSetting("TESTNAMESPACE", "TESTNAME", "testvalue"); saveUserSetting.calledOnceWithExactly(sinon.match.any, "testvalue", "TESTNAMESPACE", "TESTNAME", true).should.true; }); @@ -41,7 +41,7 @@ describe("IModelAppUiSettings", () => { deleteUserSetting, })); sinon.stub(IModelApp, "authorizationClient").get(() => ({ hasSignedIn: true })); - const sut = new IModelAppUiSettings(); + const sut = new UserSettingsStorage(); await sut.deleteSetting("TESTNAMESPACE", "TESTNAME"); deleteUserSetting.calledOnceWithExactly(sinon.match.any, "TESTNAMESPACE", "TESTNAME", true).should.true; }); @@ -52,7 +52,7 @@ describe("IModelAppUiSettings", () => { getUserSetting, })); sinon.stub(IModelApp, "authorizationClient").get(() => ({ hasSignedIn: true })); - const sut = new IModelAppUiSettings(); + const sut = new UserSettingsStorage(); const settingResult = await sut.getSetting("TESTNAMESPACE", "TESTNAME"); getUserSetting.calledOnceWithExactly(sinon.match.any, "TESTNAMESPACE", "TESTNAME", true).should.true; settingResult.setting.should.eq("testvalue"); @@ -60,21 +60,21 @@ describe("IModelAppUiSettings", () => { it("should fail to save setting", async () => { sinon.stub(IModelApp, "authorizationClient").get(() => ({ hasSignedIn: false })); - const sut = new IModelAppUiSettings(); + const sut = new UserSettingsStorage(); const result = await sut.saveSetting("TESTNAMESPACE", "TESTNAME", "testvalue"); expect(result.status).to.eq(UiSettingsStatus.AuthorizationError); }); it("should fail to delete setting", async () => { sinon.stub(IModelApp, "authorizationClient").get(() => ({ hasSignedIn: false })); - const sut = new IModelAppUiSettings(); + const sut = new UserSettingsStorage(); const result = await sut.deleteSetting("TESTNAMESPACE", "TESTNAME"); expect(result.status).to.eq(UiSettingsStatus.AuthorizationError); }); it("should fail to get setting", async () => { sinon.stub(IModelApp, "authorizationClient").get(() => ({ hasSignedIn: false })); - const sut = new IModelAppUiSettings(); + const sut = new UserSettingsStorage(); const result = await sut.getSetting("TESTNAMESPACE", "TESTNAME"); expect(result.status).to.eq(UiSettingsStatus.AuthorizationError); }); diff --git a/ui/framework/src/test/utils/UiShowHideManager.test.tsx b/ui/framework/src/test/utils/UiShowHideManager.test.tsx index 65918d99681..0e29cab8d11 100644 --- a/ui/framework/src/test/utils/UiShowHideManager.test.tsx +++ b/ui/framework/src/test/utils/UiShowHideManager.test.tsx @@ -9,221 +9,268 @@ import { render } from "@testing-library/react"; import { ConfigurableCreateInfo, ContentControl, ContentGroup, ContentLayout, ContentLayoutDef, FrontstageManager, INACTIVITY_TIME_DEFAULT, UiFramework, UiShowHideManager, + UiShowHideSettingsProvider, } from "../../ui-framework"; import { TestFrontstage } from "../frontstage/FrontstageTestUtils"; -import TestUtils from "../TestUtils"; +import TestUtils, { storageMock } from "../TestUtils"; +import { LocalSettingsStorage } from "@bentley/ui-core"; -describe("UiShowHideManager", () => { +describe("UiShowHideManager localStorage Wrapper", () => { + + const localStorageToRestore = Object.getOwnPropertyDescriptor(window, "localStorage")!; + const localStorageMock = storageMock(); before(async () => { - await TestUtils.initializeUiFramework(); + Object.defineProperty(window, "localStorage", { + get: () => localStorageMock, + }); }); after(() => { - TestUtils.terminateUiFramework(); + Object.defineProperty(window, "localStorage", localStorageToRestore); }); - describe("getters and setters", () => { + describe("UiShowHideManager", () => { - it("autoHideUi should return default of false", () => { - expect(UiShowHideManager.autoHideUi).to.be.false; + before(async () => { + await TestUtils.initializeUiFramework(); }); - it("autoHideUi should set & return correct value", () => { - UiShowHideManager.autoHideUi = true; - expect(UiShowHideManager.autoHideUi).to.be.true; - UiShowHideManager.autoHideUi = false; - expect(UiShowHideManager.autoHideUi).to.be.false; + after(() => { + TestUtils.terminateUiFramework(); }); - it("showHidePanels should return default of false", () => { - expect(UiShowHideManager.showHidePanels).to.be.false; - }); + describe("getters and setters", () => { - it("showHidePanels should set & return correct value", () => { - const spyMethod = sinon.spy(); - const remove = UiFramework.onUiVisibilityChanged.addListener(spyMethod); + it("autoHideUi should return default of false", () => { + expect(UiShowHideManager.autoHideUi).to.be.false; + }); - UiShowHideManager.showHidePanels = true; - expect(UiShowHideManager.showHidePanels).to.be.true; - spyMethod.calledOnce.should.true; + it("autoHideUi should set & return correct value", () => { + UiShowHideManager.autoHideUi = true; + expect(UiShowHideManager.autoHideUi).to.be.true; + UiShowHideManager.autoHideUi = false; + expect(UiShowHideManager.autoHideUi).to.be.false; + }); - UiShowHideManager.showHidePanels = false; - expect(UiShowHideManager.showHidePanels).to.be.false; - spyMethod.calledTwice.should.true; + it("showHidePanels should return default of false", () => { + expect(UiShowHideManager.showHidePanels).to.be.false; + }); - remove(); - }); + it("showHidePanels should set & return correct value", () => { + const spyMethod = sinon.spy(); + const remove = UiFramework.onUiVisibilityChanged.addListener(spyMethod); - it("showHideFooter should return default of false", () => { - expect(UiShowHideManager.showHideFooter).to.be.false; - }); + UiShowHideManager.showHidePanels = true; + expect(UiShowHideManager.showHidePanels).to.be.true; + spyMethod.calledOnce.should.true; - it("showHideFooter should set & return correct value", () => { - const spyMethod = sinon.spy(); - const remove = UiFramework.onUiVisibilityChanged.addListener(spyMethod); + UiShowHideManager.showHidePanels = false; + expect(UiShowHideManager.showHidePanels).to.be.false; + spyMethod.calledTwice.should.true; - UiShowHideManager.showHideFooter = true; - expect(UiShowHideManager.showHideFooter).to.be.true; - spyMethod.calledOnce.should.true; + remove(); + }); - UiShowHideManager.showHideFooter = false; - expect(UiShowHideManager.showHideFooter).to.be.false; - spyMethod.calledTwice.should.true; + it("showHideFooter should return default of false", () => { + expect(UiShowHideManager.showHideFooter).to.be.false; + }); - remove(); - }); + it("showHideFooter should set & return correct value", () => { + const spyMethod = sinon.spy(); + const remove = UiFramework.onUiVisibilityChanged.addListener(spyMethod); - it("useProximityOpacity should return default of true", () => { - expect(UiShowHideManager.useProximityOpacity).to.be.true; - }); + UiShowHideManager.showHideFooter = true; + expect(UiShowHideManager.showHideFooter).to.be.true; + spyMethod.calledOnce.should.true; - it("useProximityOpacity should set & return correct value", () => { - const spyMethod = sinon.spy(); - const remove = UiFramework.onUiVisibilityChanged.addListener(spyMethod); + UiShowHideManager.showHideFooter = false; + expect(UiShowHideManager.showHideFooter).to.be.false; + spyMethod.calledTwice.should.true; - UiShowHideManager.useProximityOpacity = false; - expect(UiShowHideManager.useProximityOpacity).to.be.false; - spyMethod.calledOnce.should.true; + remove(); + }); - UiShowHideManager.useProximityOpacity = true; - expect(UiShowHideManager.useProximityOpacity).to.be.true; - spyMethod.calledTwice.should.true; + it("useProximityOpacity should return default of true", () => { + expect(UiShowHideManager.useProximityOpacity).to.be.true; + }); - remove(); - }); + it("useProximityOpacity should set & return correct value", () => { + const spyMethod = sinon.spy(); + const remove = UiFramework.onUiVisibilityChanged.addListener(spyMethod); - it("snapWidgetOpacity should return default of false", () => { - expect(UiShowHideManager.snapWidgetOpacity).to.be.false; - }); + UiShowHideManager.useProximityOpacity = false; + expect(UiShowHideManager.useProximityOpacity).to.be.false; + spyMethod.calledOnce.should.true; - it("snapWidgetOpacity should set & return correct value", () => { - const spyMethod = sinon.spy(); - const remove = UiFramework.onUiVisibilityChanged.addListener(spyMethod); + UiShowHideManager.useProximityOpacity = true; + expect(UiShowHideManager.useProximityOpacity).to.be.true; + spyMethod.calledTwice.should.true; - UiShowHideManager.snapWidgetOpacity = true; - expect(UiShowHideManager.snapWidgetOpacity).to.be.true; - spyMethod.calledOnce.should.true; + remove(); + }); - UiShowHideManager.snapWidgetOpacity = false; - expect(UiShowHideManager.snapWidgetOpacity).to.be.false; - spyMethod.calledTwice.should.true; + it("snapWidgetOpacity should return default of false", () => { + expect(UiShowHideManager.snapWidgetOpacity).to.be.false; + }); - remove(); - }); + it("snapWidgetOpacity should set & return correct value", () => { + const spyMethod = sinon.spy(); + const remove = UiFramework.onUiVisibilityChanged.addListener(spyMethod); - it("inactivityTime should return default", () => { - expect(UiShowHideManager.inactivityTime).to.eq(INACTIVITY_TIME_DEFAULT); - }); + UiShowHideManager.snapWidgetOpacity = true; + expect(UiShowHideManager.snapWidgetOpacity).to.be.true; + spyMethod.calledOnce.should.true; - it("inactivityTime should set & return correct value", () => { - const testValue = 10000; - UiShowHideManager.inactivityTime = testValue; - expect(UiShowHideManager.inactivityTime).to.eq(testValue); + UiShowHideManager.snapWidgetOpacity = false; + expect(UiShowHideManager.snapWidgetOpacity).to.be.false; + spyMethod.calledTwice.should.true; + + remove(); + }); + + it("inactivityTime should return default", () => { + expect(UiShowHideManager.inactivityTime).to.eq(INACTIVITY_TIME_DEFAULT); + }); + + it("inactivityTime should set & return correct value", () => { + const testValue = 10000; + UiShowHideManager.inactivityTime = testValue; + expect(UiShowHideManager.inactivityTime).to.eq(testValue); + }); }); - }); - describe("Frontstage Activate", () => { + describe("Frontstage Activate", () => { - it("activating Frontstage should show UI", async () => { - UiFramework.setIsUiVisible(false); - expect(UiShowHideManager.isUiVisible).to.eq(false); - UiShowHideManager.autoHideUi = true; + it("activating Frontstage should show UI", async () => { + UiFramework.setIsUiVisible(false); + expect(UiShowHideManager.isUiVisible).to.eq(false); + UiShowHideManager.autoHideUi = true; - const frontstageProvider = new TestFrontstage(); - FrontstageManager.addFrontstageProvider(frontstageProvider); - await FrontstageManager.setActiveFrontstageDef(frontstageProvider.frontstageDef); + const frontstageProvider = new TestFrontstage(); + FrontstageManager.addFrontstageProvider(frontstageProvider); + await FrontstageManager.setActiveFrontstageDef(frontstageProvider.frontstageDef); - await TestUtils.flushAsyncOperations(); - expect(UiShowHideManager.isUiVisible).to.eq(true); + await TestUtils.flushAsyncOperations(); + expect(UiShowHideManager.isUiVisible).to.eq(true); + }); }); - }); - describe("Content Mouse Events", () => { + describe("Content Mouse Events", () => { - class TestContentControl extends ContentControl { - constructor(info: ConfigurableCreateInfo, options: any) { - super(info, options); + class TestContentControl extends ContentControl { + constructor(info: ConfigurableCreateInfo, options: any) { + super(info, options); - this.reactNode =
Test
; + this.reactNode =
Test
; + } } - } - const myContentGroup: ContentGroup = new ContentGroup({ - contents: [{ id: "myContent", classId: TestContentControl }], + const myContentGroup: ContentGroup = new ContentGroup({ + contents: [{ id: "myContent", classId: TestContentControl }], + }); + + const myContentLayout: ContentLayoutDef = new ContentLayoutDef({ + id: "SingleContent", + descriptionKey: "UiFramework:tests.singleContent", + priority: 100, + }); + + it("Mouse move in content view should show the UI then hide after inactivity", () => { + const fakeTimers = sinon.useFakeTimers(); + UiFramework.setIsUiVisible(false); + UiShowHideManager.autoHideUi = true; + UiShowHideManager.inactivityTime = 20; + expect(UiShowHideManager.isUiVisible).to.eq(false); + + const component = render(); + const container = component.getByTestId("single-content-container"); + container.dispatchEvent(new MouseEvent("mousemove", { bubbles: true, cancelable: true, view: window })); + + fakeTimers.tick(0); + expect(UiShowHideManager.isUiVisible).to.eq(true); + + fakeTimers.tick(1000); + fakeTimers.restore(); + expect(UiShowHideManager.isUiVisible).to.eq(false); + }); + + it("Mouse move in content view should do nothing if autoHideUi is off", async () => { + UiFramework.setIsUiVisible(false); + UiShowHideManager.autoHideUi = false; + expect(UiShowHideManager.isUiVisible).to.eq(false); + + const component = render(); + const container = component.getByTestId("single-content-container"); + container.dispatchEvent(new MouseEvent("mousemove", { bubbles: true, cancelable: true, view: window })); + + await TestUtils.flushAsyncOperations(); + expect(UiShowHideManager.isUiVisible).to.eq(false); + }); }); - const myContentLayout: ContentLayoutDef = new ContentLayoutDef({ - id: "SingleContent", - descriptionKey: "UiFramework:tests.singleContent", - priority: 100, - }); + describe("Widget Mouse Events", () => { + it("Mouse enter in widget should show the UI", async () => { + UiFramework.setIsUiVisible(false); + UiShowHideManager.autoHideUi = true; + expect(UiShowHideManager.isUiVisible).to.eq(false); - it("Mouse move in content view should show the UI then hide after inactivity", () => { - const fakeTimers = sinon.useFakeTimers(); - UiFramework.setIsUiVisible(false); - UiShowHideManager.autoHideUi = true; - UiShowHideManager.inactivityTime = 20; - expect(UiShowHideManager.isUiVisible).to.eq(false); + // const component = render(); + // const container = component.getByTestId("single-content-container"); + // container.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true, cancelable: true, view: window })); - const component = render(); - const container = component.getByTestId("single-content-container"); - container.dispatchEvent(new MouseEvent("mousemove", { bubbles: true, cancelable: true, view: window })); + // TEMP + UiShowHideManager.handleWidgetMouseEnter(); - fakeTimers.tick(0); - expect(UiShowHideManager.isUiVisible).to.eq(true); + await TestUtils.flushAsyncOperations(); + expect(UiShowHideManager.isUiVisible).to.eq(true); + }); - fakeTimers.tick(1000); - fakeTimers.restore(); - expect(UiShowHideManager.isUiVisible).to.eq(false); - }); + it("Mouse enter in widget should do nothing if autoHideUi is off", async () => { + UiFramework.setIsUiVisible(false); + UiShowHideManager.autoHideUi = false; + expect(UiShowHideManager.isUiVisible).to.eq(false); - it("Mouse move in content view should do nothing if autoHideUi is off", async () => { - UiFramework.setIsUiVisible(false); - UiShowHideManager.autoHideUi = false; - expect(UiShowHideManager.isUiVisible).to.eq(false); + // const component = render(); + // const container = component.getByTestId("single-content-container"); + // container.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true, cancelable: true, view: window })); - const component = render(); - const container = component.getByTestId("single-content-container"); - container.dispatchEvent(new MouseEvent("mousemove", { bubbles: true, cancelable: true, view: window })); + // TEMP + UiShowHideManager.handleWidgetMouseEnter(); - await TestUtils.flushAsyncOperations(); - expect(UiShowHideManager.isUiVisible).to.eq(false); + await TestUtils.flushAsyncOperations(); + expect(UiShowHideManager.isUiVisible).to.eq(false); + }); }); }); - describe("Widget Mouse Events", () => { - it("Mouse enter in widget should show the UI", async () => { - UiFramework.setIsUiVisible(false); - UiShowHideManager.autoHideUi = true; - expect(UiShowHideManager.isUiVisible).to.eq(false); + describe("UiShowHideSettingsProvider ", () => { - // const component = render(); - // const container = component.getByTestId("single-content-container"); - // container.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true, cancelable: true, view: window })); + it("should get and set defaults", async () => { + const settingsStorage = new LocalSettingsStorage (); + await UiShowHideSettingsProvider.storeAutoHideUi(false, settingsStorage); + await UiShowHideSettingsProvider.storeUseProximityOpacity (false, settingsStorage); + await UiShowHideSettingsProvider.storeSnapWidgetOpacity(false, settingsStorage); + await TestUtils.initializeUiFramework(); - // TEMP - UiShowHideManager.handleWidgetMouseEnter(); + const uiShowHideSettingsProvider = new UiShowHideSettingsProvider (); + await uiShowHideSettingsProvider.loadUserSettings (UiFramework.getUiSettingsStorage()); - await TestUtils.flushAsyncOperations(); - expect(UiShowHideManager.isUiVisible).to.eq(true); - }); + expect(UiShowHideManager.autoHideUi).to.eq(false); + expect(UiShowHideManager.useProximityOpacity).to.eq(false); + expect(UiShowHideManager.snapWidgetOpacity).to.eq(false); - it("Mouse enter in widget should do nothing if autoHideUi is off", async () => { - UiFramework.setIsUiVisible(false); - UiShowHideManager.autoHideUi = false; - expect(UiShowHideManager.isUiVisible).to.eq(false); + UiShowHideManager.setAutoHideUi(true); + UiShowHideManager.setUseProximityOpacity(true); + UiShowHideManager.setSnapWidgetOpacity(true); + expect(UiShowHideManager.autoHideUi).to.eq(true); + expect(UiShowHideManager.useProximityOpacity).to.eq(true); + expect(UiShowHideManager.snapWidgetOpacity).to.eq(true); - // const component = render(); - // const container = component.getByTestId("single-content-container"); - // container.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true, cancelable: true, view: window })); + TestUtils.terminateUiFramework(); - // TEMP - UiShowHideManager.handleWidgetMouseEnter(); - - await TestUtils.flushAsyncOperations(); - expect(UiShowHideManager.isUiVisible).to.eq(false); }); + }); }); diff --git a/ui/framework/src/test/widget-panels/Frontstage.test.snap b/ui/framework/src/test/widget-panels/Frontstage.test.snap index 4f927623ad0..5739ff675a7 100644 --- a/ui/framework/src/test/widget-panels/Frontstage.test.snap +++ b/ui/framework/src/test/widget-panels/Frontstage.test.snap @@ -109,6 +109,528 @@ exports[`ActiveFrontstageDefProvider should render 1`] = ` `; +exports[`Frontstage local storage wrapper ActiveFrontstageDefProvider should render 1`] = ` +
+ } + toolSettingsContent={} + widgetContent={} + > + + +
+`; + +exports[`Frontstage local storage wrapper WidgetPanelsFrontstage should not render w/o frontstage 1`] = `""`; + +exports[`Frontstage local storage wrapper WidgetPanelsFrontstage should render 1`] = ` + +`; + +exports[`Frontstage local storage wrapper WidgetPanelsFrontstage should render modal stage content 1`] = ` + +`; + +exports[`Frontstage local storage wrapper initializeNineZoneState should initialize widgets 1`] = ` +Object { + "draggedTab": undefined, + "floatingWidgets": Object { + "allIds": Array [], + "byId": Object {}, + }, + "panels": Object { + "bottom": Object { + "collapseOffset": 100, + "collapsed": false, + "maxSize": 600, + "maxWidgetCount": 2, + "minSize": 100, + "pinned": true, + "resizable": true, + "side": "bottom", + "size": undefined, + "span": true, + "widgets": Array [], + }, + "left": Object { + "collapseOffset": 100, + "collapsed": false, + "maxSize": 600, + "maxWidgetCount": 3, + "minSize": 200, + "pinned": true, + "resizable": true, + "side": "left", + "size": undefined, + "widgets": Array [], + }, + "right": Object { + "collapseOffset": 100, + "collapsed": false, + "maxSize": 600, + "maxWidgetCount": 3, + "minSize": 200, + "pinned": true, + "resizable": true, + "side": "right", + "size": undefined, + "widgets": Array [], + }, + "top": Object { + "collapseOffset": 100, + "collapsed": false, + "maxSize": 600, + "maxWidgetCount": 2, + "minSize": 100, + "pinned": true, + "resizable": true, + "side": "top", + "size": undefined, + "span": true, + "widgets": Array [], + }, + }, + "size": Object { + "height": 0, + "width": 0, + }, + "tabs": Object { + "nz-tool-settings-tab": Object { + "allowedPanelTargets": Array [ + "bottom", + "left", + "right", + ], + "id": "nz-tool-settings-tab", + "label": "Tool Settings", + }, + }, + "toolSettings": Object { + "type": "docked", + }, + "widgets": Object {}, +} +`; + +exports[`Frontstage local storage wrapper packNineZoneState should remove labels 1`] = ` +Object { + "draggedTab": undefined, + "floatingWidgets": Object { + "allIds": Array [ + "w1", + ], + "byId": Object { + "w1": Object { + "bounds": Object { + "bottom": 0, + "left": 0, + "right": 0, + "top": 0, + }, + "home": Object { + "side": "left", + "widgetId": undefined, + "widgetIndex": 0, + }, + "id": "w1", + }, + }, + }, + "panels": Object { + "bottom": Object { + "collapseOffset": 100, + "collapsed": false, + "maxSize": 600, + "maxWidgetCount": 2, + "minSize": 100, + "pinned": true, + "resizable": true, + "side": "bottom", + "size": undefined, + "span": true, + "widgets": Array [], + }, + "left": Object { + "collapseOffset": 100, + "collapsed": false, + "maxSize": 600, + "maxWidgetCount": 3, + "minSize": 200, + "pinned": true, + "resizable": true, + "side": "left", + "size": undefined, + "widgets": Array [], + }, + "right": Object { + "collapseOffset": 100, + "collapsed": false, + "maxSize": 600, + "maxWidgetCount": 3, + "minSize": 200, + "pinned": true, + "resizable": true, + "side": "right", + "size": undefined, + "widgets": Array [], + }, + "top": Object { + "collapseOffset": 100, + "collapsed": false, + "maxSize": 600, + "maxWidgetCount": 2, + "minSize": 100, + "pinned": true, + "resizable": true, + "side": "top", + "size": undefined, + "span": true, + "widgets": Array [], + }, + }, + "size": Object { + "height": 0, + "width": 0, + }, + "tabs": Object { + "t1": Object { + "allowedPanelTargets": undefined, + "id": "t1", + "preferredFloatingWidgetSize": undefined, + }, + }, + "toolSettings": Object { + "type": "docked", + }, + "widgets": Object { + "w1": Object { + "activeTabId": "t1", + "id": "w1", + "minimized": false, + "tabs": Array [ + "t1", + ], + }, + }, +} +`; + +exports[`Frontstage local storage wrapper restoreNineZoneState should log error if widgetDef is not found 1`] = ` +Object { + "frontstageId": "", + "tabId": "t1", +} +`; + +exports[`Frontstage local storage wrapper restoreNineZoneState should restore tabs 1`] = ` +Object { + "draggedTab": undefined, + "floatingWidgets": Object { + "allIds": Array [], + "byId": Object {}, + }, + "panels": Object { + "bottom": Object { + "collapseOffset": 100, + "collapsed": false, + "maxSize": 600, + "maxWidgetCount": 2, + "minSize": 100, + "pinned": true, + "resizable": true, + "side": "bottom", + "size": undefined, + "span": true, + "widgets": Array [], + }, + "left": Object { + "collapseOffset": 100, + "collapsed": false, + "maxSize": 600, + "maxWidgetCount": 3, + "minSize": 200, + "pinned": true, + "resizable": true, + "side": "left", + "size": undefined, + "widgets": Array [], + }, + "right": Object { + "collapseOffset": 100, + "collapsed": false, + "maxSize": 600, + "maxWidgetCount": 3, + "minSize": 200, + "pinned": true, + "resizable": true, + "side": "right", + "size": undefined, + "widgets": Array [], + }, + "top": Object { + "collapseOffset": 100, + "collapsed": false, + "maxSize": 600, + "maxWidgetCount": 2, + "minSize": 100, + "pinned": true, + "resizable": true, + "side": "top", + "size": undefined, + "span": true, + "widgets": Array [], + }, + }, + "size": Object { + "height": 0, + "width": 0, + }, + "tabs": Object { + "nz-tool-settings-tab": Object { + "allowedPanelTargets": Array [ + "bottom", + "left", + "right", + ], + "id": "nz-tool-settings-tab", + "label": "Tool Settings", + }, + "t1": Object { + "id": "t1", + "label": "Widget", + }, + }, + "toolSettings": Object { + "type": "docked", + }, + "widgets": Object {}, +} +`; + +exports[`Frontstage local storage wrapper useSavedFrontstageState should load saved nineZoneState 1`] = ` +Object { + "draggedTab": undefined, + "floatingWidgets": Object { + "allIds": Array [], + "byId": Object {}, + }, + "panels": Object { + "bottom": Object { + "collapseOffset": 100, + "collapsed": false, + "maxSize": 600, + "maxWidgetCount": 2, + "minSize": 100, + "pinned": true, + "resizable": true, + "side": "bottom", + "size": undefined, + "span": true, + "widgets": Array [], + }, + "left": Object { + "collapseOffset": 100, + "collapsed": false, + "maxSize": 600, + "maxWidgetCount": 3, + "minSize": 200, + "pinned": true, + "resizable": true, + "side": "left", + "size": undefined, + "widgets": Array [], + }, + "right": Object { + "collapseOffset": 100, + "collapsed": false, + "maxSize": 600, + "maxWidgetCount": 3, + "minSize": 200, + "pinned": true, + "resizable": true, + "side": "right", + "size": undefined, + "widgets": Array [], + }, + "top": Object { + "collapseOffset": 100, + "collapsed": false, + "maxSize": 600, + "maxWidgetCount": 2, + "minSize": 100, + "pinned": true, + "resizable": true, + "side": "top", + "size": undefined, + "span": true, + "widgets": Array [], + }, + }, + "size": Object { + "height": 0, + "width": 0, + }, + "tabs": Object { + "nz-tool-settings-tab": Object { + "allowedPanelTargets": Array [ + "bottom", + "left", + "right", + ], + "id": "nz-tool-settings-tab", + "label": "Tool Settings", + }, + }, + "toolSettings": Object { + "type": "docked", + }, + "widgets": Object {}, +} +`; + exports[`WidgetPanelsFrontstage should not render w/o frontstage 1`] = `""`; exports[`WidgetPanelsFrontstage should render 1`] = ` diff --git a/ui/framework/src/test/widget-panels/Frontstage.test.tsx b/ui/framework/src/test/widget-panels/Frontstage.test.tsx index ba166367966..25c4220ec2b 100644 --- a/ui/framework/src/test/widget-panels/Frontstage.test.tsx +++ b/ui/framework/src/test/widget-panels/Frontstage.test.tsx @@ -10,7 +10,7 @@ import produce from "immer"; import { render } from "@testing-library/react"; import { act, renderHook } from "@testing-library/react-hooks"; import { Logger } from "@bentley/bentleyjs-core"; -import { AbstractWidgetProps, StagePanelLocation, UiItemsManager, UiItemsProvider } from "@bentley/ui-abstract"; +import { AbstractWidgetProps, StagePanelLocation, UiItemsManager, UiItemsProvider, WidgetState } from "@bentley/ui-abstract"; import { Size, UiSettingsResult, UiSettingsStatus } from "@bentley/ui-core"; import { addFloatingWidget, addPanelWidget, addTab, createDraggedTabState, createNineZoneState, NineZone, NineZoneState, toolSettingsTabId } from "@bentley/ui-ninezone"; import { @@ -18,7 +18,7 @@ import { FrontstageManager, FrontstageProvider, getWidgetId, initializeNineZoneState, initializePanel, isFrontstageStateSettingResult, ModalFrontstageComposer, packNineZoneState, restoreNineZoneState, setWidgetState, showWidget, StagePanel, StagePanelDef, StagePanelSection, StagePanelState, StagePanelZoneDef, StagePanelZonesDef, UiSettingsProvider, useActiveModalFrontstageInfo, useFrontstageManager, useNineZoneDispatch, useNineZoneState, useSavedFrontstageState, - useSaveFrontstageSettings, useSyncDefinitions, useUpdateNineZoneSize, Widget, WidgetDef, WidgetPanelsFrontstage, WidgetPanelsFrontstageState, WidgetState, Zone, ZoneDef, + useSaveFrontstageSettings, useSyncDefinitions, useUpdateNineZoneSize, Widget, WidgetDef, WidgetPanelsFrontstage, WidgetPanelsFrontstageState, Zone, ZoneDef, } from "../../ui-framework"; import TestUtils, { mount, storageMock, stubRaf, UiSettingsStub } from "../TestUtils"; import { IModelApp, NoRenderApp } from "@bentley/imodeljs-frontend"; @@ -337,84 +337,7 @@ export class TestUi2Provider implements UiItemsProvider { } } -describe("WidgetPanelsFrontstage", () => { - it("should render", () => { - const frontstageDef = new FrontstageDef(); - sinon.stub(FrontstageManager, "activeFrontstageDef").get(() => frontstageDef); - const wrapper = shallow(); - wrapper.should.matchSnapshot(); - }); - - it("should render modal stage content", () => { - const modalStageInfo = { - title: "TestModalStage", - content:
Hello World!
, - }; - sinon.stub(FrontstageManager, "activeModalFrontstage").get(() => modalStageInfo); - const frontstageDef = new FrontstageDef(); - const contentGroup = moq.Mock.ofType(); - sinon.stub(FrontstageManager, "activeFrontstageDef").get(() => frontstageDef); - sinon.stub(frontstageDef, "contentGroup").get(() => contentGroup.object); - const wrapper = shallow(); - wrapper.should.matchSnapshot(); - }); - - it("should not render w/o frontstage", () => { - sinon.stub(FrontstageManager, "activeFrontstageDef").get(() => undefined); - const wrapper = shallow(); - wrapper.should.matchSnapshot(); - }); -}); - -describe("ModalFrontstageComposer", () => { - before(async () => { - await TestUtils.initializeUiFramework(); - }); - - after(() => { - TestUtils.terminateUiFramework(); - }); - - it("should render modal stage content when mounted", () => { - const modalStageInfo = { - title: "TestModalStage", - content:
Hello World!
, - }; - mount(); - }); - - it("should add tool activated event listener", () => { - const addListenerSpy = sinon.spy(FrontstageManager.onModalFrontstageChangedEvent, "addListener"); - const removeListenerSpy = sinon.spy(FrontstageManager.onModalFrontstageChangedEvent, "removeListener"); - const sut = renderHook(() => useActiveModalFrontstageInfo()); - sut.unmount(); - addListenerSpy.calledOnce.should.true; - removeListenerSpy.calledOnce.should.true; - }); - - it("should update active modal info", () => { - const modalStageInfo = { - title: "TestModalStage", - content:
Hello World!
, - }; - - sinon.stub(FrontstageManager, "activeModalFrontstage").get(() => undefined); - renderHook(() => useActiveModalFrontstageInfo()); - act(() => { - sinon.stub(FrontstageManager, "activeModalFrontstage").get(() => undefined); - FrontstageManager.onModalFrontstageChangedEvent.emit({ - modalFrontstageCount: 0, - }); - - sinon.stub(FrontstageManager, "activeModalFrontstage").get(() => modalStageInfo); - FrontstageManager.onModalFrontstageChangedEvent.emit({ - modalFrontstageCount: 1, - }); - }); - }); -}); - -describe("ActiveFrontstageDefProvider", () => { +describe("Frontstage local storage wrapper", () => { const localStorageToRestore = Object.getOwnPropertyDescriptor(window, "localStorage")!; const localStorageMock = storageMock(); @@ -422,1539 +345,1615 @@ describe("ActiveFrontstageDefProvider", () => { Object.defineProperty(window, "localStorage", { get: () => localStorageMock, }); - - await TestUtils.initializeUiFramework(); }); after(() => { Object.defineProperty(window, "localStorage", localStorageToRestore); - TestUtils.terminateUiFramework(); }); - beforeEach(() => { - sinon.stub(FrontstageManager, "nineZoneSize").set(() => { }); - }); + describe("WidgetPanelsFrontstage", () => { + it("should render", () => { + const frontstageDef = new FrontstageDef(); + sinon.stub(FrontstageManager, "activeFrontstageDef").get(() => frontstageDef); + const wrapper = shallow(); + wrapper.should.matchSnapshot(); + }); + + it("should render modal stage content", () => { + const modalStageInfo = { + title: "TestModalStage", + content:
Hello World!
, + }; + sinon.stub(FrontstageManager, "activeModalFrontstage").get(() => modalStageInfo); + const frontstageDef = new FrontstageDef(); + const contentGroup = moq.Mock.ofType(); + sinon.stub(FrontstageManager, "activeFrontstageDef").get(() => frontstageDef); + sinon.stub(frontstageDef, "contentGroup").get(() => contentGroup.object); + const wrapper = shallow(); + wrapper.should.matchSnapshot(); + }); - it("should render", () => { - const frontstageDef = new FrontstageDef(); - const wrapper = shallow(); - wrapper.should.matchSnapshot(); + it("should not render w/o frontstage", () => { + sinon.stub(FrontstageManager, "activeFrontstageDef").get(() => undefined); + const wrapper = shallow(); + wrapper.should.matchSnapshot(); + }); }); - it("should fall back to cached NineZoneState", () => { - const frontstageDef = new FrontstageDef(); - frontstageDef.nineZoneState = createNineZoneState(); + describe("ModalFrontstageComposer", () => { + before(async () => { + await TestUtils.initializeUiFramework(); + }); - const newFrontstageDef = new FrontstageDef(); - newFrontstageDef.nineZoneState = undefined; + after(() => { + TestUtils.terminateUiFramework(); + }); - const wrapper = mount<{ frontstageDef: FrontstageDef }>(); - wrapper.setProps({ frontstageDef: newFrontstageDef }); + it("should render modal stage content when mounted", () => { + const modalStageInfo = { + title: "TestModalStage", + content:
Hello World!
, + }; + mount(); + }); - const nineZone = wrapper.find(NineZone); - nineZone.prop("state").should.eq(frontstageDef.nineZoneState); - }); -}); + it("should add tool activated event listener", () => { + const addListenerSpy = sinon.spy(FrontstageManager.onModalFrontstageChangedEvent, "addListener"); + const removeListenerSpy = sinon.spy(FrontstageManager.onModalFrontstageChangedEvent, "removeListener"); + const sut = renderHook(() => useActiveModalFrontstageInfo()); + sut.unmount(); + addListenerSpy.calledOnce.should.true; + removeListenerSpy.calledOnce.should.true; + }); -describe("useNineZoneDispatch", () => { - beforeEach(() => { - sinon.stub(FrontstageManager, "nineZoneSize").set(() => { }); - }); + it("should update active modal info", () => { + const modalStageInfo = { + title: "TestModalStage", + content:
Hello World!
, + }; - it("should modify nineZoneState with default NineZoneReducer", () => { - const frontstageDef = new FrontstageDef(); - const nineZoneState = createNineZoneState(); - frontstageDef.nineZoneState = nineZoneState; - const { result } = renderHook(() => useNineZoneDispatch(frontstageDef)); - result.current({ - type: "PANEL_INITIALIZE", - side: "left", - size: 200, - }); - frontstageDef.nineZoneState.should.not.eq(nineZoneState); - (frontstageDef.nineZoneState.panels.left.size === 200).should.true; + sinon.stub(FrontstageManager, "activeModalFrontstage").get(() => undefined); + renderHook(() => useActiveModalFrontstageInfo()); + act(() => { + sinon.stub(FrontstageManager, "activeModalFrontstage").get(() => undefined); + FrontstageManager.onModalFrontstageChangedEvent.emit({ + modalFrontstageCount: 0, + }); + + sinon.stub(FrontstageManager, "activeModalFrontstage").get(() => modalStageInfo); + FrontstageManager.onModalFrontstageChangedEvent.emit({ + modalFrontstageCount: 1, + }); + }); + }); }); - it("should not modify when nineZoneState is not defined", () => { - const frontstageDef = new FrontstageDef(); - frontstageDef.nineZoneState = undefined; - const { result } = renderHook(() => useNineZoneDispatch(frontstageDef)); - result.current({ - type: "PANEL_INITIALIZE", - side: "left", - size: 200, + describe("ActiveFrontstageDefProvider", () => { + before(async () => { + await TestUtils.initializeUiFramework(); }); - (frontstageDef.nineZoneState === undefined).should.true; - }); - it("should set nineZoneSize when RESIZE is received", () => { - const spy = sinon.stub(FrontstageManager, "nineZoneSize").set(() => { }); - const frontstageDef = new FrontstageDef(); - frontstageDef.nineZoneState = createNineZoneState(); - const { result } = renderHook(() => useNineZoneDispatch(frontstageDef)); - result.current({ - type: "RESIZE", - size: { - width: 5, - height: 10, - }, - }); - spy.calledOnceWithExactly(sinon.match({ width: 5, height: 10 })); - }); + after(() => { + TestUtils.terminateUiFramework(); + }); - it("should set vertical (left/right) panel max size from percentage spec", () => { - const frontstageDef = new FrontstageDef(); - const panel = new StagePanelDef(); - sinon.stub(panel, "maxSizeSpec").get(() => ({ percentage: 50 })); - sinon.stub(frontstageDef, "leftPanel").get(() => panel); - frontstageDef.nineZoneState = createNineZoneState(); - const { result } = renderHook(() => useNineZoneDispatch(frontstageDef)); - result.current({ - type: "RESIZE", - size: { - height: 200, - width: 500, - }, - }); - frontstageDef.nineZoneState.panels.left.maxSize.should.eq(250); - }); + beforeEach(() => { + sinon.stub(FrontstageManager, "nineZoneSize").set(() => { }); + }); - it("should set horizontal (top/bottom) panel max size from percentage spec", () => { - const frontstageDef = new FrontstageDef(); - const panel = new StagePanelDef(); - sinon.stub(panel, "maxSizeSpec").get(() => ({ percentage: 50 })); - sinon.stub(frontstageDef, "topPanel").get(() => panel); - frontstageDef.nineZoneState = createNineZoneState(); - const { result } = renderHook(() => useNineZoneDispatch(frontstageDef)); - result.current({ - type: "RESIZE", - size: { - height: 200, - width: 500, - }, - }); - frontstageDef.nineZoneState.panels.top.maxSize.should.eq(100); - }); + it("should render", () => { + const frontstageDef = new FrontstageDef(); + const wrapper = shallow(); + wrapper.should.matchSnapshot(); + }); - it("should update panel size", () => { - const frontstageDef = new FrontstageDef(); - const panel = new StagePanelDef(); - sinon.stub(panel, "maxSizeSpec").get(() => 250); - sinon.stub(frontstageDef, "leftPanel").get(() => panel); - - let state = createNineZoneState(); - state = produce(state, (draft) => { - draft.panels.left.size = 300; - }); - frontstageDef.nineZoneState = state; - const { result } = renderHook(() => useNineZoneDispatch(frontstageDef)); - result.current({ - type: "RESIZE", - size: { - height: 200, - width: 500, - }, - }); - frontstageDef.nineZoneState.panels.left.size!.should.eq(250); - }); -}); + it("should fall back to cached NineZoneState", () => { + const frontstageDef = new FrontstageDef(); + frontstageDef.nineZoneState = createNineZoneState(); -describe("useNineZoneState", () => { - it("should return initial nineZoneState", () => { - const frontstageDef = new FrontstageDef(); - const nineZoneState = createNineZoneState(); - frontstageDef.nineZoneState = nineZoneState; - const { result } = renderHook(() => useNineZoneState(frontstageDef)); - nineZoneState.should.eq(result.current); - }); + const newFrontstageDef = new FrontstageDef(); + newFrontstageDef.nineZoneState = undefined; - it("should return nineZoneState of provided frontstageDef", () => { - const frontstageDef = new FrontstageDef(); - const nineZoneState = createNineZoneState(); - frontstageDef.nineZoneState = nineZoneState; - const newFrontstageDef = new FrontstageDef(); - const newNineZoneState = createNineZoneState(); - newFrontstageDef.nineZoneState = newNineZoneState; - const { result, rerender } = renderHook((def: FrontstageDef) => useNineZoneState(def), { - initialProps: frontstageDef, - }); - rerender(newFrontstageDef); - newNineZoneState.should.eq(result.current); - }); + const wrapper = mount<{ frontstageDef: FrontstageDef }>(); + wrapper.setProps({ frontstageDef: newFrontstageDef }); - it("should return updated nineZoneState", () => { - const frontstageDef = new FrontstageDef(); - const nineZoneState = createNineZoneState(); - const newNineZoneState = createNineZoneState(); - frontstageDef.nineZoneState = nineZoneState; - const { result } = renderHook(() => useNineZoneState(frontstageDef)); - act(() => { - frontstageDef.nineZoneState = newNineZoneState; + const nineZone = wrapper.find(NineZone); + nineZone.prop("state").should.eq(frontstageDef.nineZoneState); }); - newNineZoneState.should.eq(result.current); }); - it("should ignore nineZoneState changes of other frontstages", () => { - const frontstageDef = new FrontstageDef(); - const nineZoneState = createNineZoneState(); - const newNineZoneState = createNineZoneState(); - frontstageDef.nineZoneState = nineZoneState; - const { result } = renderHook(() => useNineZoneState(frontstageDef)); - act(() => { - (new FrontstageDef()).nineZoneState = newNineZoneState; + describe("useNineZoneDispatch", () => { + beforeEach(() => { + sinon.stub(FrontstageManager, "nineZoneSize").set(() => { }); }); - nineZoneState.should.eq(result.current); - }); -}); -describe("useSavedFrontstageState", () => { - it("should load saved nineZoneState", async () => { - const setting = createFrontstageState(); - const uiSettings = new UiSettingsStub(); - sinon.stub(uiSettings, "getSetting").resolves({ - status: UiSettingsStatus.Success, - setting, - }); - const frontstageDef = new FrontstageDef(); - renderHook(() => useSavedFrontstageState(frontstageDef), { - wrapper: (props) => , + it("should modify nineZoneState with default NineZoneReducer", () => { + const frontstageDef = new FrontstageDef(); + const nineZoneState = createNineZoneState(); + frontstageDef.nineZoneState = nineZoneState; + const { result } = renderHook(() => useNineZoneDispatch(frontstageDef)); + result.current({ + type: "PANEL_INITIALIZE", + side: "left", + size: 200, + }); + frontstageDef.nineZoneState.should.not.eq(nineZoneState); + (frontstageDef.nineZoneState.panels.left.size === 200).should.true; }); - await TestUtils.flushAsyncOperations(); - frontstageDef.nineZoneState?.should.matchSnapshot(); - }); - it("should not load nineZoneState when nineZoneState is already initialized", async () => { - const frontstageDef = new FrontstageDef(); - frontstageDef.nineZoneState = createNineZoneState(); - const uiSettings = new UiSettingsStub(); - const spy = sinon.spy(uiSettings, "getSetting"); - renderHook(() => useSavedFrontstageState(frontstageDef), { - wrapper: (props) => , + it("should not modify when nineZoneState is not defined", () => { + const frontstageDef = new FrontstageDef(); + frontstageDef.nineZoneState = undefined; + const { result } = renderHook(() => useNineZoneDispatch(frontstageDef)); + result.current({ + type: "PANEL_INITIALIZE", + side: "left", + size: 200, + }); + (frontstageDef.nineZoneState === undefined).should.true; }); - spy.notCalled.should.true; - }); - it("should initialize nineZoneState", async () => { - const setting = createFrontstageState(); - const uiSettings = new UiSettingsStub(); - sinon.stub(uiSettings, "getSetting").returns(Promise.resolve({ - status: UiSettingsStatus.Success, - setting, - })); - const frontstageDef = new FrontstageDef(); - sinon.stub(frontstageDef, "version").get(() => setting.version + 1); - renderHook(() => useSavedFrontstageState(frontstageDef), { - wrapper: (props) => , - }); - await TestUtils.flushAsyncOperations(); - (frontstageDef.nineZoneState !== undefined).should.true; - frontstageDef.nineZoneState!.should.not.eq(setting.nineZone); - }); - - it("should add missing widgets", async () => { - const setting = createFrontstageState(); - const uiSettings = new UiSettingsStub(); - sinon.stub(uiSettings, "getSetting").resolves({ - status: UiSettingsStatus.Success, - setting, - }); - const frontstageDef = new FrontstageDef(); - const leftPanel = new StagePanelDef(); - leftPanel.initializeFromProps({ - resizable: true, - widgets: [ - , - ], - }, StagePanelLocation.Left); - sinon.stub(frontstageDef, "leftPanel").get(() => leftPanel); - - renderHook(() => useSavedFrontstageState(frontstageDef), { - wrapper: (props) => , - }); - await TestUtils.flushAsyncOperations(); - - should().exist(frontstageDef.nineZoneState?.tabs.w1); - }); -}); - -describe("useSaveFrontstageSettings", () => { - it("should save frontstage settings", () => { - const fakeTimers = sinon.useFakeTimers(); - const uiSettings = new UiSettingsStub(); - const spy = sinon.stub(uiSettings, "saveSetting").resolves({ - status: UiSettingsStatus.Success, - }); - const frontstageDef = new FrontstageDef(); - frontstageDef.nineZoneState = createNineZoneState(); - renderHook(() => useSaveFrontstageSettings(frontstageDef), { - wrapper: (props) => , + it("should set nineZoneSize when RESIZE is received", () => { + const spy = sinon.stub(FrontstageManager, "nineZoneSize").set(() => { }); + const frontstageDef = new FrontstageDef(); + frontstageDef.nineZoneState = createNineZoneState(); + const { result } = renderHook(() => useNineZoneDispatch(frontstageDef)); + result.current({ + type: "RESIZE", + size: { + width: 5, + height: 10, + }, + }); + spy.calledOnceWithExactly(sinon.match({ width: 5, height: 10 })); }); - fakeTimers.tick(1000); - fakeTimers.restore(); - - spy.calledOnce.should.true; - }); - it("should not save if tab is dragged", () => { - const fakeTimers = sinon.useFakeTimers(); - const uiSettings = new UiSettingsStub(); - const spy = sinon.stub(uiSettings, "saveSetting").resolves({ - status: UiSettingsStatus.Success, - }); - const frontstageDef = new FrontstageDef(); - frontstageDef.nineZoneState = produce(createNineZoneState(), (draft) => { - draft.draggedTab = createDraggedTabState("t1"); + it("should set vertical (left/right) panel max size from percentage spec", () => { + const frontstageDef = new FrontstageDef(); + const panel = new StagePanelDef(); + sinon.stub(panel, "maxSizeSpec").get(() => ({ percentage: 50 })); + sinon.stub(frontstageDef, "leftPanel").get(() => panel); + frontstageDef.nineZoneState = createNineZoneState(); + const { result } = renderHook(() => useNineZoneDispatch(frontstageDef)); + result.current({ + type: "RESIZE", + size: { + height: 200, + width: 500, + }, + }); + frontstageDef.nineZoneState.panels.left.maxSize.should.eq(250); }); - renderHook(() => useSaveFrontstageSettings(frontstageDef), { - wrapper: (props) => , + + it("should set horizontal (top/bottom) panel max size from percentage spec", () => { + const frontstageDef = new FrontstageDef(); + const panel = new StagePanelDef(); + sinon.stub(panel, "maxSizeSpec").get(() => ({ percentage: 50 })); + sinon.stub(frontstageDef, "topPanel").get(() => panel); + frontstageDef.nineZoneState = createNineZoneState(); + const { result } = renderHook(() => useNineZoneDispatch(frontstageDef)); + result.current({ + type: "RESIZE", + size: { + height: 200, + width: 500, + }, + }); + frontstageDef.nineZoneState.panels.top.maxSize.should.eq(100); }); - fakeTimers.tick(1000); - fakeTimers.restore(); - spy.notCalled.should.true; - }); -}); + it("should update panel size", () => { + const frontstageDef = new FrontstageDef(); + const panel = new StagePanelDef(); + sinon.stub(panel, "maxSizeSpec").get(() => 250); + sinon.stub(frontstageDef, "leftPanel").get(() => panel); -describe("useFrontstageManager", () => { - it("should not handle onWidgetStateChangedEvent when nineZoneState is unset", () => { - const frontstageDef = new FrontstageDef(); - frontstageDef.nineZoneState = undefined; - renderHook(() => useFrontstageManager(frontstageDef)); - const widgetDef = new WidgetDef({}); - FrontstageManager.onWidgetStateChangedEvent.emit({ - widgetDef, - widgetState: WidgetState.Open, - }); - (frontstageDef.nineZoneState === undefined).should.true; + let state = createNineZoneState(); + state = produce(state, (draft) => { + draft.panels.left.size = 300; + }); + frontstageDef.nineZoneState = state; + const { result } = renderHook(() => useNineZoneDispatch(frontstageDef)); + result.current({ + type: "RESIZE", + size: { + height: 200, + width: 500, + }, + }); + frontstageDef.nineZoneState.panels.left.size!.should.eq(250); + }); }); - it("should handle onWidgetStateChangedEvent", () => { - const frontstageDef = new FrontstageDef(); - let nineZoneState = createNineZoneState(); - nineZoneState = addPanelWidget(nineZoneState, "left", "w1", ["t1"]); - nineZoneState = addPanelWidget(nineZoneState, "left", "w2", ["t2"]); - nineZoneState = addTab(nineZoneState, "t1"); - frontstageDef.nineZoneState = nineZoneState; - renderHook(() => useFrontstageManager(frontstageDef)); - const widgetDef = new WidgetDef({ - id: "t1", - }); - FrontstageManager.onWidgetStateChangedEvent.emit({ - widgetDef, - widgetState: WidgetState.Closed, - }); - frontstageDef.nineZoneState.widgets.w1.minimized.should.true; - }); + describe("useNineZoneState", () => { + it("should return initial nineZoneState", () => { + const frontstageDef = new FrontstageDef(); + const nineZoneState = createNineZoneState(); + frontstageDef.nineZoneState = nineZoneState; + const { result } = renderHook(() => useNineZoneState(frontstageDef)); + nineZoneState.should.eq(result.current); + }); - it("should handle onWidgetShowEvent", () => { - const frontstageDef = new FrontstageDef(); - let nineZoneState = createNineZoneState(); - nineZoneState = addPanelWidget(nineZoneState, "left", "w1", ["t1"]); - nineZoneState = addTab(nineZoneState, "t1"); - nineZoneState = produce(nineZoneState, (draft) => { - draft.panels.left.collapsed = true; + it("should return nineZoneState of provided frontstageDef", () => { + const frontstageDef = new FrontstageDef(); + const nineZoneState = createNineZoneState(); + frontstageDef.nineZoneState = nineZoneState; + const newFrontstageDef = new FrontstageDef(); + const newNineZoneState = createNineZoneState(); + newFrontstageDef.nineZoneState = newNineZoneState; + const { result, rerender } = renderHook((def: FrontstageDef) => useNineZoneState(def), { + initialProps: frontstageDef, + }); + rerender(newFrontstageDef); + newNineZoneState.should.eq(result.current); }); - frontstageDef.nineZoneState = nineZoneState; - renderHook(() => useFrontstageManager(frontstageDef)); - const widgetDef = new WidgetDef({ - id: "t1", + + it("should return updated nineZoneState", () => { + const frontstageDef = new FrontstageDef(); + const nineZoneState = createNineZoneState(); + const newNineZoneState = createNineZoneState(); + frontstageDef.nineZoneState = nineZoneState; + const { result } = renderHook(() => useNineZoneState(frontstageDef)); + act(() => { + frontstageDef.nineZoneState = newNineZoneState; + }); + newNineZoneState.should.eq(result.current); }); - FrontstageManager.onWidgetShowEvent.emit({ - widgetDef, + + it("should ignore nineZoneState changes of other frontstages", () => { + const frontstageDef = new FrontstageDef(); + const nineZoneState = createNineZoneState(); + const newNineZoneState = createNineZoneState(); + frontstageDef.nineZoneState = nineZoneState; + const { result } = renderHook(() => useNineZoneState(frontstageDef)); + act(() => { + (new FrontstageDef()).nineZoneState = newNineZoneState; + }); + nineZoneState.should.eq(result.current); }); - frontstageDef.nineZoneState.panels.left.collapsed.should.false; }); - it("should handle onWidgetExpandEvent", () => { - const frontstageDef = new FrontstageDef(); - let nineZoneState = createNineZoneState(); - nineZoneState = addPanelWidget(nineZoneState, "left", "w1", ["t1"], { minimized: true }); - nineZoneState = addPanelWidget(nineZoneState, "left", "w2", ["t2"]); - nineZoneState = addTab(nineZoneState, "t1"); - frontstageDef.nineZoneState = nineZoneState; - renderHook(() => useFrontstageManager(frontstageDef)); - const widgetDef = new WidgetDef({ - id: "t1", - }); - FrontstageManager.onWidgetExpandEvent.emit({ - widgetDef, - }); - frontstageDef.nineZoneState.widgets.w1.minimized.should.false; - }); + describe("useSavedFrontstageState", () => { + it("should load saved nineZoneState", async () => { + const setting = createFrontstageState(); + const uiSettings = new UiSettingsStub(); + sinon.stub(uiSettings, "getSetting").resolves({ + status: UiSettingsStatus.Success, + setting, + }); + const frontstageDef = new FrontstageDef(); + renderHook(() => useSavedFrontstageState(frontstageDef), { + wrapper: (props) => , + }); + await TestUtils.flushAsyncOperations(); + frontstageDef.nineZoneState?.should.matchSnapshot(); + }); - describe("onFrontstageRestoreLayoutEvent", () => { - it("should delete saved setting", () => { + it("should not load nineZoneState when nineZoneState is already initialized", async () => { const frontstageDef = new FrontstageDef(); frontstageDef.nineZoneState = createNineZoneState(); const uiSettings = new UiSettingsStub(); - const spy = sinon.spy(uiSettings, "deleteSetting"); - renderHook(() => useFrontstageManager(frontstageDef), { - wrapper: (props) => , + const spy = sinon.spy(uiSettings, "getSetting"); + renderHook(() => useSavedFrontstageState(frontstageDef), { + wrapper: (props) => , + }); + spy.notCalled.should.true; + }); + + it("should initialize nineZoneState", async () => { + const setting = createFrontstageState(); + const uiSettings = new UiSettingsStub(); + sinon.stub(uiSettings, "getSetting").returns(Promise.resolve({ + status: UiSettingsStatus.Success, + setting, + })); + const frontstageDef = new FrontstageDef(); + sinon.stub(frontstageDef, "version").get(() => setting.version + 1); + renderHook(() => useSavedFrontstageState(frontstageDef), { + wrapper: (props) => , + }); + await TestUtils.flushAsyncOperations(); + (frontstageDef.nineZoneState !== undefined).should.true; + frontstageDef.nineZoneState!.should.not.eq(setting.nineZone); + }); + + it("should add missing widgets", async () => { + const setting = createFrontstageState(); + const uiSettings = new UiSettingsStub(); + sinon.stub(uiSettings, "getSetting").resolves({ + status: UiSettingsStatus.Success, + setting, }); - FrontstageManager.onFrontstageRestoreLayoutEvent.emit({ - frontstageDef, + const frontstageDef = new FrontstageDef(); + const leftPanel = new StagePanelDef(); + leftPanel.initializeFromProps({ + resizable: true, + widgets: [ + , + ], + }, StagePanelLocation.Left); + sinon.stub(frontstageDef, "leftPanel").get(() => leftPanel); + + renderHook(() => useSavedFrontstageState(frontstageDef), { + wrapper: (props) => , }); - spy.calledOnce.should.true; + await TestUtils.flushAsyncOperations(); + + should().exist(frontstageDef.nineZoneState?.tabs.w1); }); + }); - it("should unset nineZoneState", () => { + describe("useSaveFrontstageSettings", () => { + it("should save frontstage settings", () => { + const fakeTimers = sinon.useFakeTimers(); + const uiSettings = new UiSettingsStub(); + const spy = sinon.stub(uiSettings, "saveSetting").resolves({ + status: UiSettingsStatus.Success, + }); const frontstageDef = new FrontstageDef(); frontstageDef.nineZoneState = createNineZoneState(); + renderHook(() => useSaveFrontstageSettings(frontstageDef), { + wrapper: (props) => , + }); + fakeTimers.tick(1000); + fakeTimers.restore(); + + spy.calledOnce.should.true; + }); + + it("should not save if tab is dragged", () => { + const fakeTimers = sinon.useFakeTimers(); const uiSettings = new UiSettingsStub(); - renderHook(() => useFrontstageManager(frontstageDef), { - wrapper: (props) => , + const spy = sinon.stub(uiSettings, "saveSetting").resolves({ + status: UiSettingsStatus.Success, }); - const frontstageDef1 = new FrontstageDef(); - sinon.stub(frontstageDef1, "id").get(() => "f1"); - frontstageDef1.nineZoneState = createNineZoneState(); - FrontstageManager.onFrontstageRestoreLayoutEvent.emit({ - frontstageDef: frontstageDef1, + const frontstageDef = new FrontstageDef(); + frontstageDef.nineZoneState = produce(createNineZoneState(), (draft) => { + draft.draggedTab = createDraggedTabState("t1"); + }); + renderHook(() => useSaveFrontstageSettings(frontstageDef), { + wrapper: (props) => , }); - (frontstageDef1.nineZoneState === undefined).should.true; + fakeTimers.tick(1000); + fakeTimers.restore(); + + spy.notCalled.should.true; }); }); - describe("onWidgetLabelChangedEvent", () => { - it("should update tab label", () => { + describe("useFrontstageManager", () => { + it("should not handle onWidgetStateChangedEvent when nineZoneState is unset", () => { const frontstageDef = new FrontstageDef(); - let state = createNineZoneState(); - state = addPanelWidget(state, "left", "w1", ["t1"]); - state = addTab(state, "t1"); - frontstageDef.nineZoneState = state; - const widgetDef = new WidgetDef({ id: "t1" }); + frontstageDef.nineZoneState = undefined; renderHook(() => useFrontstageManager(frontstageDef)); - - sinon.stub(widgetDef, "label").get(() => "test"); - FrontstageManager.onWidgetLabelChangedEvent.emit({ + const widgetDef = new WidgetDef({}); + FrontstageManager.onWidgetStateChangedEvent.emit({ widgetDef, + widgetState: WidgetState.Open, }); + (frontstageDef.nineZoneState === undefined).should.true; + }); - frontstageDef.nineZoneState.tabs.t1.label.should.eq("test"); + it("should handle onWidgetStateChangedEvent", () => { + const frontstageDef = new FrontstageDef(); + let nineZoneState = createNineZoneState(); + nineZoneState = addPanelWidget(nineZoneState, "left", "w1", ["t1"]); + nineZoneState = addPanelWidget(nineZoneState, "left", "w2", ["t2"]); + nineZoneState = addTab(nineZoneState, "t1"); + frontstageDef.nineZoneState = nineZoneState; + renderHook(() => useFrontstageManager(frontstageDef)); + const widgetDef = new WidgetDef({ + id: "t1", + }); + FrontstageManager.onWidgetStateChangedEvent.emit({ + widgetDef, + widgetState: WidgetState.Closed, + }); + frontstageDef.nineZoneState.widgets.w1.minimized.should.true; }); - it("should not fail if tab doesn't exist", () => { + it("should handle onWidgetShowEvent", () => { const frontstageDef = new FrontstageDef(); - frontstageDef.nineZoneState = createNineZoneState(); - const widgetDef = new WidgetDef({ id: "t1" }); + let nineZoneState = createNineZoneState(); + nineZoneState = addPanelWidget(nineZoneState, "left", "w1", ["t1"]); + nineZoneState = addTab(nineZoneState, "t1"); + nineZoneState = produce(nineZoneState, (draft) => { + draft.panels.left.collapsed = true; + }); + frontstageDef.nineZoneState = nineZoneState; renderHook(() => useFrontstageManager(frontstageDef)); + const widgetDef = new WidgetDef({ + id: "t1", + }); + FrontstageManager.onWidgetShowEvent.emit({ + widgetDef, + }); + frontstageDef.nineZoneState.panels.left.collapsed.should.false; + }); - sinon.stub(widgetDef, "label").get(() => "test"); + it("should handle onWidgetExpandEvent", () => { + const frontstageDef = new FrontstageDef(); + let nineZoneState = createNineZoneState(); + nineZoneState = addPanelWidget(nineZoneState, "left", "w1", ["t1"], { minimized: true }); + nineZoneState = addPanelWidget(nineZoneState, "left", "w2", ["t2"]); + nineZoneState = addTab(nineZoneState, "t1"); + frontstageDef.nineZoneState = nineZoneState; + renderHook(() => useFrontstageManager(frontstageDef)); + const widgetDef = new WidgetDef({ + id: "t1", + }); + FrontstageManager.onWidgetExpandEvent.emit({ + widgetDef, + }); + frontstageDef.nineZoneState.widgets.w1.minimized.should.false; + }); + + describe("onFrontstageRestoreLayoutEvent", () => { + it("should delete saved setting", () => { + const frontstageDef = new FrontstageDef(); + frontstageDef.nineZoneState = createNineZoneState(); + const uiSettings = new UiSettingsStub(); + const spy = sinon.spy(uiSettings, "deleteSetting"); + renderHook(() => useFrontstageManager(frontstageDef), { + wrapper: (props) => , + }); + FrontstageManager.onFrontstageRestoreLayoutEvent.emit({ + frontstageDef, + }); + spy.calledOnce.should.true; + }); - (() => { - FrontstageManager.onWidgetLabelChangedEvent.emit({ widgetDef }); - }).should.not.throw(); + it("should unset nineZoneState", () => { + const frontstageDef = new FrontstageDef(); + frontstageDef.nineZoneState = createNineZoneState(); + const uiSettings = new UiSettingsStub(); + renderHook(() => useFrontstageManager(frontstageDef), { + wrapper: (props) => , + }); + const frontstageDef1 = new FrontstageDef(); + sinon.stub(frontstageDef1, "id").get(() => "f1"); + frontstageDef1.nineZoneState = createNineZoneState(); + FrontstageManager.onFrontstageRestoreLayoutEvent.emit({ + frontstageDef: frontstageDef1, + }); + (frontstageDef1.nineZoneState === undefined).should.true; + }); }); - }); -}); -describe("useSyncDefinitions", () => { - it("should set panel widget state to Open", () => { - const frontstageDef = new FrontstageDef(); - const zoneDef = new ZoneDef(); - sinon.stub(frontstageDef, "centerRight").get(() => zoneDef); - const widgetDef = new WidgetDef({}); - sinon.stub(widgetDef, "id").get(() => "t1"); - const spy = sinon.spy(widgetDef, "setWidgetState"); - zoneDef.addWidgetDef(widgetDef); - renderHook(() => useSyncDefinitions(frontstageDef)); - act(() => { - let nineZone = createNineZoneState(); - nineZone = addPanelWidget(nineZone, "left", "w1", ["t1"]); - nineZone = addTab(nineZone, "t1"); - frontstageDef.nineZoneState = nineZone; - }); - spy.calledOnceWithExactly(WidgetState.Open).should.true; - }); + describe("onWidgetLabelChangedEvent", () => { + it("should update tab label", () => { + const frontstageDef = new FrontstageDef(); + let state = createNineZoneState(); + state = addPanelWidget(state, "left", "w1", ["t1"]); + state = addTab(state, "t1"); + frontstageDef.nineZoneState = state; + const widgetDef = new WidgetDef({ id: "t1" }); + renderHook(() => useFrontstageManager(frontstageDef)); - it("should set panel widget state to Closed", () => { - const frontstageDef = new FrontstageDef(); - const zoneDef = new ZoneDef(); - sinon.stub(frontstageDef, "centerRight").get(() => zoneDef); - const widgetDef = new WidgetDef({}); - sinon.stub(widgetDef, "id").get(() => "t1"); - const spy = sinon.spy(widgetDef, "setWidgetState"); - zoneDef.addWidgetDef(widgetDef); - renderHook(() => useSyncDefinitions(frontstageDef)); - act(() => { - let nineZone = createNineZoneState(); - nineZone = addPanelWidget(nineZone, "left", "w1", ["t1", "t2"], { activeTabId: "t2" }); - nineZone = addTab(nineZone, "t1"); - nineZone = addTab(nineZone, "t2"); - frontstageDef.nineZoneState = nineZone; - }); - spy.calledOnceWithExactly(WidgetState.Closed).should.true; - }); + sinon.stub(widgetDef, "label").get(() => "test"); + FrontstageManager.onWidgetLabelChangedEvent.emit({ + widgetDef, + }); - it("should set StagePanelDef size", () => { - const frontstageDef = new FrontstageDef(); - const rightPanel = new StagePanelDef(); - sinon.stub(frontstageDef, "rightPanel").get(() => rightPanel); - const spy = sinon.spy(rightPanel, "size", ["set"]); - renderHook(() => useSyncDefinitions(frontstageDef)); - act(() => { - let nineZone = createNineZoneState(); - nineZone = produce(nineZone, (draft) => { - draft.panels.right.size = 234; + frontstageDef.nineZoneState.tabs.t1.label.should.eq("test"); + }); + + it("should not fail if tab doesn't exist", () => { + const frontstageDef = new FrontstageDef(); + frontstageDef.nineZoneState = createNineZoneState(); + const widgetDef = new WidgetDef({ id: "t1" }); + renderHook(() => useFrontstageManager(frontstageDef)); + + sinon.stub(widgetDef, "label").get(() => "test"); + + (() => { + FrontstageManager.onWidgetLabelChangedEvent.emit({ widgetDef }); + }).should.not.throw(); }); - frontstageDef.nineZoneState = nineZone; }); - sinon.assert.calledOnceWithExactly(spy.set, 234); }); - it("should set StagePanelState.Off", () => { - const frontstageDef = new FrontstageDef(); - const rightPanel = new StagePanelDef(); - const spy = sinon.spy(); - sinon.stub(rightPanel, "panelState").get(() => StagePanelState.Off).set(spy); - sinon.stub(frontstageDef, "rightPanel").get(() => rightPanel); - renderHook(() => useSyncDefinitions(frontstageDef)); - act(() => { - let nineZone = createNineZoneState(); - nineZone = produce(nineZone, (draft) => { - draft.panels.right.collapsed = true; + describe("useSyncDefinitions", () => { + it("should set panel widget state to Open", () => { + const frontstageDef = new FrontstageDef(); + const zoneDef = new ZoneDef(); + sinon.stub(frontstageDef, "centerRight").get(() => zoneDef); + const widgetDef = new WidgetDef({}); + sinon.stub(widgetDef, "id").get(() => "t1"); + const spy = sinon.spy(widgetDef, "setWidgetState"); + zoneDef.addWidgetDef(widgetDef); + renderHook(() => useSyncDefinitions(frontstageDef)); + act(() => { + let nineZone = createNineZoneState(); + nineZone = addPanelWidget(nineZone, "left", "w1", ["t1"]); + nineZone = addTab(nineZone, "t1"); + frontstageDef.nineZoneState = nineZone; }); - frontstageDef.nineZoneState = nineZone; + spy.calledOnceWithExactly(WidgetState.Open).should.true; }); - sinon.assert.calledOnceWithExactly(spy, StagePanelState.Off); - }); - it("should set floating widget state to Open", () => { - const frontstageDef = new FrontstageDef(); - const zoneDef = new ZoneDef(); - sinon.stub(frontstageDef, "centerRight").get(() => zoneDef); - const widgetDef = new WidgetDef({}); - sinon.stub(widgetDef, "id").get(() => "t1"); - const spy = sinon.spy(widgetDef, "setWidgetState"); - zoneDef.addWidgetDef(widgetDef); - renderHook(() => useSyncDefinitions(frontstageDef)); - act(() => { - let nineZone = createNineZoneState(); - nineZone = addFloatingWidget(nineZone, "w1", ["t1"]); - nineZone = addTab(nineZone, "t1"); - frontstageDef.nineZoneState = nineZone; + it("should set panel widget state to Closed", () => { + const frontstageDef = new FrontstageDef(); + const zoneDef = new ZoneDef(); + sinon.stub(frontstageDef, "centerRight").get(() => zoneDef); + const widgetDef = new WidgetDef({}); + sinon.stub(widgetDef, "id").get(() => "t1"); + const spy = sinon.spy(widgetDef, "setWidgetState"); + zoneDef.addWidgetDef(widgetDef); + renderHook(() => useSyncDefinitions(frontstageDef)); + act(() => { + let nineZone = createNineZoneState(); + nineZone = addPanelWidget(nineZone, "left", "w1", ["t1", "t2"], { activeTabId: "t2" }); + nineZone = addTab(nineZone, "t1"); + nineZone = addTab(nineZone, "t2"); + frontstageDef.nineZoneState = nineZone; + }); + spy.calledOnceWithExactly(WidgetState.Closed).should.true; }); - spy.calledOnceWithExactly(WidgetState.Open).should.true; - }); - it("should set floating widget state to Closed", () => { - const frontstageDef = new FrontstageDef(); - const zoneDef = new ZoneDef(); - sinon.stub(frontstageDef, "centerRight").get(() => zoneDef); - const widgetDef = new WidgetDef({}); - sinon.stub(widgetDef, "id").get(() => "t1"); - const spy = sinon.spy(widgetDef, "setWidgetState"); - zoneDef.addWidgetDef(widgetDef); - renderHook(() => useSyncDefinitions(frontstageDef)); - act(() => { - let nineZone = createNineZoneState(); - nineZone = addFloatingWidget(nineZone, "w1", ["t1", "t2"], undefined, { activeTabId: "t2" }); - nineZone = addTab(nineZone, "t1"); - frontstageDef.nineZoneState = nineZone; + it("should set StagePanelDef size", () => { + const frontstageDef = new FrontstageDef(); + const rightPanel = new StagePanelDef(); + sinon.stub(frontstageDef, "rightPanel").get(() => rightPanel); + const spy = sinon.spy(rightPanel, "size", ["set"]); + renderHook(() => useSyncDefinitions(frontstageDef)); + act(() => { + let nineZone = createNineZoneState(); + nineZone = produce(nineZone, (draft) => { + draft.panels.right.size = 234; + }); + frontstageDef.nineZoneState = nineZone; + }); + sinon.assert.calledOnceWithExactly(spy.set, 234); }); - spy.calledOnceWithExactly(WidgetState.Closed).should.true; - }); -}); -describe("initializeNineZoneState", () => { - it("should initialize widgets", () => { - const frontstageDef = new FrontstageDef(); - sinon.stub(frontstageDef, "centerLeft").get(() => new ZoneDef()); - sinon.stub(frontstageDef, "bottomLeft").get(() => new ZoneDef()); - sinon.stub(frontstageDef, "leftPanel").get(() => new StagePanelDef()); - sinon.stub(frontstageDef, "centerRight").get(() => new ZoneDef()); - sinon.stub(frontstageDef, "bottomRight").get(() => new ZoneDef()); - sinon.stub(frontstageDef, "rightPanel").get(() => new StagePanelDef()); - sinon.stub(frontstageDef, "topPanel").get(() => new StagePanelDef()); - sinon.stub(frontstageDef, "topMostPanel").get(() => new StagePanelDef()); - sinon.stub(frontstageDef, "bottomPanel").get(() => new StagePanelDef()); - sinon.stub(frontstageDef, "bottomMostPanel").get(() => new StagePanelDef()); - const state = initializeNineZoneState(frontstageDef); - state.should.matchSnapshot(); - }); + it("should set StagePanelState.Off", () => { + const frontstageDef = new FrontstageDef(); + const rightPanel = new StagePanelDef(); + const spy = sinon.spy(); + sinon.stub(rightPanel, "panelState").get(() => StagePanelState.Off).set(spy); + sinon.stub(frontstageDef, "rightPanel").get(() => rightPanel); + renderHook(() => useSyncDefinitions(frontstageDef)); + act(() => { + let nineZone = createNineZoneState(); + nineZone = produce(nineZone, (draft) => { + draft.panels.right.collapsed = true; + }); + frontstageDef.nineZoneState = nineZone; + }); + sinon.assert.calledOnceWithExactly(spy, StagePanelState.Off); + }); - it("should keep one widget open", () => { - const frontstageDef = new FrontstageDef(); - const centerLeft = new ZoneDef(); - const widgetDef = new WidgetDef({ - id: "w1", + it("should set floating widget state to Open", () => { + const frontstageDef = new FrontstageDef(); + const zoneDef = new ZoneDef(); + sinon.stub(frontstageDef, "centerRight").get(() => zoneDef); + const widgetDef = new WidgetDef({}); + sinon.stub(widgetDef, "id").get(() => "t1"); + const spy = sinon.spy(widgetDef, "setWidgetState"); + zoneDef.addWidgetDef(widgetDef); + renderHook(() => useSyncDefinitions(frontstageDef)); + act(() => { + let nineZone = createNineZoneState(); + nineZone = addFloatingWidget(nineZone, "w1", ["t1"]); + nineZone = addTab(nineZone, "t1"); + frontstageDef.nineZoneState = nineZone; + }); + spy.calledOnceWithExactly(WidgetState.Open).should.true; }); - sinon.stub(frontstageDef, "centerLeft").get(() => centerLeft); - sinon.stub(centerLeft, "widgetDefs").get(() => [widgetDef]); - const state = initializeNineZoneState(frontstageDef); - state.widgets.leftStart.activeTabId.should.eq("w1"); - }); - it("should initialize size", () => { - sinon.stub(FrontstageManager, "nineZoneSize").get(() => new Size(10, 20)); - const frontstageDef = new FrontstageDef(); - const sut = initializeNineZoneState(frontstageDef); - sut.size.should.eql({ width: 10, height: 20 }); + it("should set floating widget state to Closed", () => { + const frontstageDef = new FrontstageDef(); + const zoneDef = new ZoneDef(); + sinon.stub(frontstageDef, "centerRight").get(() => zoneDef); + const widgetDef = new WidgetDef({}); + sinon.stub(widgetDef, "id").get(() => "t1"); + const spy = sinon.spy(widgetDef, "setWidgetState"); + zoneDef.addWidgetDef(widgetDef); + renderHook(() => useSyncDefinitions(frontstageDef)); + act(() => { + let nineZone = createNineZoneState(); + nineZone = addFloatingWidget(nineZone, "w1", ["t1", "t2"], undefined, { activeTabId: "t2" }); + nineZone = addTab(nineZone, "t1"); + frontstageDef.nineZoneState = nineZone; + }); + spy.calledOnceWithExactly(WidgetState.Closed).should.true; + }); }); - it("should not initialize size", () => { - const frontstageDef = new FrontstageDef(); - const sut = initializeNineZoneState(frontstageDef); - sut.size.should.eql({ width: 0, height: 0 }); - }); + describe("initializeNineZoneState", () => { + it("should initialize widgets", () => { + const frontstageDef = new FrontstageDef(); + sinon.stub(frontstageDef, "centerLeft").get(() => new ZoneDef()); + sinon.stub(frontstageDef, "bottomLeft").get(() => new ZoneDef()); + sinon.stub(frontstageDef, "leftPanel").get(() => new StagePanelDef()); + sinon.stub(frontstageDef, "centerRight").get(() => new ZoneDef()); + sinon.stub(frontstageDef, "bottomRight").get(() => new ZoneDef()); + sinon.stub(frontstageDef, "rightPanel").get(() => new StagePanelDef()); + sinon.stub(frontstageDef, "topPanel").get(() => new StagePanelDef()); + sinon.stub(frontstageDef, "topMostPanel").get(() => new StagePanelDef()); + sinon.stub(frontstageDef, "bottomPanel").get(() => new StagePanelDef()); + sinon.stub(frontstageDef, "bottomMostPanel").get(() => new StagePanelDef()); + const state = initializeNineZoneState(frontstageDef); + state.should.matchSnapshot(); + }); + + it("should keep one widget open", () => { + const frontstageDef = new FrontstageDef(); + const centerLeft = new ZoneDef(); + const widgetDef = new WidgetDef({ + id: "w1", + }); + sinon.stub(frontstageDef, "centerLeft").get(() => centerLeft); + sinon.stub(centerLeft, "widgetDefs").get(() => [widgetDef]); + const state = initializeNineZoneState(frontstageDef); + state.widgets.leftStart.activeTabId.should.eq("w1"); + }); - it("should initialize preferredPanelWidgetSize of tool settings widget", () => { - const frontstageDef = new FrontstageDef(); - const zoneDef = new ZoneDef(); - const widgetDef = new WidgetDef({ - id: "w1", - preferredPanelSize: "fit-content", - }); - sinon.stub(frontstageDef, "topCenter").get(() => zoneDef); - sinon.stub(zoneDef, "getSingleWidgetDef").returns(widgetDef); - const sut = initializeNineZoneState(frontstageDef); - sut.tabs[toolSettingsTabId].preferredPanelWidgetSize!.should.eq("fit-content"); - }); + it("should initialize size", () => { + sinon.stub(FrontstageManager, "nineZoneSize").get(() => new Size(10, 20)); + const frontstageDef = new FrontstageDef(); + const sut = initializeNineZoneState(frontstageDef); + sut.size.should.eql({ width: 10, height: 20 }); + }); - it("should add panel zone widgets", () => { - const frontstageDef = new FrontstageDef(); - const panelDef = new StagePanelDef(); - const start = new StagePanelZoneDef(); - const middle = new StagePanelZoneDef(); - const end = new StagePanelZoneDef(); - const w1 = new WidgetDef({ id: "w1" }); - const w2 = new WidgetDef({ id: "w2" }); - const w3 = new WidgetDef({ id: "w3" }); - sinon.stub(frontstageDef, "leftPanel").get(() => panelDef); - sinon.stub(panelDef.panelZones, "start").get(() => start); - sinon.stub(panelDef.panelZones, "middle").get(() => middle); - sinon.stub(panelDef.panelZones, "end").get(() => end); - sinon.stub(start, "widgetDefs").get(() => [w1]); - sinon.stub(middle, "widgetDefs").get(() => [w2]); - sinon.stub(end, "widgetDefs").get(() => [w3]); - const state = initializeNineZoneState(frontstageDef); - state.panels.left.widgets.should.eql(["leftStart", "leftMiddle", "leftEnd"]); - should().exist("w1"); - should().exist("w2"); - should().exist("w3"); - }); -}); + it("should not initialize size", () => { + const frontstageDef = new FrontstageDef(); + const sut = initializeNineZoneState(frontstageDef); + sut.size.should.eql({ width: 0, height: 0 }); + }); -describe("addPanelWidgets", () => { - it("should add widgets from panel zones", () => { - let state = createNineZoneState(); - const frontstageDef = new FrontstageDef(); - const leftPanel = new StagePanelDef(); - const panelZones = new StagePanelZonesDef(); - const panelZone = new StagePanelZoneDef(); - const widgetDef = new WidgetDef({ - id: "w1", - }); - sinon.stub(frontstageDef, "leftPanel").get(() => leftPanel); - sinon.stub(leftPanel, "panelZones").get(() => panelZones); - sinon.stub(panelZones, "start").get(() => panelZone); - sinon.stub(panelZone, "widgetDefs").get(() => [widgetDef]); - state = addPanelWidgets(state, frontstageDef, "left"); - state.panels.left.widgets[0].should.eq("leftStart"); - }); + it("should initialize preferredPanelWidgetSize of tool settings widget", () => { + const frontstageDef = new FrontstageDef(); + const zoneDef = new ZoneDef(); + const widgetDef = new WidgetDef({ + id: "w1", + preferredPanelSize: "fit-content", + }); + sinon.stub(frontstageDef, "topCenter").get(() => zoneDef); + sinon.stub(zoneDef, "getSingleWidgetDef").returns(widgetDef); + const sut = initializeNineZoneState(frontstageDef); + sut.tabs[toolSettingsTabId].preferredPanelWidgetSize!.should.eq("fit-content"); + }); - it("should add bottomLeft widgets", () => { - let state = createNineZoneState(); - const frontstageDef = new FrontstageDef(); - const zoneDef = new ZoneDef(); - const widgetDef = new WidgetDef({ - id: "w1", - }); - sinon.stub(frontstageDef, "bottomLeft").get(() => zoneDef); - sinon.stub(zoneDef, "widgetDefs").get(() => [widgetDef]); - state = addPanelWidgets(state, frontstageDef, "left"); - state.panels.left.widgets[0].should.eq("leftMiddle"); - state.widgets.leftMiddle.tabs.should.eql(["w1"]); - }); + it("should add panel zone widgets", () => { + const frontstageDef = new FrontstageDef(); + const panelDef = new StagePanelDef(); + const start = new StagePanelZoneDef(); + const middle = new StagePanelZoneDef(); + const end = new StagePanelZoneDef(); + const w1 = new WidgetDef({ id: "w1" }); + const w2 = new WidgetDef({ id: "w2" }); + const w3 = new WidgetDef({ id: "w3" }); + sinon.stub(frontstageDef, "leftPanel").get(() => panelDef); + sinon.stub(panelDef.panelZones, "start").get(() => start); + sinon.stub(panelDef.panelZones, "middle").get(() => middle); + sinon.stub(panelDef.panelZones, "end").get(() => end); + sinon.stub(start, "widgetDefs").get(() => [w1]); + sinon.stub(middle, "widgetDefs").get(() => [w2]); + sinon.stub(end, "widgetDefs").get(() => [w3]); + const state = initializeNineZoneState(frontstageDef); + state.panels.left.widgets.should.eql(["leftStart", "leftMiddle", "leftEnd"]); + should().exist("w1"); + should().exist("w2"); + should().exist("w3"); + }); + }); + + describe("addPanelWidgets", () => { + it("should add widgets from panel zones", () => { + let state = createNineZoneState(); + const frontstageDef = new FrontstageDef(); + const leftPanel = new StagePanelDef(); + const panelZones = new StagePanelZonesDef(); + const panelZone = new StagePanelZoneDef(); + const widgetDef = new WidgetDef({ + id: "w1", + }); + sinon.stub(frontstageDef, "leftPanel").get(() => leftPanel); + sinon.stub(leftPanel, "panelZones").get(() => panelZones); + sinon.stub(panelZones, "start").get(() => panelZone); + sinon.stub(panelZone, "widgetDefs").get(() => [widgetDef]); + state = addPanelWidgets(state, frontstageDef, "left"); + state.panels.left.widgets[0].should.eq("leftStart"); + }); - it("should add centerRight widgets", () => { - let state = createNineZoneState(); - const frontstageDef = new FrontstageDef(); - const zoneDef = new ZoneDef(); - const widgetDef = new WidgetDef({ - id: "w1", - }); - sinon.stub(frontstageDef, "centerRight").get(() => zoneDef); - sinon.stub(zoneDef, "widgetDefs").get(() => [widgetDef]); - state = addPanelWidgets(state, frontstageDef, "right"); - state.panels.right.widgets[0].should.eq("rightStart"); - state.widgets.rightStart.tabs.should.eql(["w1"]); - }); + it("should add bottomLeft widgets", () => { + let state = createNineZoneState(); + const frontstageDef = new FrontstageDef(); + const zoneDef = new ZoneDef(); + const widgetDef = new WidgetDef({ + id: "w1", + }); + sinon.stub(frontstageDef, "bottomLeft").get(() => zoneDef); + sinon.stub(zoneDef, "widgetDefs").get(() => [widgetDef]); + state = addPanelWidgets(state, frontstageDef, "left"); + state.panels.left.widgets[0].should.eq("leftMiddle"); + state.widgets.leftMiddle.tabs.should.eql(["w1"]); + }); - it("should add bottomRight widgets", () => { - let state = createNineZoneState(); - const frontstageDef = new FrontstageDef(); - const zoneDef = new ZoneDef(); - const widgetDef = new WidgetDef({ - id: "w1", - }); - sinon.stub(frontstageDef, "bottomRight").get(() => zoneDef); - sinon.stub(zoneDef, "widgetDefs").get(() => [widgetDef]); - state = addPanelWidgets(state, frontstageDef, "right"); - state.panels.right.widgets[0].should.eq("rightMiddle"); - state.widgets.rightMiddle.tabs.should.eql(["w1"]); - }); + it("should add centerRight widgets", () => { + let state = createNineZoneState(); + const frontstageDef = new FrontstageDef(); + const zoneDef = new ZoneDef(); + const widgetDef = new WidgetDef({ + id: "w1", + }); + sinon.stub(frontstageDef, "centerRight").get(() => zoneDef); + sinon.stub(zoneDef, "widgetDefs").get(() => [widgetDef]); + state = addPanelWidgets(state, frontstageDef, "right"); + state.panels.right.widgets[0].should.eq("rightStart"); + state.widgets.rightStart.tabs.should.eql(["w1"]); + }); - it("should add leftPanel widgets", () => { - let state = createNineZoneState(); - const frontstageDef = new FrontstageDef(); - const panelDef = new StagePanelDef(); - const widgetDef = new WidgetDef({ - id: "w1", - }); - sinon.stub(frontstageDef, "leftPanel").get(() => panelDef); - sinon.stub(panelDef, "panelWidgetDefs").get(() => [widgetDef]); - state = addPanelWidgets(state, frontstageDef, "left"); - state.panels.left.widgets[0].should.eq("leftEnd"); - state.widgets.leftEnd.tabs.should.eql(["w1"]); - }); + it("should add bottomRight widgets", () => { + let state = createNineZoneState(); + const frontstageDef = new FrontstageDef(); + const zoneDef = new ZoneDef(); + const widgetDef = new WidgetDef({ + id: "w1", + }); + sinon.stub(frontstageDef, "bottomRight").get(() => zoneDef); + sinon.stub(zoneDef, "widgetDefs").get(() => [widgetDef]); + state = addPanelWidgets(state, frontstageDef, "right"); + state.panels.right.widgets[0].should.eq("rightMiddle"); + state.widgets.rightMiddle.tabs.should.eql(["w1"]); + }); - it("should add rightPanel widgets", () => { - let state = createNineZoneState(); - const frontstageDef = new FrontstageDef(); - const panelDef = new StagePanelDef(); - const widgetDef = new WidgetDef({ - id: "w1", - }); - sinon.stub(frontstageDef, "rightPanel").get(() => panelDef); - sinon.stub(panelDef, "panelWidgetDefs").get(() => [widgetDef]); - state = addPanelWidgets(state, frontstageDef, "right"); - state.panels.right.widgets[0].should.eq("rightEnd"); - state.widgets.rightEnd.tabs.should.eql(["w1"]); - }); + it("should add leftPanel widgets", () => { + let state = createNineZoneState(); + const frontstageDef = new FrontstageDef(); + const panelDef = new StagePanelDef(); + const widgetDef = new WidgetDef({ + id: "w1", + }); + sinon.stub(frontstageDef, "leftPanel").get(() => panelDef); + sinon.stub(panelDef, "panelWidgetDefs").get(() => [widgetDef]); + state = addPanelWidgets(state, frontstageDef, "left"); + state.panels.left.widgets[0].should.eq("leftEnd"); + state.widgets.leftEnd.tabs.should.eql(["w1"]); + }); - it("should add topPanel widgets", () => { - let state = createNineZoneState(); - const frontstageDef = new FrontstageDef(); - const panelDef = new StagePanelDef(); - const widgetDef = new WidgetDef({ - id: "w1", - }); - sinon.stub(frontstageDef, "topPanel").get(() => panelDef); - sinon.stub(panelDef, "panelWidgetDefs").get(() => [widgetDef]); - state = addPanelWidgets(state, frontstageDef, "top"); - state.panels.top.widgets[0].should.eq("topStart"); - state.widgets.topStart.tabs.should.eql(["w1"]); - }); + it("should add rightPanel widgets", () => { + let state = createNineZoneState(); + const frontstageDef = new FrontstageDef(); + const panelDef = new StagePanelDef(); + const widgetDef = new WidgetDef({ + id: "w1", + }); + sinon.stub(frontstageDef, "rightPanel").get(() => panelDef); + sinon.stub(panelDef, "panelWidgetDefs").get(() => [widgetDef]); + state = addPanelWidgets(state, frontstageDef, "right"); + state.panels.right.widgets[0].should.eq("rightEnd"); + state.widgets.rightEnd.tabs.should.eql(["w1"]); + }); - it("should add topMostPanel widgets", () => { - let state = createNineZoneState(); - const frontstageDef = new FrontstageDef(); - const panelDef = new StagePanelDef(); - const widgetDef = new WidgetDef({ - id: "w1", - }); - sinon.stub(frontstageDef, "topMostPanel").get(() => panelDef); - sinon.stub(panelDef, "panelWidgetDefs").get(() => [widgetDef]); - state = addPanelWidgets(state, frontstageDef, "top"); - state.panels.top.widgets[0].should.eq("topEnd"); - state.widgets.topEnd.tabs.should.eql(["w1"]); - }); + it("should add topPanel widgets", () => { + let state = createNineZoneState(); + const frontstageDef = new FrontstageDef(); + const panelDef = new StagePanelDef(); + const widgetDef = new WidgetDef({ + id: "w1", + }); + sinon.stub(frontstageDef, "topPanel").get(() => panelDef); + sinon.stub(panelDef, "panelWidgetDefs").get(() => [widgetDef]); + state = addPanelWidgets(state, frontstageDef, "top"); + state.panels.top.widgets[0].should.eq("topStart"); + state.widgets.topStart.tabs.should.eql(["w1"]); + }); - it("should add bottomPanel widgets", () => { - let state = createNineZoneState(); - const frontstageDef = new FrontstageDef(); - const panelDef = new StagePanelDef(); - const widgetDef = new WidgetDef({ - id: "w1", - }); - sinon.stub(frontstageDef, "bottomPanel").get(() => panelDef); - sinon.stub(panelDef, "panelWidgetDefs").get(() => [widgetDef]); - state = addPanelWidgets(state, frontstageDef, "bottom"); - state.panels.bottom.widgets[0].should.eq("bottomStart"); - state.widgets.bottomStart.tabs.should.eql(["w1"]); - }); + it("should add topMostPanel widgets", () => { + let state = createNineZoneState(); + const frontstageDef = new FrontstageDef(); + const panelDef = new StagePanelDef(); + const widgetDef = new WidgetDef({ + id: "w1", + }); + sinon.stub(frontstageDef, "topMostPanel").get(() => panelDef); + sinon.stub(panelDef, "panelWidgetDefs").get(() => [widgetDef]); + state = addPanelWidgets(state, frontstageDef, "top"); + state.panels.top.widgets[0].should.eq("topEnd"); + state.widgets.topEnd.tabs.should.eql(["w1"]); + }); - it("should add bottomMostPanel widgets", () => { - let state = createNineZoneState(); - const frontstageDef = new FrontstageDef(); - const panelDef = new StagePanelDef(); - const widgetDef = new WidgetDef({ - id: "w1", - }); - sinon.stub(frontstageDef, "bottomMostPanel").get(() => panelDef); - sinon.stub(panelDef, "panelWidgetDefs").get(() => [widgetDef]); - state = addPanelWidgets(state, frontstageDef, "bottom"); - state.panels.bottom.widgets[0].should.eq("bottomEnd"); - state.widgets.bottomEnd.tabs.should.eql(["w1"]); - }); -}); + it("should add bottomPanel widgets", () => { + let state = createNineZoneState(); + const frontstageDef = new FrontstageDef(); + const panelDef = new StagePanelDef(); + const widgetDef = new WidgetDef({ + id: "w1", + }); + sinon.stub(frontstageDef, "bottomPanel").get(() => panelDef); + sinon.stub(panelDef, "panelWidgetDefs").get(() => [widgetDef]); + state = addPanelWidgets(state, frontstageDef, "bottom"); + state.panels.bottom.widgets[0].should.eq("bottomStart"); + state.widgets.bottomStart.tabs.should.eql(["w1"]); + }); -describe("initializePanel", () => { - it("should initialize max size", () => { - const state = createNineZoneState(); - const frontstageDef = new FrontstageDef(); - const leftPanel = new StagePanelDef(); - sinon.stub(frontstageDef, "leftPanel").get(() => leftPanel); - sinon.stub(leftPanel, "maxSizeSpec").get(() => 100); - const sut = initializePanel(state, frontstageDef, "left"); - sut.panels.left.maxSize.should.eq(100); + it("should add bottomMostPanel widgets", () => { + let state = createNineZoneState(); + const frontstageDef = new FrontstageDef(); + const panelDef = new StagePanelDef(); + const widgetDef = new WidgetDef({ + id: "w1", + }); + sinon.stub(frontstageDef, "bottomMostPanel").get(() => panelDef); + sinon.stub(panelDef, "panelWidgetDefs").get(() => [widgetDef]); + state = addPanelWidgets(state, frontstageDef, "bottom"); + state.panels.bottom.widgets[0].should.eq("bottomEnd"); + state.widgets.bottomEnd.tabs.should.eql(["w1"]); + }); }); - it("should initialize min size", () => { - const state = createNineZoneState(); - const frontstageDef = new FrontstageDef(); - const leftPanel = new StagePanelDef(); - sinon.stub(frontstageDef, "leftPanel").get(() => leftPanel); - sinon.stub(leftPanel, "minSize").get(() => 50); - const sut = initializePanel(state, frontstageDef, "left"); - sut.panels.left.minSize.should.eq(50); - }); -}); + describe("initializePanel", () => { + it("should initialize max size", () => { + const state = createNineZoneState(); + const frontstageDef = new FrontstageDef(); + const leftPanel = new StagePanelDef(); + sinon.stub(frontstageDef, "leftPanel").get(() => leftPanel); + sinon.stub(leftPanel, "maxSizeSpec").get(() => 100); + const sut = initializePanel(state, frontstageDef, "left"); + sut.panels.left.maxSize.should.eq(100); + }); -describe("addWidgets", () => { - it("should use widget label", () => { - let state = createNineZoneState(); - const widget = new WidgetDef({ - id: "w1", - label: "Widget 1", + it("should initialize min size", () => { + const state = createNineZoneState(); + const frontstageDef = new FrontstageDef(); + const leftPanel = new StagePanelDef(); + sinon.stub(frontstageDef, "leftPanel").get(() => leftPanel); + sinon.stub(leftPanel, "minSize").get(() => 50); + const sut = initializePanel(state, frontstageDef, "left"); + sut.panels.left.minSize.should.eq(50); }); - state = addWidgets(state, [widget], "left", "leftStart"); - state.tabs.w1.label.should.eq("Widget 1"); }); - it("should activate tab based on widget state", () => { - let state = createNineZoneState(); - const widget = new WidgetDef({ - id: "w1", - defaultState: WidgetState.Open, + describe("addWidgets", () => { + it("should use widget label", () => { + let state = createNineZoneState(); + const widget = new WidgetDef({ + id: "w1", + label: "Widget 1", + }); + state = addWidgets(state, [widget], "left", "leftStart"); + state.tabs.w1.label.should.eq("Widget 1"); }); - state = addWidgets(state, [widget], "left", "leftStart"); - state.widgets.leftStart.activeTabId.should.eq("w1"); - }); -}); -describe("getWidgetId", () => { - it("should return 'leftStart'", () => { - getWidgetId("left", "start").should.eq("leftStart"); + it("should activate tab based on widget state", () => { + let state = createNineZoneState(); + const widget = new WidgetDef({ + id: "w1", + defaultState: WidgetState.Open, + }); + state = addWidgets(state, [widget], "left", "leftStart"); + state.widgets.leftStart.activeTabId.should.eq("w1"); + }); }); - it("should return 'leftMiddle'", () => { - getWidgetId("left", "middle").should.eq("leftMiddle"); - }); + describe("getWidgetId", () => { + it("should return 'leftStart'", () => { + getWidgetId("left", "start").should.eq("leftStart"); + }); - it("should return 'leftEnd'", () => { - getWidgetId("left", "end").should.eq("leftEnd"); - }); + it("should return 'leftMiddle'", () => { + getWidgetId("left", "middle").should.eq("leftMiddle"); + }); - it("should return 'rightStart'", () => { - getWidgetId("right", "start").should.eq("rightStart"); - }); + it("should return 'leftEnd'", () => { + getWidgetId("left", "end").should.eq("leftEnd"); + }); - it("should return 'rightMiddle'", () => { - getWidgetId("right", "middle").should.eq("rightMiddle"); - }); + it("should return 'rightStart'", () => { + getWidgetId("right", "start").should.eq("rightStart"); + }); - it("should return 'rightEnd'", () => { - getWidgetId("right", "end").should.eq("rightEnd"); - }); + it("should return 'rightMiddle'", () => { + getWidgetId("right", "middle").should.eq("rightMiddle"); + }); - it("should return 'topStart'", () => { - getWidgetId("top", "start").should.eq("topStart"); - }); + it("should return 'rightEnd'", () => { + getWidgetId("right", "end").should.eq("rightEnd"); + }); - it("should return 'topEnd'", () => { - getWidgetId("top", "end").should.eq("topEnd"); - }); + it("should return 'topStart'", () => { + getWidgetId("top", "start").should.eq("topStart"); + }); - it("should return 'bottomStart'", () => { - getWidgetId("bottom", "start").should.eq("bottomStart"); - }); + it("should return 'topEnd'", () => { + getWidgetId("top", "end").should.eq("topEnd"); + }); - it("should return 'bottomEnd'", () => { - getWidgetId("bottom", "end").should.eq("bottomEnd"); - }); -}); + it("should return 'bottomStart'", () => { + getWidgetId("bottom", "start").should.eq("bottomStart"); + }); -describe("isFrontstageStateSettingResult", () => { - it("isFrontstageStateSettingResult", () => { - isFrontstageStateSettingResult({ status: UiSettingsStatus.UnknownError }).should.false; + it("should return 'bottomEnd'", () => { + getWidgetId("bottom", "end").should.eq("bottomEnd"); + }); }); -}); -describe("setWidgetState", () => { - it("should not update for other states", () => { - let nineZone = createNineZoneState(); - nineZone = addPanelWidget(nineZone, "left", "w1", ["t1"]); - nineZone = addTab(nineZone, "t1"); - const sut = setWidgetState(nineZone, new WidgetDef({ id: "t1" }), WidgetState.Floating); - sut.should.eq(nineZone); + describe("isFrontstageStateSettingResult", () => { + it("isFrontstageStateSettingResult", () => { + isFrontstageStateSettingResult({ status: UiSettingsStatus.UnknownError }).should.false; + }); }); - describe("WidgetState.Open", () => { - it("should open widget", () => { + describe("setWidgetState", () => { + it("should not update for other states", () => { let nineZone = createNineZoneState(); nineZone = addPanelWidget(nineZone, "left", "w1", ["t1"]); nineZone = addTab(nineZone, "t1"); - const sut = setWidgetState(nineZone, new WidgetDef({ id: "t1" }), WidgetState.Open); - sut.widgets.w1.activeTabId.should.eq("t1"); + const sut = setWidgetState(nineZone, new WidgetDef({ id: "t1" }), WidgetState.Floating); + sut.should.eq(nineZone); }); - it("should add removed tab", () => { - let nineZone = createNineZoneState(); - nineZone = addPanelWidget(nineZone, "left", "w1", ["t1"]); - nineZone = addTab(nineZone, "t1"); - const sut = setWidgetState(nineZone, new WidgetDef({ id: "t2" }), WidgetState.Open); - sut.panels.left.widgets.length.should.eq(2); - }); + describe("WidgetState.Open", () => { + it("should open widget", () => { + let nineZone = createNineZoneState(); + nineZone = addPanelWidget(nineZone, "left", "w1", ["t1"]); + nineZone = addTab(nineZone, "t1"); + const sut = setWidgetState(nineZone, new WidgetDef({ id: "t1" }), WidgetState.Open); + sut.widgets.w1.activeTabId.should.eq("t1"); + }); - it("should add removed tab by existing widget", () => { - let nineZone = createNineZoneState(); - nineZone = addPanelWidget(nineZone, "left", "w1", ["t1"]); - nineZone = addPanelWidget(nineZone, "left", "w2", ["t2"]); - nineZone = addTab(nineZone, "t1"); - nineZone = addTab(nineZone, "t2"); - const widgetDef = new WidgetDef({ id: "t3" }); - widgetDef.tabLocation = { - ...widgetDef.tabLocation, - widgetId: "w2", - }; - const sut = setWidgetState(nineZone, widgetDef, WidgetState.Open); - sut.widgets.w2.tabs.should.eql(["t3", "t2"]); - }); + it("should add removed tab", () => { + let nineZone = createNineZoneState(); + nineZone = addPanelWidget(nineZone, "left", "w1", ["t1"]); + nineZone = addTab(nineZone, "t1"); + const sut = setWidgetState(nineZone, new WidgetDef({ id: "t2" }), WidgetState.Open); + sut.panels.left.widgets.length.should.eq(2); + }); - it("should add removed tab to existing panel widget", () => { - let nineZone = createNineZoneState(); - nineZone = addPanelWidget(nineZone, "left", "w1", ["t1"]); - nineZone = addPanelWidget(nineZone, "left", "w2", ["t2_1", "t2_2", "t2_3"]); - nineZone = addPanelWidget(nineZone, "left", "w3", ["t3"]); - nineZone = addTab(nineZone, "t1"); - nineZone = addTab(nineZone, "t2_1"); - nineZone = addTab(nineZone, "t2_2"); - nineZone = addTab(nineZone, "t2_3"); - nineZone = addTab(nineZone, "t3"); - const widgetDef = new WidgetDef({ id: "t4" }); - widgetDef.tabLocation = { - ...widgetDef.tabLocation, - widgetIndex: 1, - tabIndex: 2, - }; - const sut = setWidgetState(nineZone, widgetDef, WidgetState.Open); - sut.widgets.w2.tabs.should.eql(["t2_1", "t2_2", "t4", "t2_3"]); - }); - }); + it("should add removed tab by existing widget", () => { + let nineZone = createNineZoneState(); + nineZone = addPanelWidget(nineZone, "left", "w1", ["t1"]); + nineZone = addPanelWidget(nineZone, "left", "w2", ["t2"]); + nineZone = addTab(nineZone, "t1"); + nineZone = addTab(nineZone, "t2"); + const widgetDef = new WidgetDef({ id: "t3" }); + widgetDef.tabLocation = { + ...widgetDef.tabLocation, + widgetId: "w2", + }; + const sut = setWidgetState(nineZone, widgetDef, WidgetState.Open); + sut.widgets.w2.tabs.should.eql(["t3", "t2"]); + }); - describe("WidgetState.Closed", () => { - it("should not minimize if tab is not active", () => { - let nineZone = createNineZoneState(); - nineZone = addFloatingWidget(nineZone, "w1", ["t1", "t2"], undefined, { activeTabId: "t2" }); - nineZone = addTab(nineZone, "t1"); - nineZone = addTab(nineZone, "t2"); - const sut = setWidgetState(nineZone, new WidgetDef({ id: "t1" }), WidgetState.Closed); - sut.should.eq(nineZone); + it("should add removed tab to existing panel widget", () => { + let nineZone = createNineZoneState(); + nineZone = addPanelWidget(nineZone, "left", "w1", ["t1"]); + nineZone = addPanelWidget(nineZone, "left", "w2", ["t2_1", "t2_2", "t2_3"]); + nineZone = addPanelWidget(nineZone, "left", "w3", ["t3"]); + nineZone = addTab(nineZone, "t1"); + nineZone = addTab(nineZone, "t2_1"); + nineZone = addTab(nineZone, "t2_2"); + nineZone = addTab(nineZone, "t2_3"); + nineZone = addTab(nineZone, "t3"); + const widgetDef = new WidgetDef({ id: "t4" }); + widgetDef.tabLocation = { + ...widgetDef.tabLocation, + widgetIndex: 1, + tabIndex: 2, + }; + const sut = setWidgetState(nineZone, widgetDef, WidgetState.Open); + sut.widgets.w2.tabs.should.eql(["t2_1", "t2_2", "t4", "t2_3"]); + }); }); - it("should minimize floating widget", () => { - let nineZone = createNineZoneState(); - nineZone = addFloatingWidget(nineZone, "w1", ["t1"]); - nineZone = addTab(nineZone, "t1"); - const sut = setWidgetState(nineZone, new WidgetDef({ id: "t1" }), WidgetState.Closed); - sut.widgets.w1.minimized.should.true; - }); + describe("WidgetState.Closed", () => { + it("should not minimize if tab is not active", () => { + let nineZone = createNineZoneState(); + nineZone = addFloatingWidget(nineZone, "w1", ["t1", "t2"], undefined, { activeTabId: "t2" }); + nineZone = addTab(nineZone, "t1"); + nineZone = addTab(nineZone, "t2"); + const sut = setWidgetState(nineZone, new WidgetDef({ id: "t1" }), WidgetState.Closed); + sut.should.eq(nineZone); + }); - it("should minimize panel widget", () => { - let nineZone = createNineZoneState(); - nineZone = addPanelWidget(nineZone, "left", "w1", ["t1"]); - nineZone = addPanelWidget(nineZone, "left", "w2", ["t2"]); - nineZone = addTab(nineZone, "t1"); - const sut = setWidgetState(nineZone, new WidgetDef({ id: "t1" }), WidgetState.Closed); - sut.widgets.w1.minimized.should.true; - }); + it("should minimize floating widget", () => { + let nineZone = createNineZoneState(); + nineZone = addFloatingWidget(nineZone, "w1", ["t1"]); + nineZone = addTab(nineZone, "t1"); + const sut = setWidgetState(nineZone, new WidgetDef({ id: "t1" }), WidgetState.Closed); + sut.widgets.w1.minimized.should.true; + }); - it("should not minimize single panel widget", () => { - let nineZone = createNineZoneState(); - nineZone = addPanelWidget(nineZone, "left", "w1", ["t1"]); - nineZone = addTab(nineZone, "t1"); - const sut = setWidgetState(nineZone, new WidgetDef({ id: "t1" }), WidgetState.Closed); - sut.widgets.w1.minimized.should.false; + it("should minimize panel widget", () => { + let nineZone = createNineZoneState(); + nineZone = addPanelWidget(nineZone, "left", "w1", ["t1"]); + nineZone = addPanelWidget(nineZone, "left", "w2", ["t2"]); + nineZone = addTab(nineZone, "t1"); + const sut = setWidgetState(nineZone, new WidgetDef({ id: "t1" }), WidgetState.Closed); + sut.widgets.w1.minimized.should.true; + }); + + it("should not minimize single panel widget", () => { + let nineZone = createNineZoneState(); + nineZone = addPanelWidget(nineZone, "left", "w1", ["t1"]); + nineZone = addTab(nineZone, "t1"); + const sut = setWidgetState(nineZone, new WidgetDef({ id: "t1" }), WidgetState.Closed); + sut.widgets.w1.minimized.should.false; + }); + + it("should add removed tab", () => { + let nineZone = createNineZoneState(); + nineZone = addPanelWidget(nineZone, "left", "w1", ["t1"]); + nineZone = addTab(nineZone, "t1"); + const sut = setWidgetState(nineZone, new WidgetDef({ id: "t2" }), WidgetState.Closed); + sut.panels.left.widgets.length.should.eq(2); + }); }); - it("should add removed tab", () => { - let nineZone = createNineZoneState(); - nineZone = addPanelWidget(nineZone, "left", "w1", ["t1"]); - nineZone = addTab(nineZone, "t1"); - const sut = setWidgetState(nineZone, new WidgetDef({ id: "t2" }), WidgetState.Closed); - sut.panels.left.widgets.length.should.eq(2); + describe("WidgetState.Hidden", () => { + it("should not update if tab is not found", () => { + const nineZone = createNineZoneState(); + const sut = setWidgetState(nineZone, new WidgetDef({ id: "t1" }), WidgetState.Hidden); + sut.should.eq(nineZone); + }); + + it("should hide the widget", () => { + let nineZone = createNineZoneState(); + nineZone = addPanelWidget(nineZone, "left", "w1", ["t1", "t2"]); + nineZone = addTab(nineZone, "t1"); + const sut = setWidgetState(nineZone, new WidgetDef({ id: "t1" }), WidgetState.Hidden); + sut.widgets.w1.tabs.should.eql(["t2"]); + }); + + it("should use default panel side for a floating widget", () => { + let nineZone = createNineZoneState(); + nineZone = addFloatingWidget(nineZone, "w1", ["t1"]); + nineZone = addTab(nineZone, "t1"); + const widgetDef = new WidgetDef({ id: "t1" }); + setWidgetState(nineZone, widgetDef, WidgetState.Hidden); + widgetDef.tabLocation.side.should.eq("left"); + widgetDef.tabLocation.widgetIndex.should.eq(0); + }); }); + }); - describe("WidgetState.Hidden", () => { + describe("showWidget ", () => { it("should not update if tab is not found", () => { const nineZone = createNineZoneState(); - const sut = setWidgetState(nineZone, new WidgetDef({ id: "t1" }), WidgetState.Hidden); + const sut = showWidget(nineZone, "t1"); sut.should.eq(nineZone); }); - it("should hide the widget", () => { - let nineZone = createNineZoneState(); - nineZone = addPanelWidget(nineZone, "left", "w1", ["t1", "t2"]); - nineZone = addTab(nineZone, "t1"); - const sut = setWidgetState(nineZone, new WidgetDef({ id: "t1" }), WidgetState.Hidden); - sut.widgets.w1.tabs.should.eql(["t2"]); - }); - - it("should use default panel side for a floating widget", () => { + it("should bring floating widget to front", () => { let nineZone = createNineZoneState(); nineZone = addFloatingWidget(nineZone, "w1", ["t1"]); + nineZone = addFloatingWidget(nineZone, "w2", ["t2"]); nineZone = addTab(nineZone, "t1"); - const widgetDef = new WidgetDef({ id: "t1" }); - setWidgetState(nineZone, widgetDef, WidgetState.Hidden); - widgetDef.tabLocation.side.should.eq("left"); - widgetDef.tabLocation.widgetIndex.should.eq(0); + const sut = showWidget(nineZone, "t1"); + sut.floatingWidgets.allIds[0].should.eq("w2"); + sut.floatingWidgets.allIds[1].should.eq("w1"); }); }); -}); - -describe("showWidget ", () => { - it("should not update if tab is not found", () => { - const nineZone = createNineZoneState(); - const sut = showWidget(nineZone, "t1"); - sut.should.eq(nineZone); - }); + describe("expandWidget ", () => { + it("should not update if tab is not found", () => { + const nineZone = createNineZoneState(); + const sut = expandWidget(nineZone, "t1"); + sut.should.eq(nineZone); + }); - it("should bring floating widget to front", () => { - let nineZone = createNineZoneState(); - nineZone = addFloatingWidget(nineZone, "w1", ["t1"]); - nineZone = addFloatingWidget(nineZone, "w2", ["t2"]); - nineZone = addTab(nineZone, "t1"); - const sut = showWidget(nineZone, "t1"); - sut.floatingWidgets.allIds[0].should.eq("w2"); - sut.floatingWidgets.allIds[1].should.eq("w1"); + it("should expand floating widget", () => { + let nineZone = createNineZoneState(); + nineZone = addFloatingWidget(nineZone, "w1", ["t1"], undefined, { minimized: true }); + nineZone = addTab(nineZone, "t1"); + const sut = expandWidget(nineZone, "t1"); + sut.widgets.w1.minimized.should.false; + }); }); -}); -describe("expandWidget ", () => { - it("should not update if tab is not found", () => { - const nineZone = createNineZoneState(); - const sut = expandWidget(nineZone, "t1"); - sut.should.eq(nineZone); - }); + describe("restoreNineZoneState", () => { + it("should log error if widgetDef is not found", () => { + const spy = sinon.spy(Logger, "logError"); + const frontstageDef = new FrontstageDef(); + const savedState = { + ...createSavedNineZoneState(), + tabs: { + t1: createSavedTabState("t1"), + }, + }; + restoreNineZoneState(frontstageDef, savedState); + spy.calledOnce.should.true; + spy.firstCall.args[2]!().should.matchSnapshot(); + }); - it("should expand floating widget", () => { - let nineZone = createNineZoneState(); - nineZone = addFloatingWidget(nineZone, "w1", ["t1"], undefined, { minimized: true }); - nineZone = addTab(nineZone, "t1"); - const sut = expandWidget(nineZone, "t1"); - sut.widgets.w1.minimized.should.false; - }); -}); + it("should remove tab if widgetDef is not found", () => { + const frontstageDef = new FrontstageDef(); + sinon.stub(frontstageDef, "findWidgetDef").withArgs("t2").returns(new WidgetDef({})); + let state = createNineZoneState(); + state = addPanelWidget(state, "left", "w1", ["t1", "t2"]); + state = addTab(state, "t1"); + state = addTab(state, "t2"); + const savedState = { + ...createSavedNineZoneState(state), + tabs: { + t1: createSavedTabState("t1"), + t2: createSavedTabState("t2"), + }, + }; + const newState = restoreNineZoneState(frontstageDef, savedState); + (newState.tabs.t1 === undefined).should.true; + newState.widgets.w1.tabs.indexOf("t1").should.eq(-1); + newState.widgets.w1.tabs.indexOf("t2").should.eq(0); + }); -describe("restoreNineZoneState", () => { - it("should log error if widgetDef is not found", () => { - const spy = sinon.spy(Logger, "logError"); - const frontstageDef = new FrontstageDef(); - const savedState = { - ...createSavedNineZoneState(), - tabs: { - t1: createSavedTabState("t1"), - }, - }; - restoreNineZoneState(frontstageDef, savedState); - spy.calledOnce.should.true; - spy.firstCall.args[2]!().should.matchSnapshot(); - }); + it("should restore tabs", () => { + const frontstageDef = new FrontstageDef(); + const widgetDef = new WidgetDef({}); + sinon.stub(frontstageDef, "findWidgetDef").returns(widgetDef); + const savedState = { + ...createSavedNineZoneState(), + tabs: { + t1: createSavedTabState("t1"), + }, + }; + const sut = restoreNineZoneState(frontstageDef, savedState); + sut.should.matchSnapshot(); + }); - it("should remove tab if widgetDef is not found", () => { - const frontstageDef = new FrontstageDef(); - sinon.stub(frontstageDef, "findWidgetDef").withArgs("t2").returns(new WidgetDef({})); - let state = createNineZoneState(); - state = addPanelWidget(state, "left", "w1", ["t1", "t2"]); - state = addTab(state, "t1"); - state = addTab(state, "t2"); - const savedState = { - ...createSavedNineZoneState(state), - tabs: { - t1: createSavedTabState("t1"), - t2: createSavedTabState("t2"), - }, - }; - const newState = restoreNineZoneState(frontstageDef, savedState); - (newState.tabs.t1 === undefined).should.true; - newState.widgets.w1.tabs.indexOf("t1").should.eq(-1); - newState.widgets.w1.tabs.indexOf("t2").should.eq(0); - }); + it("should RESIZE", () => { + sinon.stub(FrontstageManager, "nineZoneSize").get(() => new Size(10, 20)); + const frontstageDef = new FrontstageDef(); + const savedState = { + ...createSavedNineZoneState({ + size: { + width: 1, + height: 2, + }, + }), + tabs: { + t1: createSavedTabState("t1"), + }, + }; - it("should restore tabs", () => { - const frontstageDef = new FrontstageDef(); - const widgetDef = new WidgetDef({}); - sinon.stub(frontstageDef, "findWidgetDef").returns(widgetDef); - const savedState = { - ...createSavedNineZoneState(), - tabs: { - t1: createSavedTabState("t1"), - }, - }; - const sut = restoreNineZoneState(frontstageDef, savedState); - sut.should.matchSnapshot(); - }); + const sut = restoreNineZoneState(frontstageDef, savedState); + sut.size.should.eql({ width: 10, height: 20 }); + }); - it("should RESIZE", () => { - sinon.stub(FrontstageManager, "nineZoneSize").get(() => new Size(10, 20)); - const frontstageDef = new FrontstageDef(); - const savedState = { - ...createSavedNineZoneState({ - size: { - width: 1, - height: 2, + it("should not RESIZE", () => { + const frontstageDef = new FrontstageDef(); + const savedState = { + ...createSavedNineZoneState({ + size: { + width: 1, + height: 2, + }, + }), + tabs: { + t1: createSavedTabState("t1"), }, - }), - tabs: { - t1: createSavedTabState("t1"), - }, - }; - - const sut = restoreNineZoneState(frontstageDef, savedState); - sut.size.should.eql({ width: 10, height: 20 }); - }); + }; - it("should not RESIZE", () => { - const frontstageDef = new FrontstageDef(); - const savedState = { - ...createSavedNineZoneState({ - size: { - width: 1, - height: 2, - }, - }), - tabs: { - t1: createSavedTabState("t1"), - }, - }; - - const sut = restoreNineZoneState(frontstageDef, savedState); - sut.size.should.eql({ width: 1, height: 2 }); + const sut = restoreNineZoneState(frontstageDef, savedState); + sut.size.should.eql({ width: 1, height: 2 }); + }); }); -}); -describe("packNineZoneState", () => { - it("should remove labels", () => { - let nineZone = createNineZoneState(); - nineZone = addFloatingWidget(nineZone, "w1", ["t1"]); - nineZone = addTab(nineZone, "t1"); - const sut = packNineZoneState(nineZone); - sut.should.matchSnapshot(); + describe("packNineZoneState", () => { + it("should remove labels", () => { + let nineZone = createNineZoneState(); + nineZone = addFloatingWidget(nineZone, "w1", ["t1"]); + nineZone = addTab(nineZone, "t1"); + const sut = packNineZoneState(nineZone); + sut.should.matchSnapshot(); + }); }); -}); - -describe("useUpdateNineZoneSize", () => { - it("should update size of nine zone state when new frontstage is activated", () => { - const { rerender } = renderHook((props) => useUpdateNineZoneSize(props), { initialProps: new FrontstageDef() }); - const newFrontstageDef = new FrontstageDef(); - newFrontstageDef.nineZoneState = createNineZoneState(); + describe("useUpdateNineZoneSize", () => { + it("should update size of nine zone state when new frontstage is activated", () => { + const { rerender } = renderHook((props) => useUpdateNineZoneSize(props), { initialProps: new FrontstageDef() }); - sinon.stub(FrontstageManager, "nineZoneSize").get(() => new Size(10, 20)); - rerender(newFrontstageDef); + const newFrontstageDef = new FrontstageDef(); + newFrontstageDef.nineZoneState = createNineZoneState(); - newFrontstageDef.nineZoneState.size.should.eql({ width: 10, height: 20 }); - }); + sinon.stub(FrontstageManager, "nineZoneSize").get(() => new Size(10, 20)); + rerender(newFrontstageDef); - it("should not update size if FrontstageManager.nineZoneSize is not initialized", () => { - const { rerender } = renderHook((props) => useUpdateNineZoneSize(props), { initialProps: new FrontstageDef() }); + newFrontstageDef.nineZoneState.size.should.eql({ width: 10, height: 20 }); + }); - const newFrontstageDef = new FrontstageDef(); - newFrontstageDef.nineZoneState = createNineZoneState({ size: { height: 1, width: 2 } }); + it("should not update size if FrontstageManager.nineZoneSize is not initialized", () => { + const { rerender } = renderHook((props) => useUpdateNineZoneSize(props), { initialProps: new FrontstageDef() }); - rerender(newFrontstageDef); + const newFrontstageDef = new FrontstageDef(); + newFrontstageDef.nineZoneState = createNineZoneState({ size: { height: 1, width: 2 } }); - newFrontstageDef.nineZoneState.size.should.eql({ height: 1, width: 2 }); - }); -}); + rerender(newFrontstageDef); -describe("addMissingWidgets", () => { - it("should add centerLeft widgets", () => { - const state = createNineZoneState(); - const frontstageDef = new FrontstageDef(); - const zoneDef = new ZoneDef(); - const widgetDef = new WidgetDef({ id: "w1" }); - sinon.stub(zoneDef, "widgetDefs").get(() => [widgetDef]); - sinon.stub(frontstageDef, "centerLeft").get(() => zoneDef); - const newState = addMissingWidgets(frontstageDef, state); - should().exist(newState.tabs.w1); + newFrontstageDef.nineZoneState.size.should.eql({ height: 1, width: 2 }); + }); }); - it("should add bottomLeft widgets", () => { - const state = createNineZoneState(); - const frontstageDef = new FrontstageDef(); - const zoneDef = new ZoneDef(); - const widgetDef = new WidgetDef({ id: "w1" }); - sinon.stub(zoneDef, "widgetDefs").get(() => [widgetDef]); - sinon.stub(frontstageDef, "bottomLeft").get(() => zoneDef); - const newState = addMissingWidgets(frontstageDef, state); - should().exist(newState.tabs.w1); - }); + describe("addMissingWidgets", () => { + it("should add centerLeft widgets", () => { + const state = createNineZoneState(); + const frontstageDef = new FrontstageDef(); + const zoneDef = new ZoneDef(); + const widgetDef = new WidgetDef({ id: "w1" }); + sinon.stub(zoneDef, "widgetDefs").get(() => [widgetDef]); + sinon.stub(frontstageDef, "centerLeft").get(() => zoneDef); + const newState = addMissingWidgets(frontstageDef, state); + should().exist(newState.tabs.w1); + }); - it("should add centerRight widgets", () => { - const state = createNineZoneState(); - const frontstageDef = new FrontstageDef(); - const zoneDef = new ZoneDef(); - const widgetDef = new WidgetDef({ id: "w1" }); - sinon.stub(zoneDef, "widgetDefs").get(() => [widgetDef]); - sinon.stub(frontstageDef, "centerRight").get(() => zoneDef); - const newState = addMissingWidgets(frontstageDef, state); - should().exist(newState.tabs.w1); - }); + it("should add bottomLeft widgets", () => { + const state = createNineZoneState(); + const frontstageDef = new FrontstageDef(); + const zoneDef = new ZoneDef(); + const widgetDef = new WidgetDef({ id: "w1" }); + sinon.stub(zoneDef, "widgetDefs").get(() => [widgetDef]); + sinon.stub(frontstageDef, "bottomLeft").get(() => zoneDef); + const newState = addMissingWidgets(frontstageDef, state); + should().exist(newState.tabs.w1); + }); - it("should add bottomRight widgets", () => { - const state = createNineZoneState(); - const frontstageDef = new FrontstageDef(); - const zoneDef = new ZoneDef(); - const widgetDef = new WidgetDef({ id: "w1" }); - sinon.stub(zoneDef, "widgetDefs").get(() => [widgetDef]); - sinon.stub(frontstageDef, "bottomRight").get(() => zoneDef); - const newState = addMissingWidgets(frontstageDef, state); - should().exist(newState.tabs.w1); - }); + it("should add centerRight widgets", () => { + const state = createNineZoneState(); + const frontstageDef = new FrontstageDef(); + const zoneDef = new ZoneDef(); + const widgetDef = new WidgetDef({ id: "w1" }); + sinon.stub(zoneDef, "widgetDefs").get(() => [widgetDef]); + sinon.stub(frontstageDef, "centerRight").get(() => zoneDef); + const newState = addMissingWidgets(frontstageDef, state); + should().exist(newState.tabs.w1); + }); - it("should add leftPanel widgets", () => { - let state = createNineZoneState(); - state = addPanelWidget(state, "left", "start", ["start1"]); - state = addPanelWidget(state, "left", "middle", ["middle1"]); - state = addPanelWidget(state, "left", "end", ["end1"]); - state = addTab(state, "start1"); - state = addTab(state, "middle1"); - state = addTab(state, "end1"); - const frontstageDef = new FrontstageDef(); - const panelDef = new StagePanelDef(); - panelDef.initializeFromProps({ - resizable: true, - widgets: [ - , - ], - panelZones: { - start: { - widgets: [ - , - ], - }, - middle: { - widgets: [ - , - ], - }, - end: { - widgets: [ - , - ], - }, - }, - }, StagePanelLocation.Left); - sinon.stub(frontstageDef, "leftPanel").get(() => panelDef); - const newState = addMissingWidgets(frontstageDef, state); - newState.widgets.start.tabs.should.eql(["start1", "ws1"]); - newState.widgets.middle.tabs.should.eql(["middle1", "wm1"]); - newState.widgets.end.tabs.should.eql(["end1", "w1", "we1"]); - }); + it("should add bottomRight widgets", () => { + const state = createNineZoneState(); + const frontstageDef = new FrontstageDef(); + const zoneDef = new ZoneDef(); + const widgetDef = new WidgetDef({ id: "w1" }); + sinon.stub(zoneDef, "widgetDefs").get(() => [widgetDef]); + sinon.stub(frontstageDef, "bottomRight").get(() => zoneDef); + const newState = addMissingWidgets(frontstageDef, state); + should().exist(newState.tabs.w1); + }); - it("should add rightPanel widgets", () => { - let state = createNineZoneState(); - state = addPanelWidget(state, "right", "start", ["start1"]); - state = addPanelWidget(state, "right", "middle", ["middle1"]); - state = addPanelWidget(state, "right", "end", ["end1"]); - state = addTab(state, "start1"); - state = addTab(state, "middle1"); - state = addTab(state, "end1"); - const frontstageDef = new FrontstageDef(); - const panelDef = new StagePanelDef(); - panelDef.initializeFromProps({ - resizable: true, - widgets: [ - , - ], - panelZones: { - start: { - widgets: [ - , - ], - }, - middle: { - widgets: [ - , - ], - }, - end: { - widgets: [ - , - ], + it("should add leftPanel widgets", () => { + let state = createNineZoneState(); + state = addPanelWidget(state, "left", "start", ["start1"]); + state = addPanelWidget(state, "left", "middle", ["middle1"]); + state = addPanelWidget(state, "left", "end", ["end1"]); + state = addTab(state, "start1"); + state = addTab(state, "middle1"); + state = addTab(state, "end1"); + const frontstageDef = new FrontstageDef(); + const panelDef = new StagePanelDef(); + panelDef.initializeFromProps({ + resizable: true, + widgets: [ + , + ], + panelZones: { + start: { + widgets: [ + , + ], + }, + middle: { + widgets: [ + , + ], + }, + end: { + widgets: [ + , + ], + }, }, - }, - }, StagePanelLocation.Right); - sinon.stub(frontstageDef, "rightPanel").get(() => panelDef); - const newState = addMissingWidgets(frontstageDef, state); - newState.widgets.start.tabs.should.eql(["start1", "ws1"]); - newState.widgets.middle.tabs.should.eql(["middle1", "wm1"]); - newState.widgets.end.tabs.should.eql(["end1", "w1", "we1"]); - }); + }, StagePanelLocation.Left); + sinon.stub(frontstageDef, "leftPanel").get(() => panelDef); + const newState = addMissingWidgets(frontstageDef, state); + newState.widgets.start.tabs.should.eql(["start1", "ws1"]); + newState.widgets.middle.tabs.should.eql(["middle1", "wm1"]); + newState.widgets.end.tabs.should.eql(["end1", "w1", "we1"]); + }); - it("should add topPanel widgets", () => { - let state = createNineZoneState(); - state = addPanelWidget(state, "top", "start", ["start1"]); - state = addPanelWidget(state, "top", "end", ["end1"]); - state = addTab(state, "start1"); - state = addTab(state, "end1"); - const frontstageDef = new FrontstageDef(); - const panelDef = new StagePanelDef(); - panelDef.initializeFromProps({ - resizable: true, - widgets: [ - , - ], - panelZones: { - start: { - widgets: [ - , - ], - }, - end: { - widgets: [ - , - ], + it("should add rightPanel widgets", () => { + let state = createNineZoneState(); + state = addPanelWidget(state, "right", "start", ["start1"]); + state = addPanelWidget(state, "right", "middle", ["middle1"]); + state = addPanelWidget(state, "right", "end", ["end1"]); + state = addTab(state, "start1"); + state = addTab(state, "middle1"); + state = addTab(state, "end1"); + const frontstageDef = new FrontstageDef(); + const panelDef = new StagePanelDef(); + panelDef.initializeFromProps({ + resizable: true, + widgets: [ + , + ], + panelZones: { + start: { + widgets: [ + , + ], + }, + middle: { + widgets: [ + , + ], + }, + end: { + widgets: [ + , + ], + }, }, - }, - }, StagePanelLocation.Top); - const panelDef1 = new StagePanelDef(); - panelDef1.initializeFromProps({ - resizable: true, - widgets: [ - , - ], - }, StagePanelLocation.TopMost); - sinon.stub(frontstageDef, "topPanel").get(() => panelDef); - sinon.stub(frontstageDef, "topMostPanel").get(() => panelDef1); - const newState = addMissingWidgets(frontstageDef, state); - newState.widgets.start.tabs.should.eql(["start1", "w1", "ws1"]); - newState.widgets.end.tabs.should.eql(["end1", "w2", "we1"]); - }); + }, StagePanelLocation.Right); + sinon.stub(frontstageDef, "rightPanel").get(() => panelDef); + const newState = addMissingWidgets(frontstageDef, state); + newState.widgets.start.tabs.should.eql(["start1", "ws1"]); + newState.widgets.middle.tabs.should.eql(["middle1", "wm1"]); + newState.widgets.end.tabs.should.eql(["end1", "w1", "we1"]); + }); - it("should add bottomPanel widgets", () => { - let state = createNineZoneState(); - state = addPanelWidget(state, "bottom", "start", ["start1"]); - state = addPanelWidget(state, "bottom", "end", ["end1"]); - state = addTab(state, "start1"); - state = addTab(state, "end1"); - const frontstageDef = new FrontstageDef(); - const panelDef = new StagePanelDef(); - panelDef.initializeFromProps({ - resizable: true, - widgets: [ - , - ], - panelZones: { - start: { - widgets: [ - , - ], + it("should add topPanel widgets", () => { + let state = createNineZoneState(); + state = addPanelWidget(state, "top", "start", ["start1"]); + state = addPanelWidget(state, "top", "end", ["end1"]); + state = addTab(state, "start1"); + state = addTab(state, "end1"); + const frontstageDef = new FrontstageDef(); + const panelDef = new StagePanelDef(); + panelDef.initializeFromProps({ + resizable: true, + widgets: [ + , + ], + panelZones: { + start: { + widgets: [ + , + ], + }, + end: { + widgets: [ + , + ], + }, }, - end: { - widgets: [ - , - ], + }, StagePanelLocation.Top); + const panelDef1 = new StagePanelDef(); + panelDef1.initializeFromProps({ + resizable: true, + widgets: [ + , + ], + }, StagePanelLocation.TopMost); + sinon.stub(frontstageDef, "topPanel").get(() => panelDef); + sinon.stub(frontstageDef, "topMostPanel").get(() => panelDef1); + const newState = addMissingWidgets(frontstageDef, state); + newState.widgets.start.tabs.should.eql(["start1", "w1", "ws1"]); + newState.widgets.end.tabs.should.eql(["end1", "w2", "we1"]); + }); + + it("should add bottomPanel widgets", () => { + let state = createNineZoneState(); + state = addPanelWidget(state, "bottom", "start", ["start1"]); + state = addPanelWidget(state, "bottom", "end", ["end1"]); + state = addTab(state, "start1"); + state = addTab(state, "end1"); + const frontstageDef = new FrontstageDef(); + const panelDef = new StagePanelDef(); + panelDef.initializeFromProps({ + resizable: true, + widgets: [ + , + ], + panelZones: { + start: { + widgets: [ + , + ], + }, + end: { + widgets: [ + , + ], + }, }, - }, - }, StagePanelLocation.Bottom); - const panelDef1 = new StagePanelDef(); - panelDef1.initializeFromProps({ - resizable: true, - widgets: [ - , - ], - }, StagePanelLocation.BottomMost); - sinon.stub(frontstageDef, "bottomPanel").get(() => panelDef); - sinon.stub(frontstageDef, "bottomMostPanel").get(() => panelDef1); - const newState = addMissingWidgets(frontstageDef, state); - newState.widgets.start.tabs.should.eql(["start1", "w1", "ws1"]); - newState.widgets.end.tabs.should.eql(["end1", "w2", "we1"]); - }); -}); - -describe("dynamic widgets", () => { - const localStorageToRestore = Object.getOwnPropertyDescriptor(window, "localStorage")!; - const localStorageMock = storageMock(); - - stubRaf(); - beforeEach(async () => { - Object.defineProperty(window, "localStorage", { - get: () => localStorageMock, + }, StagePanelLocation.Bottom); + const panelDef1 = new StagePanelDef(); + panelDef1.initializeFromProps({ + resizable: true, + widgets: [ + , + ], + }, StagePanelLocation.BottomMost); + sinon.stub(frontstageDef, "bottomPanel").get(() => panelDef); + sinon.stub(frontstageDef, "bottomMostPanel").get(() => panelDef1); + const newState = addMissingWidgets(frontstageDef, state); + newState.widgets.start.tabs.should.eql(["start1", "w1", "ws1"]); + newState.widgets.end.tabs.should.eql(["end1", "w2", "we1"]); + }); + }); + + describe("dynamic widgets", () => { + stubRaf(); + beforeEach(async () => { + await TestUtils.initializeUiFramework(); + await NoRenderApp.startup(); + }); + + afterEach(() => { + UiItemsManager.unregister("TestUi2Provider"); + FrontstageManager.clearFrontstageDefs(); + FrontstageManager.setActiveFrontstageDef(undefined); }); - await TestUtils.initializeUiFramework(); - await NoRenderApp.startup(); - }); - - afterEach(() => { - UiItemsManager.unregister("TestUi2Provider"); - FrontstageManager.clearFrontstageDefs(); - FrontstageManager.setActiveFrontstageDef(undefined); - }); + afterEach(() => { + TestUtils.terminateUiFramework(); + IModelApp.shutdown(); + }); - afterEach(() => { - Object.defineProperty(window, "localStorage", localStorageToRestore); - TestUtils.terminateUiFramework(); - IModelApp.shutdown(); - }); + it("should render pre-loaded extension widgets when state is initialized", async () => { + UiItemsManager.register(new TestUi2Provider()); - it("should render pre-loaded extension widgets when state is initialized", async () => { - UiItemsManager.register(new TestUi2Provider()); + const frontstageProvider = new TestFrontstageUi2(); + FrontstageManager.addFrontstageProvider(frontstageProvider); + await FrontstageManager.setActiveFrontstageDef(frontstageProvider.frontstageDef); + const { findByText } = render(); + await findByText("Left Start 1"); + await findByText("TestUi2Provider RM1"); + await findByText("TestUi2Provider W1"); + }); - const frontstageProvider = new TestFrontstageUi2(); - FrontstageManager.addFrontstageProvider(frontstageProvider); - await FrontstageManager.setActiveFrontstageDef(frontstageProvider.frontstageDef); - const { findByText } = render(); - await findByText("Left Start 1"); - await findByText("TestUi2Provider RM1"); - await findByText("TestUi2Provider W1"); - }); + it("should render pre-loaded extension widgets when state is restored", async () => { + UiItemsManager.register(new TestUi2Provider()); - it("should render pre-loaded extension widgets when state is restored", async () => { - UiItemsManager.register(new TestUi2Provider()); + const spy = sinon.spy(localStorageMock, "getItem"); + let state = createNineZoneState(); + state = addPanelWidget(state, "left", "leftStart", ["LeftStart1"]); + state = addTab(state, "LeftStart1"); + const setting = createFrontstageState(state); - const spy = sinon.spy(localStorageMock, "getItem"); - let state = createNineZoneState(); - state = addPanelWidget(state, "left", "leftStart", ["LeftStart1"]); - state = addTab(state, "LeftStart1"); - const setting = createFrontstageState(state); + const uiSettings = new UiSettingsStub(); + sinon.stub(uiSettings, "getSetting").resolves({ + status: UiSettingsStatus.Success, + setting, + }); - const uiSettings = new UiSettingsStub(); - sinon.stub(uiSettings, "getSetting").resolves({ - status: UiSettingsStatus.Success, - setting, - }); + const frontstageProvider = new TestFrontstageUi2(); + FrontstageManager.addFrontstageProvider(frontstageProvider); + await FrontstageManager.setActiveFrontstageDef(frontstageProvider.frontstageDef); + const { findByText } = render(, { + wrapper: (props) => , + }); + await findByText("Left Start 1"); + await findByText("TestUi2Provider RM1"); + await findByText("TestUi2Provider W1"); - const frontstageProvider = new TestFrontstageUi2(); - FrontstageManager.addFrontstageProvider(frontstageProvider); - await FrontstageManager.setActiveFrontstageDef(frontstageProvider.frontstageDef); - const { findByText } = render(, { - wrapper: (props) => , + sinon.assert.notCalled(spy); }); - await findByText("Left Start 1"); - await findByText("TestUi2Provider RM1"); - await findByText("TestUi2Provider W1"); - - sinon.assert.notCalled(spy); - }); - it("should render loaded extension widgets", async () => { - const frontstageProvider = new TestFrontstageUi2(); - FrontstageManager.addFrontstageProvider(frontstageProvider); - await FrontstageManager.setActiveFrontstageDef(frontstageProvider.frontstageDef); - const { findByText } = render(); - await findByText("Left Start 1"); + it("should render loaded extension widgets", async () => { + const frontstageProvider = new TestFrontstageUi2(); + FrontstageManager.addFrontstageProvider(frontstageProvider); + await FrontstageManager.setActiveFrontstageDef(frontstageProvider.frontstageDef); + const { findByText } = render(); + await findByText("Left Start 1"); - act(() => { - UiItemsManager.register(new TestUi2Provider()); + act(() => { + UiItemsManager.register(new TestUi2Provider()); + }); + await findByText("TestUi2Provider RM1"); + await findByText("TestUi2Provider W1"); }); - await findByText("TestUi2Provider RM1"); - await findByText("TestUi2Provider W1"); - }); - it("should stop rendering unloaded extension widgets", async () => { - const frontstageProvider = new TestFrontstageUi2(); - FrontstageManager.addFrontstageProvider(frontstageProvider); - await FrontstageManager.setActiveFrontstageDef(frontstageProvider.frontstageDef); - const frontstageDef = FrontstageManager.activeFrontstageDef!; - render(); + it("should stop rendering unloaded extension widgets", async () => { + const frontstageProvider = new TestFrontstageUi2(); + FrontstageManager.addFrontstageProvider(frontstageProvider); + await FrontstageManager.setActiveFrontstageDef(frontstageProvider.frontstageDef); + const frontstageDef = FrontstageManager.activeFrontstageDef!; + render(); - act(() => { - UiItemsManager.register(new TestUi2Provider()); - }); + act(() => { + UiItemsManager.register(new TestUi2Provider()); + }); - await TestUtils.flushAsyncOperations(); - should().exist(frontstageDef.nineZoneState!.tabs.LeftStart1, "LeftStart1"); - should().exist(frontstageDef.nineZoneState!.tabs.TestUi2ProviderRM1, "TestUi2ProviderRM1"); - should().exist(frontstageDef.nineZoneState!.tabs.TestUi2ProviderW1, "TestUi2ProviderW1"); - frontstageDef.nineZoneState!.widgets.rightMiddle.tabs.should.eql(["TestUi2ProviderRM1"], "rigthMiddle widget tabs"); - frontstageDef.nineZoneState!.widgets.leftStart.tabs.should.eql(["LeftStart1", "TestUi2ProviderW1"], "leftStart widget tabs"); + await TestUtils.flushAsyncOperations(); + should().exist(frontstageDef.nineZoneState!.tabs.LeftStart1, "LeftStart1"); + should().exist(frontstageDef.nineZoneState!.tabs.TestUi2ProviderRM1, "TestUi2ProviderRM1"); + should().exist(frontstageDef.nineZoneState!.tabs.TestUi2ProviderW1, "TestUi2ProviderW1"); + frontstageDef.nineZoneState!.widgets.rightMiddle.tabs.should.eql(["TestUi2ProviderRM1"], "rigthMiddle widget tabs"); + frontstageDef.nineZoneState!.widgets.leftStart.tabs.should.eql(["LeftStart1", "TestUi2ProviderW1"], "leftStart widget tabs"); - act(() => { - UiItemsManager.unregister("TestUi2Provider"); - }); + act(() => { + UiItemsManager.unregister("TestUi2Provider"); + }); - await TestUtils.flushAsyncOperations(); - should().exist(frontstageDef.nineZoneState!.tabs.LeftStart1, "LeftStart1 after unregister"); - should().not.exist(frontstageDef.nineZoneState!.tabs.TestUi2ProviderRM1, "TestUi2ProviderRM1 after unregister"); - should().not.exist(frontstageDef.nineZoneState!.tabs.TestUi2ProviderW1, "TestUi2ProviderW1 after unregister"); - should().not.exist(frontstageDef.nineZoneState!.widgets.rightMiddle, "rigthMiddle widget"); - frontstageDef.nineZoneState!.widgets.leftStart.tabs.should.eql(["LeftStart1"], "leftStart widget tabs"); - }); + await TestUtils.flushAsyncOperations(); + should().exist(frontstageDef.nineZoneState!.tabs.LeftStart1, "LeftStart1 after unregister"); + should().not.exist(frontstageDef.nineZoneState!.tabs.TestUi2ProviderRM1, "TestUi2ProviderRM1 after unregister"); + should().not.exist(frontstageDef.nineZoneState!.tabs.TestUi2ProviderW1, "TestUi2ProviderW1 after unregister"); + should().not.exist(frontstageDef.nineZoneState!.widgets.rightMiddle, "rigthMiddle widget"); + frontstageDef.nineZoneState!.widgets.leftStart.tabs.should.eql(["LeftStart1"], "leftStart widget tabs"); + }); - it("should render from 1.0 definition", async () => { - const frontstageProvider = new TestFrontstageUi1(); - FrontstageManager.addFrontstageProvider(frontstageProvider); - await FrontstageManager.setActiveFrontstageDef(frontstageProvider.frontstageDef); - const frontstageDef = FrontstageManager.activeFrontstageDef!; - render(); + it("should render from 1.0 definition", async () => { + const frontstageProvider = new TestFrontstageUi1(); + FrontstageManager.addFrontstageProvider(frontstageProvider); + await FrontstageManager.setActiveFrontstageDef(frontstageProvider.frontstageDef); + const frontstageDef = FrontstageManager.activeFrontstageDef!; + render(); - await TestUtils.flushAsyncOperations(); - const state = frontstageDef.nineZoneState!; + await TestUtils.flushAsyncOperations(); + const state = frontstageDef.nineZoneState!; - state.panels.left.widgets.should.eql(["leftStart", "leftMiddle", "leftEnd"]); - state.panels.right.widgets.should.eql(["rightStart", "rightMiddle", "rightEnd"]); - state.panels.top.widgets.should.eql(["topStart", "topEnd"]); - state.panels.bottom.widgets.should.eql(["bottomStart", "bottomEnd"]); + state.panels.left.widgets.should.eql(["leftStart", "leftMiddle", "leftEnd"]); + state.panels.right.widgets.should.eql(["rightStart", "rightMiddle", "rightEnd"]); + state.panels.top.widgets.should.eql(["topStart", "topEnd"]); + state.panels.bottom.widgets.should.eql(["bottomStart", "bottomEnd"]); - state.widgets.leftStart.tabs.should.eql(["CenterLeft1", "LeftStart1"]); - state.widgets.leftMiddle.tabs.should.eql(["BottomLeft1", "LeftMiddle1"]); - state.widgets.leftEnd.tabs.should.eql(["Left1", "LeftEnd1"]); + state.widgets.leftStart.tabs.should.eql(["CenterLeft1", "LeftStart1"]); + state.widgets.leftMiddle.tabs.should.eql(["BottomLeft1", "LeftMiddle1"]); + state.widgets.leftEnd.tabs.should.eql(["Left1", "LeftEnd1"]); - state.widgets.rightStart.tabs.should.eql(["CenterRight1", "RightStart1"]); - state.widgets.rightMiddle.tabs.should.eql(["BottomRight1", "RightMiddle1"]); - state.widgets.rightEnd.tabs.should.eql(["Right1", "RightEnd1"]); + state.widgets.rightStart.tabs.should.eql(["CenterRight1", "RightStart1"]); + state.widgets.rightMiddle.tabs.should.eql(["BottomRight1", "RightMiddle1"]); + state.widgets.rightEnd.tabs.should.eql(["Right1", "RightEnd1"]); - state.widgets.topStart.tabs.should.eql(["Top1", "TopStart1"]); - state.widgets.topEnd.tabs.should.eql(["TopMost1", "TopEnd1"]); + state.widgets.topStart.tabs.should.eql(["Top1", "TopStart1"]); + state.widgets.topEnd.tabs.should.eql(["TopMost1", "TopEnd1"]); - state.widgets.bottomStart.tabs.should.eql(["Bottom1", "BottomStart1"]); - state.widgets.bottomEnd.tabs.should.eql(["BottomMost1", "BottomEnd1"]); + state.widgets.bottomStart.tabs.should.eql(["Bottom1", "BottomStart1"]); + state.widgets.bottomEnd.tabs.should.eql(["BottomMost1", "BottomEnd1"]); + }); }); }); diff --git a/ui/framework/src/test/widgets/NavigationWidget.test.snap b/ui/framework/src/test/widgets/NavigationWidget.test.snap index b8343cf8793..8f3740adf69 100644 --- a/ui/framework/src/test/widgets/NavigationWidget.test.snap +++ b/ui/framework/src/test/widgets/NavigationWidget.test.snap @@ -83,3 +83,87 @@ exports[`NavigationWidget NavigationWidget should render correctly 1`] = ` } /> `; + +exports[`NavigationWidget localStorage Wrapper NavigationWidget NavigationWidget should render correctly 1`] = ` + + + + + } + panelAlignment={0} + /> + } + navigationWidgetDef={ + NavigationWidgetDef { + "_classId": undefined, + "_defaultState": 4, + "_fillZone": false, + "_handleSyncUiEvent": [Function], + "_id": "navigationWidget", + "_isFloatingStateSupported": false, + "_isFloatingStateWindowResizable": true, + "_isFreeform": false, + "_isStatusBar": false, + "_isToolSettings": false, + "_label": "", + "_navigationAidId": "", + "_onWidgetStateChanged": undefined, + "_preferredPanelSize": undefined, + "_priority": 0, + "_restoreTransientState": undefined, + "_saveTransientState": undefined, + "_state": 4, + "_stateChanged": false, + "_syncEventIds": Array [], + "_tabLocation": Object { + "side": "left", + "tabIndex": 0, + "widgetId": "", + "widgetIndex": 0, + }, + "_toolbarBaseName": "[]NavigationWidget", + "_tooltip": "", + "_widgetType": 1, + "horizontalDirection": 4, + "horizontalItems": undefined, + "horizontalPanelAlignment": 1, + "verticalDirection": 1, + "verticalItems": undefined, + "verticalPanelAlignment": 0, + } + } + verticalToolbar={ + + + + + } + panelAlignment={0} + /> + } +/> +`; diff --git a/ui/framework/src/test/widgets/NavigationWidget.test.tsx b/ui/framework/src/test/widgets/NavigationWidget.test.tsx index 6909fdc4522..5092506cc54 100644 --- a/ui/framework/src/test/widgets/NavigationWidget.test.tsx +++ b/ui/framework/src/test/widgets/NavigationWidget.test.tsx @@ -17,46 +17,61 @@ import { ConfigurableUiManager } from "../../ui-framework/configurableui/Configu import { CoreTools } from "../../ui-framework/tools/CoreToolDefinitions"; import { FrameworkVersion } from "../../ui-framework/hooks/useFrameworkVersion"; import { NavigationAidControl } from "../../ui-framework/navigationaids/NavigationAidControl"; -import TestUtils from "../TestUtils"; +import TestUtils, { storageMock } from "../TestUtils"; import { UiShowHideManager } from "../../ui-framework/utils/UiShowHideManager"; -describe("NavigationWidget", () => { +describe("NavigationWidget localStorage Wrapper", () => { + + const localStorageToRestore = Object.getOwnPropertyDescriptor(window, "localStorage")!; + const localStorageMock = storageMock(); before(async () => { - await TestUtils.initializeUiFramework(); + Object.defineProperty(window, "localStorage", { + get: () => localStorageMock, + }); }); after(() => { - TestUtils.terminateUiFramework(); + Object.defineProperty(window, "localStorage", localStorageToRestore); }); - const widgetProps: AnyWidgetProps = { - id: "navigationWidget", - classId: "NavigationWidget", - defaultState: WidgetState.Open, - isFreeform: true, - iconSpec: "icon-home", - labelKey: "SampleApp:Test.my-label", - navigationAidId: "StandardRotationNavigationAid", - horizontalDirection: Direction.Top, - verticalDirection: Direction.Left, - }; + describe("NavigationWidget", () => { - it("NavigationWidgetDef from WidgetProps", () => { + before(async () => { + await TestUtils.initializeUiFramework(); + }); - const widgetDef = new NavigationWidgetDef(widgetProps); // eslint-disable-line deprecation/deprecation - expect(widgetDef).to.be.instanceof(NavigationWidgetDef); // eslint-disable-line deprecation/deprecation + after(() => { + TestUtils.terminateUiFramework(); + }); - const navigationWidgetDef = widgetDef; + const widgetProps: AnyWidgetProps = { + id: "navigationWidget", + classId: "NavigationWidget", + defaultState: WidgetState.Open, + isFreeform: true, + iconSpec: "icon-home", + labelKey: "SampleApp:Test.my-label", + navigationAidId: "StandardRotationNavigationAid", + horizontalDirection: Direction.Top, + verticalDirection: Direction.Left, + }; - const reactNode = navigationWidgetDef.reactNode; - expect(reactNode).to.not.be.undefined; + it("NavigationWidgetDef from WidgetProps", () => { - const cornerNode = navigationWidgetDef.renderCornerItem(); - expect(cornerNode).to.not.be.undefined; - }); + const widgetDef = new NavigationWidgetDef(widgetProps); // eslint-disable-line deprecation/deprecation + expect(widgetDef).to.be.instanceof(NavigationWidgetDef); // eslint-disable-line deprecation/deprecation + + const navigationWidgetDef = widgetDef; + + const reactNode = navigationWidgetDef.reactNode; + expect(reactNode).to.not.be.undefined; - const horizontalToolbar = + const cornerNode = navigationWidgetDef.renderCornerItem(); + expect(cornerNode).to.not.be.undefined; + }); + + const horizontalToolbar = { } />; - const verticalToolbar = + const verticalToolbar = { } />; - it("NavigationWidget should render", () => { - mount( - , - ); - }); + it("NavigationWidget should render", () => { + mount( + , + ); + }); - it("NavigationWidget should render correctly", () => { - shallow( - , - ).should.matchSnapshot(); - }); + it("NavigationWidget should render correctly", () => { + shallow( + , + ).should.matchSnapshot(); + }); - it("NavigationWidget should render with an item list", () => { - const hItemList = new ItemList([CoreTools.selectElementCommand]); - const vItemList = new ItemList([CoreTools.fitViewCommand]); + it("NavigationWidget should render with an item list", () => { + const hItemList = new ItemList([CoreTools.selectElementCommand]); + const vItemList = new ItemList([CoreTools.fitViewCommand]); - mount( - , - ); - }); + mount( + , + ); + }); - it("NavigationWidget should support update", () => { - const wrapper = mount( - , - ); - expect(wrapper.find(ToolButton).length).to.eq(4); - - wrapper.setProps({ verticalToolbar: undefined }); - wrapper.update(); - expect(wrapper.find(ToolButton).length).to.eq(2); - }); + it("NavigationWidget should support update", () => { + const wrapper = mount( + , + ); + expect(wrapper.find(ToolButton).length).to.eq(4); + + wrapper.setProps({ verticalToolbar: undefined }); + wrapper.update(); + expect(wrapper.find(ToolButton).length).to.eq(2); + }); - class TestContentControl extends ContentControl { - constructor(info: ConfigurableCreateInfo, options: any) { - super(info, options); + class TestContentControl extends ContentControl { + constructor(info: ConfigurableCreateInfo, options: any) { + super(info, options); - this.reactNode =
; + this.reactNode =
; + } } - } - class TestNavigationAidControl extends NavigationAidControl { - constructor(info: ConfigurableCreateInfo, options: any) { - super(info, options); + class TestNavigationAidControl extends NavigationAidControl { + constructor(info: ConfigurableCreateInfo, options: any) { + super(info, options); - this.reactNode =
Test Navigation Aid
; + this.reactNode =
Test Navigation Aid
; + } } - } - it("NavigationWidgetDef with invalid navigation aid should throw Error", () => { - const def = new NavigationWidgetDef({ // eslint-disable-line deprecation/deprecation - navigationAidId: "Aid1", + it("NavigationWidgetDef with invalid navigation aid should throw Error", () => { + const def = new NavigationWidgetDef({ // eslint-disable-line deprecation/deprecation + navigationAidId: "Aid1", + }); + ConfigurableUiManager.registerControl("Aid1", TestContentControl); + expect(() => def.renderCornerItem()).to.throw(Error); + ConfigurableUiManager.unregisterControl("Aid1"); }); - ConfigurableUiManager.registerControl("Aid1", TestContentControl); - expect(() => def.renderCornerItem()).to.throw(Error); - ConfigurableUiManager.unregisterControl("Aid1"); - }); - it("NavigationWidgetDef should handle updateNavigationAid", () => { - const def = new NavigationWidgetDef({ // eslint-disable-line deprecation/deprecation - navigationAidId: "Aid1", - }); - ConfigurableUiManager.registerControl("Aid1", TestNavigationAidControl); + it("NavigationWidgetDef should handle updateNavigationAid", () => { + const def = new NavigationWidgetDef({ // eslint-disable-line deprecation/deprecation + navigationAidId: "Aid1", + }); + ConfigurableUiManager.registerControl("Aid1", TestNavigationAidControl); - const element = def.reactNode; - expect(def.reactNode).to.eq(element); - const wrapper = mount(element as React.ReactElement); + const element = def.reactNode; + expect(def.reactNode).to.eq(element); + const wrapper = mount(element as React.ReactElement); - const connection = moq.Mock.ofType(); - FrontstageManager.setActiveNavigationAid("Aid1", connection.object); - wrapper.update(); + const connection = moq.Mock.ofType(); + FrontstageManager.setActiveNavigationAid("Aid1", connection.object); + wrapper.update(); - FrontstageManager.setActiveToolId(CoreTools.selectElementCommand.toolId); + FrontstageManager.setActiveToolId(CoreTools.selectElementCommand.toolId); - ConfigurableUiManager.unregisterControl("Aid1"); - }); + ConfigurableUiManager.unregisterControl("Aid1"); + }); - it("NavigationAidHost should render in 2.0 mode", () => { - mount( - - - ); - }); + it("NavigationAidHost should render in 2.0 mode", () => { + mount( + + + ); + }); - it("NavigationAidHost should render in 2.0 mode with snapWidgetOpacity", () => { - UiShowHideManager.snapWidgetOpacity = true; - mount( - - - ); - UiShowHideManager.snapWidgetOpacity = false; + it("NavigationAidHost should render in 2.0 mode with snapWidgetOpacity", () => { + UiShowHideManager.snapWidgetOpacity = true; + mount( + + + ); + UiShowHideManager.snapWidgetOpacity = false; + }); }); - }); diff --git a/ui/framework/src/test/widgets/ToolWidgetComposer.test.snap b/ui/framework/src/test/widgets/ToolWidgetComposer.test.snap index 4e69e853db6..84b983b0082 100644 --- a/ui/framework/src/test/widgets/ToolWidgetComposer.test.snap +++ b/ui/framework/src/test/widgets/ToolWidgetComposer.test.snap @@ -1,5 +1,40 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`FrameworkAccuDraw localStorage Wrapper ToolWidgetComposer ToolWidgetComposer should render correctly 1`] = ` + + + +`; + +exports[`FrameworkAccuDraw localStorage Wrapper ToolWidgetComposer ToolWidgetComposer with should render 1`] = ` + + + } + onMouseEnter={[Function]} + /> + +`; + exports[`ToolWidgetComposer ToolWidgetComposer should render correctly 1`] = ` { +describe("FrameworkAccuDraw localStorage Wrapper", () => { + + const localStorageToRestore = Object.getOwnPropertyDescriptor(window, "localStorage")!; + const localStorageMock = storageMock(); before(async () => { - await TestUtils.initializeUiFramework(); + Object.defineProperty(window, "localStorage", { + get: () => localStorageMock, + }); }); after(() => { - TestUtils.terminateUiFramework(); + Object.defineProperty(window, "localStorage", localStorageToRestore); }); - it("ToolWidgetComposer should render", () => { - mount(); - }); + describe("ToolWidgetComposer", () => { - it("ToolWidgetComposer should render correctly", () => { - shallow().should.matchSnapshot(); - }); + before(async () => { + await TestUtils.initializeUiFramework(); + }); - it("ToolWidgetComposer with should render", () => { - shallow(} />).should.matchSnapshot(); - }); + after(() => { + TestUtils.terminateUiFramework(); + }); - it("BackstageAppButtonProps should render", () => { - const wrapper = mount(); - wrapper.setProps({ icon: "icon-bentley" }); - }); + it("ToolWidgetComposer should render", () => { + mount(); + }); - it("BackstageAppButtonProps should update with default icon", () => { - const wrapper = mount(); - wrapper.setProps({ icon: undefined }); - }); + it("ToolWidgetComposer should render correctly", () => { + shallow().should.matchSnapshot(); + }); - it("BackstageAppButton should render in 2.0 mode", () => { - mount( - - - ); - }); + it("ToolWidgetComposer with should render", () => { + shallow(} />).should.matchSnapshot(); + }); + + it("BackstageAppButtonProps should render", () => { + const wrapper = mount(); + wrapper.setProps({ icon: "icon-bentley" }); + }); + + it("BackstageAppButtonProps should update with default icon", () => { + const wrapper = mount(); + wrapper.setProps({ icon: undefined }); + }); + it("BackstageAppButton should render in 2.0 mode", () => { + mount( + + + ); + }); + + }); }); diff --git a/ui/framework/src/ui-framework.ts b/ui/framework/src/ui-framework.ts index 6fa3c490b55..76756cf57d3 100644 --- a/ui/framework/src/ui-framework.ts +++ b/ui/framework/src/ui-framework.ts @@ -145,6 +145,7 @@ export * from "./ui-framework/selection/SelectionContextItemDef"; export * from "./ui-framework/selection/HideIsolateEmphasizeManager"; export * from "./ui-framework/selection/ClearEmphasisStatusField"; +export * from "./ui-framework/settings/ui/UiSettingsPage"; export * from "./ui-framework/settings/quantityformatting/QuantityFormat"; export * from "./ui-framework/shared/ActionButtonItemDef"; @@ -224,7 +225,8 @@ export * from "./ui-framework/uiadmin/FrameworkUiAdmin"; export * from "./ui-framework/uiprovider/DefaultDialogGridContainer"; -export * from "./ui-framework/uisettings/IModelAppUiSettings"; +export * from "./ui-framework/uisettings/AppUiSettings"; +export * from "./ui-framework/uisettings/UserSettingsStorage"; export * from "./ui-framework/uisettings/useUiSettings"; export * from "./ui-framework/utils/ViewUtilities"; diff --git a/ui/framework/src/ui-framework/UiFramework.ts b/ui/framework/src/ui-framework/UiFramework.ts index b42e403f8a9..fc9cfe29c04 100644 --- a/ui/framework/src/ui-framework/UiFramework.ts +++ b/ui/framework/src/ui-framework/UiFramework.ts @@ -6,6 +6,8 @@ * @module Utilities */ +// cSpell:ignore configurableui clientservices + import { Store } from "redux"; import { GuidString, Logger, ProcessDetector } from "@bentley/bentleyjs-core"; import { isFrontendAuthorizationClient } from "@bentley/frontend-authorization-client"; @@ -16,7 +18,7 @@ import { Presentation } from "@bentley/presentation-frontend"; import { TelemetryEvent } from "@bentley/telemetry-client"; import { getClassName, UiError } from "@bentley/ui-abstract"; import { UiComponents } from "@bentley/ui-components"; -import { LocalUiSettings, SettingsManager, UiEvent, UiSettings } from "@bentley/ui-core"; +import { LocalSettingsStorage, SettingsManager, UiEvent, UiSettingsStorage } from "@bentley/ui-core"; import { BackstageManager } from "./backstage/BackstageManager"; import { DefaultIModelServices } from "./clientservices/DefaultIModelServices"; import { DefaultProjectServices } from "./clientservices/DefaultProjectServices"; @@ -34,11 +36,21 @@ import * as keyinPaletteTools from "./tools/KeyinPaletteTools"; import * as restoreLayoutTools from "./tools/RestoreLayoutTool"; import * as openSettingTools from "./tools/OpenSettingsTool"; import * as toolSettingTools from "./tools/ToolSettingsTools"; -import { UiShowHideManager } from "./utils/UiShowHideManager"; +import { UiShowHideManager, UiShowHideSettingsProvider } from "./utils/UiShowHideManager"; import { WidgetManager } from "./widgets/WidgetManager"; // cSpell:ignore Mobi +/** Interface to be implemented but any classes that wants to load their user settings when the UiSetting storage class is set. + * @beta + */ +export interface UserSettingsProvider { + /** Unique provider Id */ + providerId: string; + /** Function to load settings from settings storage */ + loadUserSettings(storage: UiSettingsStorage): Promise; +} + /** UiVisibility Event Args interface. * @public */ @@ -70,8 +82,8 @@ export class FrameworkVersionChangedEvent extends UiEvent = new Map(); + + /** Registers class that will be informed when the UserSettingsStorage location has been set or changed. This allows + * classes to load any previously saved settings from the new storage location. Common storage locations are the browser's + * local storage, or the iTwin Product Settings cloud storage available via the SettingsAdmin see `IModelApp.settingsAdmin`. + * @alpha + */ + public static registerUserSettingsProvider(entry: UserSettingsProvider) { + if (this._uiSettingsProviderRegistry.has(entry.providerId)) + return false; + + this._uiSettingsProviderRegistry.set(entry.providerId, entry); + return true; + } /** Get Show Ui event. * @public @@ -135,7 +160,7 @@ export class UiFramework { if (frameworkStateKey && store) UiFramework._frameworkStateKeyInStore = frameworkStateKey; - // set up namespace and register akk tools from package + // set up namespace and register all tools from package const frameworkNamespace = UiFramework._i18n.registerNamespace(UiFramework.i18nNamespace); [ restoreLayoutTools, @@ -174,10 +199,13 @@ export class UiFramework { UiFramework._initialized = true; + // initialize any standalone settings providers that don't need to have defaults set by iModelApp + UiShowHideSettingsProvider.initialize(); + return readFinishedPromise; } - /** Unregisters the UiFramework internationalization service namespace */ + /** Un-registers the UiFramework internationalization service namespace */ public static terminate() { UiFramework._store = undefined; UiFramework._frameworkStateKeyInStore = "frameworkState"; @@ -218,8 +246,12 @@ export class UiFramework { * @beta */ public static get frameworkState(): FrameworkState | undefined { - // eslint-disable-next-line dot-notation - return UiFramework.store.getState()[UiFramework.frameworkStateKey]; + try { + // eslint-disable-next-line dot-notation + return UiFramework.store.getState()[UiFramework.frameworkStateKey]; + } catch (_e) { + return undefined; + } } /** The Redux store */ @@ -322,7 +354,7 @@ export class UiFramework { } public static setAccudrawSnapMode(snapMode: SnapMode) { - UiFramework.store.dispatch({ type: ConfigurableUiActionId.SetSnapMode, payload: snapMode }); + UiFramework.dispatchActionToStore(ConfigurableUiActionId.SetSnapMode, snapMode, true); } public static getAccudrawSnapMode(): SnapMode { @@ -377,8 +409,18 @@ export class UiFramework { } /** @beta */ - public static setUiSettings(uiSettings: UiSettings, immediateSync = false) { - UiFramework._uiSettings = uiSettings; + public static async setUiSettingsStorage(storage: UiSettingsStorage, immediateSync = false) { + if (UiFramework._uiSettingsStorage === storage) + return; + + UiFramework._uiSettingsStorage = storage; + + // let any registered providers to load values from the new storage location + const providerKeys = [...this._uiSettingsProviderRegistry.keys()]; + for await (const key of providerKeys) { + await this._uiSettingsProviderRegistry.get(key)!.loadUserSettings(storage); + } + // istanbul ignore next if (immediateSync) SyncUiEventDispatcher.dispatchImmediateSyncUiEvent(SyncUiEventId.UiSettingsChanged); @@ -387,10 +429,8 @@ export class UiFramework { } /** @beta */ - public static getUiSettings(): UiSettings { - if (undefined === UiFramework._uiSettings) - UiFramework._uiSettings = new LocalUiSettings(); - return UiFramework._uiSettings; + public static getUiSettingsStorage(): UiSettingsStorage { + return UiFramework._uiSettingsStorage; } /** @beta */ @@ -446,7 +486,10 @@ export class UiFramework { } public static setColorTheme(theme: string) { - UiFramework.store.dispatch({ type: ConfigurableUiActionId.SetTheme, payload: theme }); + if (UiFramework.getColorTheme() === theme) + return; + + UiFramework.dispatchActionToStore(ConfigurableUiActionId.SetTheme, theme, true); } public static getColorTheme(): string { @@ -454,7 +497,10 @@ export class UiFramework { } public static setWidgetOpacity(opacity: number) { - UiFramework.store.dispatch({ type: ConfigurableUiActionId.SetWidgetOpacity, payload: opacity }); + if (UiFramework.getWidgetOpacity() === opacity) + return; + + UiFramework.dispatchActionToStore(ConfigurableUiActionId.SetWidgetOpacity, opacity, true); } public static getWidgetOpacity(): number { @@ -468,16 +514,30 @@ export class UiFramework { /** Returns the Ui Version. * @beta */ - // istanbul ignore next public static get uiVersion(): string { - return UiFramework._uiVersion; + return UiFramework.frameworkState ? UiFramework.frameworkState.configurableUiState.frameworkVersion : this._uiVersion; + } + + public static setUiVersion(version: string) { + if (UiFramework.uiVersion === version) + return; + + UiFramework.dispatchActionToStore(ConfigurableUiActionId.SetFrameworkVersion, version === "1" ? "1" : "2", true); + } + + public static get useDragInteraction(): boolean { + return UiFramework.frameworkState ? UiFramework.frameworkState.configurableUiState.useDragInteraction : false; + } + + public static setUseDragInteraction(useDragInteraction: boolean) { + UiFramework.dispatchActionToStore(ConfigurableUiActionId.SetDragInteraction, useDragInteraction, true); } /** Send logging message to the telemetry system * @internal */ // istanbul ignore next - public static async postTelemetry(eventName: string, eventId?: GuidString, contextId?: GuidString, iModeId?: GuidString, changeSetId?: GuidString, time?: TrackingTime, additionalProperties?: { [key: string]: any }): Promise { + public static async postTelemetry(eventName: string, eventId?: GuidString, contextId?: GuidString, iModeId?: GuidString, changeSetId?: string, time?: TrackingTime, additionalProperties?: { [key: string]: any }): Promise { if (!IModelApp.authorizationClient || !IModelApp.authorizationClient.hasSignedIn) return; const requestContext = await AuthorizedFrontendRequestContext.create(); @@ -490,15 +550,6 @@ export class UiFramework { // eslint-disable-next-line @typescript-eslint/no-floating-promises UiFramework.postTelemetry(`Ui Version changed to ${args.version} `, "F2772C81-962D-4755-807C-2D675A5FF399"); UiFramework._uiVersion = args.version; - - // If Ui Version 1, save widget opacity - // istanbul ignore if - if (args.oldVersion === "1") - UiFramework._version1WidgetOpacity = UiFramework.getWidgetOpacity(); - - // If Ui Version 1, restore widget opacity; otherwise, set widget opacity to 1.0 to basically turn the feature off. - // This fixes use of "backdrop-filter: blur(10px)"" CSS. - UiFramework.setWidgetOpacity(args.version === "1" ? UiFramework._version1WidgetOpacity : 1.0); }; // istanbul ignore next diff --git a/ui/framework/src/ui-framework/accudraw/AccuDrawFieldContainer.tsx b/ui/framework/src/ui-framework/accudraw/AccuDrawFieldContainer.tsx index ec94876dd29..d79a29e0262 100644 --- a/ui/framework/src/ui-framework/accudraw/AccuDrawFieldContainer.tsx +++ b/ui/framework/src/ui-framework/accudraw/AccuDrawFieldContainer.tsx @@ -14,7 +14,7 @@ import { AccuDrawSetFieldFocusEventArgs, AccuDrawSetFieldLockEventArgs, AccuDrawSetModeEventArgs, AccuDrawUiAdmin, IconSpecUtilities, } from "@bentley/ui-abstract"; -import { CommonProps, IconSpec, Orientation, UiSettings } from "@bentley/ui-core"; +import { CommonProps, IconSpec, Orientation, UiSettingsStorage } from "@bentley/ui-core"; import { AccuDrawInputField } from "./AccuDrawInputField"; import { CompassMode, IModelApp, ItemField, ScreenViewport, SelectedViewportChangedArgs } from "@bentley/imodeljs-frontend"; import { KeyboardShortcutManager } from "../keyboardshortcut/KeyboardShortcut"; @@ -30,8 +30,8 @@ import { ColorDef } from "@bentley/imodeljs-common"; export interface AccuDrawFieldContainerProps extends CommonProps { /** Orientation of the fields */ orientation: Orientation; - /** Optional parameter for persistent UI settings. Defaults to LocalUiSettings. */ - uiSettings?: UiSettings; + /** Optional parameter for persistent UI settings. Defaults to LocalSettingsStorage. */ + uiSettingsStorage?: UiSettingsStorage; /** @internal */ showZOverride?: boolean; } @@ -52,7 +52,7 @@ const defaultDistanceIcon = IconSpecUtilities.createSvgIconSpec(distanceIconSvg) /** @alpha */ export function AccuDrawFieldContainer(props: AccuDrawFieldContainerProps) { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { className, style, orientation, uiSettings, showZOverride, ...otherProps } = props; + const { className, style, orientation, uiSettingsStorage, showZOverride, ...otherProps } = props; const [containerIndex] = React.useState(() => ++AccuDrawContainerIndex); const xInputRef = React.useRef(null); diff --git a/ui/framework/src/ui-framework/accudraw/FrameworkAccuDraw.ts b/ui/framework/src/ui-framework/accudraw/FrameworkAccuDraw.ts index 83721d0eb31..5449798aa95 100644 --- a/ui/framework/src/ui-framework/accudraw/FrameworkAccuDraw.ts +++ b/ui/framework/src/ui-framework/accudraw/FrameworkAccuDraw.ts @@ -8,10 +8,11 @@ import { AccuDraw, BeButtonEvent, CompassMode, IModelApp, ItemField, NotifyMessageDetails, OutputMessagePriority, QuantityType, RotationMode } from "@bentley/imodeljs-frontend"; import { AccuDrawField, AccuDrawMode, AccuDrawSetFieldValueFromUiEventArgs, AccuDrawUiAdmin, ConditionalBooleanValue } from "@bentley/ui-abstract"; -import { UiFramework } from "../UiFramework"; +import { UiFramework, UserSettingsProvider } from "../UiFramework"; import { SyncUiEventDispatcher, SyncUiEventId } from "../syncui/SyncUiEventDispatcher"; import { AccuDrawUiSettings } from "./AccuDrawUiSettings"; import { BeUiEvent } from "@bentley/bentleyjs-core"; +import { UiSettings, UiSettingsStatus } from "@bentley/ui-core"; // cspell:ignore dont @@ -49,9 +50,12 @@ const rotationModeToKeyMap = new Map([ export class AccuDrawUiSettingsChangedEvent extends BeUiEvent<{}> { } /** @internal */ -export class FrameworkAccuDraw extends AccuDraw { +export class FrameworkAccuDraw extends AccuDraw implements UserSettingsProvider { private static _displayNotifications = false; private static _uiSettings: AccuDrawUiSettings | undefined; + private static _settingsNamespace = "AppUiSettings"; + private static _notificationsKey = "AccuDrawNotifications"; + public readonly providerId = "FrameworkAccuDraw"; /** Determines if AccuDraw.rotationMode === RotationMode.Top */ public static readonly isTopRotationConditional = new ConditionalBooleanValue(() => IModelApp.accuDraw.rotationMode === RotationMode.Top, [SyncUiEventId.AccuDrawRotationChanged]); @@ -75,7 +79,16 @@ export class FrameworkAccuDraw extends AccuDraw { /** Determines if notifications should be displayed for AccuDraw changes */ public static get displayNotifications(): boolean { return FrameworkAccuDraw._displayNotifications; } - public static set displayNotifications(v: boolean) { FrameworkAccuDraw._displayNotifications = v; } + public static set displayNotifications(v: boolean) { + FrameworkAccuDraw._displayNotifications = v; + void UiFramework.getUiSettingsStorage().saveSetting (this._settingsNamespace, this._notificationsKey, v); + } + + public async loadUserSettings(storage: UiSettings): Promise { + const result = await storage.getSetting (FrameworkAccuDraw._settingsNamespace, FrameworkAccuDraw._notificationsKey); + if (result.status === UiSettingsStatus.Success) + FrameworkAccuDraw._displayNotifications = result.setting; + } /** AccuDraw User Interface settings */ public static get uiSettings(): AccuDrawUiSettings | undefined { return FrameworkAccuDraw._uiSettings; } @@ -87,6 +100,7 @@ export class FrameworkAccuDraw extends AccuDraw { constructor() { super(); AccuDrawUiAdmin.onAccuDrawSetFieldValueFromUiEvent.addListener(this.handleSetFieldValueFromUiEvent); + UiFramework.registerUserSettingsProvider(this); } private handleSetFieldValueFromUiEvent = async (args: AccuDrawSetFieldValueFromUiEventArgs) => { diff --git a/ui/framework/src/ui-framework/backstage/BackstageComposer.tsx b/ui/framework/src/ui-framework/backstage/BackstageComposer.tsx index 03535e77f4b..784e84e11cd 100644 --- a/ui/framework/src/ui-framework/backstage/BackstageComposer.tsx +++ b/ui/framework/src/ui-framework/backstage/BackstageComposer.tsx @@ -96,7 +96,7 @@ export const useGroupedItems = (items: ReadonlyArray): GroupedIte }; /** Props of [[BackstageComposer]] component. - * @beta + * @public */ export interface BackstageComposerProps extends CommonProps { /** React node for an optional header item */ @@ -108,7 +108,7 @@ export interface BackstageComposerProps extends CommonProps { } /** Backstage component composed from [[BackstageManager]] items. - * @beta + * @public */ export function BackstageComposer(props: BackstageComposerProps) { const [defaultItemsManager, setDefaultItemsManager] = React.useState(new BackstageItemsManager(props.items)); diff --git a/ui/framework/src/ui-framework/configurableui/state.ts b/ui/framework/src/ui-framework/configurableui/state.ts index eb7ed6cb3b0..f04102b999e 100644 --- a/ui/framework/src/ui-framework/configurableui/state.ts +++ b/ui/framework/src/ui-framework/configurableui/state.ts @@ -21,6 +21,8 @@ export enum ConfigurableUiActionId { SetTheme = "configurableui:set_theme", SetToolPrompt = "configurableui:set_toolprompt", SetWidgetOpacity = "configurableui:set_widget_opacity", + SetDragInteraction = "configurableui:set-drag-interaction", + SetFrameworkVersion = "configurableui:set-framework-version", } /** The portion of state managed by the ConfigurableUiReducer. @@ -31,6 +33,8 @@ export interface ConfigurableUiState { toolPrompt: string; theme: string; widgetOpacity: number; + useDragInteraction: boolean; + frameworkVersion: string; } /** used on first call of ConfigurableUiReducer */ @@ -39,6 +43,8 @@ const initialState: ConfigurableUiState = { toolPrompt: "", theme: SYSTEM_PREFERRED_COLOR_THEME, widgetOpacity: WIDGET_OPACITY_DEFAULT, + useDragInteraction: false, + frameworkVersion: "1", }; /** An object with a function that creates each ConfigurableUiReducer that can be handled by our reducer. @@ -55,6 +61,8 @@ export const ConfigurableUiActions = { // eslint-disable-line @typescript-esli setWidgetOpacity: // istanbul ignore next (opacity: number) => createAction(ConfigurableUiActionId.SetWidgetOpacity, opacity), + setDragInteraction: (dragInteraction: boolean) => createAction(ConfigurableUiActionId.SetDragInteraction, dragInteraction), + setFrameworkVersion: (frameworkVersion: string) => createAction(ConfigurableUiActionId.SetFrameworkVersion, frameworkVersion), }; /** Union of ConfigurableUi Redux actions @@ -65,35 +73,28 @@ export type ConfigurableUiActionsUnion = ActionsUnion { return { selectedNodeItems: this.filterSelectionItems(selectedNodeItems), @@ -108,7 +108,7 @@ export class VisibilityTreeEventHandler extends UnifiedSelectionTreeEventHandler } public onSelectionReplaced({ replacements }: TreeSelectionReplacementEventArgs) { - const filteredReplacements = from(replacements).pipe( + const filteredReplacements = toRxjsObservable(replacements).pipe( map(({ selectedNodeItems }) => { return { selectedNodeItems: this.filterSelectionItems(selectedNodeItems), diff --git a/ui/framework/src/ui-framework/popup/KeyinPalettePanel.tsx b/ui/framework/src/ui-framework/popup/KeyinPalettePanel.tsx index c3c424eff5c..9d2b833674c 100644 --- a/ui/framework/src/ui-framework/popup/KeyinPalettePanel.tsx +++ b/ui/framework/src/ui-framework/popup/KeyinPalettePanel.tsx @@ -15,7 +15,7 @@ import { IModelApp, NotifyMessageDetails, OutputMessagePriority, OutputMessageTy import { UiFramework } from "../UiFramework"; import { matchesWords, OnItemExecutedFunc, SpecialKey } from "@bentley/ui-abstract"; import { ClearKeyinPaletteHistoryTool } from "../tools/KeyinPaletteTools"; -import { useUiSettingsContext } from "../uisettings/useUiSettings"; +import { useUiSettingsStorageContext } from "../uisettings/useUiSettings"; import { KeyinEntry } from "../uiadmin/FrameworkUiAdmin"; const KEYIN_PALETTE_NAMESPACE = "KeyinPalettePanel"; @@ -23,11 +23,11 @@ const KEYIN_HISTORY_KEY = "historyArray"; /** @internal */ export function clearKeyinPaletteHistory() { - const uiSettings = UiFramework.getUiSettings(); + const uiSettingsStorage = UiFramework.getUiSettingsStorage(); // istanbul ignore else - if (uiSettings) { + if (uiSettingsStorage) { // eslint-disable-next-line @typescript-eslint/no-floating-promises - uiSettings.deleteSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY); + uiSettingsStorage.deleteSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY); } } @@ -46,11 +46,11 @@ export function KeyinPalettePanel({ keyins, onKeyinExecuted, historyLength: allo const inputRef = React.useRef(null); const keyinSeparator = "--#--"; const [historyKeyins, setHistoryKeyins] = React.useState([]); - const uiSettings = useUiSettingsContext(); + const uiSettingsStorage = useUiSettingsStorageContext(); React.useEffect(() => { async function fetchState() { - const settingsResult = await uiSettings.getSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY); + const settingsResult = await uiSettingsStorage.getSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY); // istanbul ignore else if (UiSettingsStatus.Success === settingsResult.status) { const filteredHistory = (settingsResult.setting as string[]).filter((keyin)=>{ @@ -64,18 +64,18 @@ export function KeyinPalettePanel({ keyins, onKeyinExecuted, historyLength: allo } fetchState(); // eslint-disable-line @typescript-eslint/no-floating-promises - }, [uiSettings]); + }, [uiSettingsStorage]); // istanbul ignore next const storeHistoryKeyins = React.useCallback(async (value: string[]) => { // eslint-disable-next-line @typescript-eslint/no-floating-promises - const result = await uiSettings.saveSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY, value); + const result = await uiSettingsStorage.saveSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY, value); if (result.status !== UiSettingsStatus.Success) { const briefMessage = UiFramework.translate("keyinbrowser.couldNotSaveHistory"); const errorDetails = new NotifyMessageDetails(OutputMessagePriority.Error, briefMessage); IModelApp.notifications.outputMessage(errorDetails); } - }, [uiSettings]); + }, [uiSettingsStorage]); const allKeyins = React.useMemo(() => { const availableKeyins = []; diff --git a/ui/framework/src/ui-framework/redux/FrameworkState.ts b/ui/framework/src/ui-framework/redux/FrameworkState.ts index dcdfefe0c56..365da7fbb0c 100644 --- a/ui/framework/src/ui-framework/redux/FrameworkState.ts +++ b/ui/framework/src/ui-framework/redux/FrameworkState.ts @@ -11,7 +11,7 @@ import { combineReducers } from "./redux-ts"; import { SessionState, SessionStateReducer } from "./SessionState"; /** Interface combining all the Framework state interfaces. - * @beta + * @public */ export interface FrameworkState { configurableUiState: ConfigurableUiState; @@ -19,7 +19,7 @@ export interface FrameworkState { } /** Framework reducer that combines the [[ConfigurableUiReducer]] and [[SessionStateReducer]]. - * @beta + * @public */ export const FrameworkReducer = combineReducers({ // eslint-disable-line @typescript-eslint/naming-convention configurableUiState: ConfigurableUiReducer, diff --git a/ui/framework/src/ui-framework/redux/ReducerRegistry.ts b/ui/framework/src/ui-framework/redux/ReducerRegistry.ts index 85abb956ad0..a0f99f4abab 100644 --- a/ui/framework/src/ui-framework/redux/ReducerRegistry.ts +++ b/ui/framework/src/ui-framework/redux/ReducerRegistry.ts @@ -10,7 +10,7 @@ import { UiError } from "@bentley/ui-abstract"; import { UiFramework } from "../UiFramework"; /** NameToReducerMap used by Reducer Registry - * @beta + * @public */ export interface NameToReducerMap { [name: string]: (state: any, action: any) => any; diff --git a/ui/framework/src/ui-framework/redux/SessionState.ts b/ui/framework/src/ui-framework/redux/SessionState.ts index 82c9b2f80d1..435226cd175 100644 --- a/ui/framework/src/ui-framework/redux/SessionState.ts +++ b/ui/framework/src/ui-framework/redux/SessionState.ts @@ -17,7 +17,7 @@ import { ActionsUnion, createAction, DeepReadonly } from "./redux-ts"; /** PresentationSelectionScope holds the id and the localized label for a selection scope supported for a specific iModel. * Added to avoid an api-extract error caused by using SelectionScope. - * @beta + * @public  */ export interface PresentationSelectionScope { id: string; @@ -26,7 +26,7 @@ export interface PresentationSelectionScope { /** Definition of data added to Redux store to define cursor menu. If menuItems are empty the menu control is not displayed. * To close the menu clear the menuItems or pass in undefined as the CursorData. - * @beta + * @public */ export interface CursorMenuData { items: MenuItemProps[]; @@ -35,7 +35,7 @@ export interface CursorMenuData { /** Action Ids used by Redux and to send sync UI components. Typically used to refresh visibility or enable state of control. * Since these are also used as sync ids they should be in lowercase. - * @beta + * @public */ export enum SessionStateActionId { SetNumItemsSelected = "sessionstate:set-num-items-selected", @@ -51,7 +51,7 @@ export enum SessionStateActionId { } /** The portion of state managed by the SessionStateReducer. - * @beta + * @public */ export interface SessionState { numItemsSelected: number; @@ -103,7 +103,7 @@ export interface SessionStateActionsProps { } /** An object with a function that creates each SessionStateReducer that can be handled by our reducer. - * @beta + * @public */ export const SessionStateActions = { // eslint-disable-line @typescript-eslint/naming-convention setUserInfo: @@ -140,12 +140,12 @@ export const SessionStateActions = { // eslint-disable-line @typescript-eslint/ export const sessionStateMapDispatchToProps = { ...SessionStateActions }; /** Union of SessionState Redux actions - * @beta + * @public */ export type SessionStateActionsUnion = ActionsUnion; /** Handles actions to update SessionState. - * @beta + * @public */ export function SessionStateReducer(state: SessionState = initialState, action: SessionStateActionsUnion): DeepReadonly { switch (action.type) { diff --git a/ui/framework/src/ui-framework/redux/StateManager.ts b/ui/framework/src/ui-framework/redux/StateManager.ts index eb0c7db0206..03bd6e17625 100644 --- a/ui/framework/src/ui-framework/redux/StateManager.ts +++ b/ui/framework/src/ui-framework/redux/StateManager.ts @@ -26,7 +26,7 @@ export interface FrameworkRootState { * Centralized state management class using Redux actions, reducers and store. This class monitors the ReducerRegistry and will * automatically update the store when a new reducer is registered. This allows the store to be incrementally constructed as modules * and/or extensions are loaded. - * @beta + * @public */ export class StateManager { private static _LOG_CATEGORY = "StateManager"; diff --git a/ui/framework/src/ui-framework/settings/quantityformatting/QuantityFormat.tsx b/ui/framework/src/ui-framework/settings/quantityformatting/QuantityFormat.tsx index a39bd487f54..8a0414b841e 100644 --- a/ui/framework/src/ui-framework/settings/quantityformatting/QuantityFormat.tsx +++ b/ui/framework/src/ui-framework/settings/quantityformatting/QuantityFormat.tsx @@ -50,7 +50,7 @@ export function getQuantityFormatsSettingsManagerEntry(itemPriority: number, opt itemPriority, tabId: "uifw:Quantity", label: UiFramework.translate("settings.quantity-formatting.label"), subLabel: UiFramework.translate("settings.quantity-formatting.subLabel"), - page: , isDisabled: false, icon: "icon-measure", @@ -62,7 +62,7 @@ export function getQuantityFormatsSettingsManagerEntry(itemPriority: number, opt /** UI Component shown in settings page to set the active Presentation Unit System and to set format overrides. * @beta */ -export function QuantityFormatSettingsPanel({initialQuantityType, availableUnitSystems}: QuantityFormatterSettingsOptions) { +export function QuantityFormatSettingsPage({initialQuantityType, availableUnitSystems}: QuantityFormatterSettingsOptions) { const [activeUnitSystemKey, setActiveUnitSystemKey] = React.useState(IModelApp.quantityFormatter.activeUnitSystem); const [activeQuantityType, setActiveQuantityType] = React.useState(getQuantityTypeKey(initialQuantityType)); const [activeFormatterSpec, setActiveFormatterSpec] = diff --git a/ui/framework/src/ui-framework/settings/quantityformatting/UnitSystemSelector.tsx b/ui/framework/src/ui-framework/settings/quantityformatting/UnitSystemSelector.tsx index 0217d97defc..cdbf482a7c8 100644 --- a/ui/framework/src/ui-framework/settings/quantityformatting/UnitSystemSelector.tsx +++ b/ui/framework/src/ui-framework/settings/quantityformatting/UnitSystemSelector.tsx @@ -11,14 +11,18 @@ import { UiFramework } from "../../UiFramework"; import { Select } from "@bentley/ui-core"; import { UnitSystemKey } from "@bentley/imodeljs-frontend"; -/** @alpha */ +/** Props for [[UnitSystemSelector]] + * @beta + */ export interface UnitSystemSelectorProps { selectedUnitSystemKey: UnitSystemKey; onUnitSystemSelected: (unitSystem: UnitSystemKey) => void; availableUnitSystems: Set; } -/** @alpha */ +/** Select control to set the "active" Presentation Unit System. This setting determine what units are display for quantity values (i.e. foot vs meter). + * @alpha + */ export function UnitSystemSelector(props: UnitSystemSelectorProps) { // eslint-disable-line @typescript-eslint/naming-convention const label = React.useRef (UiFramework.translate("presentationUnitSystem.selector-label")); const { selectedUnitSystemKey, onUnitSystemSelected, availableUnitSystems } = props; diff --git a/ui/framework/src/ui-framework/settings/ui/UiSettingsPage.scss b/ui/framework/src/ui-framework/settings/ui/UiSettingsPage.scss new file mode 100644 index 00000000000..e946aa3b215 --- /dev/null +++ b/ui/framework/src/ui-framework/settings/ui/UiSettingsPage.scss @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Bentley Systems, Incorporated. All rights reserved. +* See LICENSE.md in the project root for license terms and full copyright notice. +*--------------------------------------------------------------------------------------------*/ +@import "~@bentley/ui-core/lib/ui-core/style/themecolors"; +@import "~@bentley/ui-core/lib/ui-core/style/typography"; +@import "~@bentley/ui-core/lib/ui-core/base/base"; + +// cspell:ignore leftpanel rightpanel + +$settings-leftpanel-width: 225px; +$settings-rightpanel-width: 220px; + +.components-settings-container { + height: calc(100% - 50px); + overflow: hidden; + } + +.uifw-settings { + background: $buic-background-dialog; + color: $buic-text-color; + border-radius: 3px; + font-size: $uicore-font-size; + display: flex; + flex-direction: column; +} + +.uifw-settings-item { + display: flex; + + > .panel { + flex: 1; + display: flex; + box-sizing: border-box; + } + + > .left-panel { + flex-direction: column; + min-width: $settings-leftpanel-width; + padding: 20px 15px 20px 15px; + + > .title { + font-size: 18px; + } + + > .description { + margin-top: 10px; + font-size: 12px; + color: $buic-text-color-muted; + } + } + + > .right-panel { + display:flex; + align-items: center; + min-width: $settings-rightpanel-width; + padding: 30px 50px 30px 30px; + + .toggle { + margin-right: 10px; + } + + .select-theme-container { + flex: 1; + } + } +} diff --git a/ui/framework/src/ui-framework/settings/ui/UiSettingsPage.tsx b/ui/framework/src/ui-framework/settings/ui/UiSettingsPage.tsx new file mode 100644 index 00000000000..8f48b2c2900 --- /dev/null +++ b/ui/framework/src/ui-framework/settings/ui/UiSettingsPage.tsx @@ -0,0 +1,208 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Bentley Systems, Incorporated. All rights reserved. +* See LICENSE.md in the project root for license terms and full copyright notice. +*--------------------------------------------------------------------------------------------*/ +/** @packageDocumentation + * @module Settings + */ + +// cSpell:ignore configurableui checkmark + +import widowSettingsIconSvg from "@bentley/icons-generic/icons/window-settings.svg?sprite"; +import "./UiSettingsPage.scss"; +import * as React from "react"; +import { Select, SelectOption, SettingsTabEntry, Slider, Toggle } from "@bentley/ui-core"; +import { UiFramework } from "../../UiFramework"; +import { ColorTheme, SYSTEM_PREFERRED_COLOR_THEME } from "../../theme/ThemeManager"; +import { UiShowHideManager } from "../../utils/UiShowHideManager"; +import { SyncUiEventArgs, SyncUiEventDispatcher, SyncUiEventId } from "../../syncui/SyncUiEventDispatcher"; +import { IconSpecUtilities } from "@bentley/ui-abstract"; + +/** UiSettingsPage displaying the active UI settings. This page lets users set the following settings. + * + * - theme - Dark, Light, or based on OS preference. + * - auto hide - Starts a timer and blanks out ui components that overlay content if there is no mouse movement for a period of time. + * - drag interaction - If set, toolbar group buttons require a press and drag or a long press to open. In this mode a child action + * item is shown as the group button and is activated when button is clicked. If a different child item is selected, it becomes the + * active group button item. + * - use proximity - Changes the opacity of toolbar from transparent to opaque as the mouse moves closer. + * - snap widget opacity - triggers an abrupt change from transparent to opaque for tool and navigation widgets, instead of a gradual change based on mouse location. + * - widget opacity - determines how transparent floating widgets in V2 and all widgets in V1 become when the mouse in not in them. + * - UI version - if allowed by props, the UI version can be toggled between V1 and V2. + * + * @beta + */ +export function UiSettingsPage({allowSettingUiFrameworkVersion}: {allowSettingUiFrameworkVersion: boolean}) { + const themeTitle = React.useRef(UiFramework.translate("settings.uiSettingsPage.themeTitle")); + const themeDescription = React.useRef(UiFramework.translate("settings.uiSettingsPage.themeDescription")); + const autoHideTitle = React.useRef(UiFramework.translate("settings.uiSettingsPage.autoHideTitle")); + const autoHideDescription = React.useRef(UiFramework.translate("settings.uiSettingsPage.autoHideDescription")); + const dragInteractionTitle = React.useRef(UiFramework.translate("settings.uiSettingsPage.dragInteractionTitle")); + const dragInteractionDescription = React.useRef(UiFramework.translate("settings.uiSettingsPage.dragInteractionDescription")); + const useNewUiTitle = React.useRef(UiFramework.translate("settings.uiSettingsPage.newUiTitle")); + const useNewUiDescription = React.useRef(UiFramework.translate("settings.uiSettingsPage.newUiDescription")); + const useProximityOpacityTitle = React.useRef(UiFramework.translate("settings.uiSettingsPage.useProximityOpacityTitle")); + const useProximityOpacityDescription = React.useRef(UiFramework.translate("settings.uiSettingsPage.useProximityOpacityDescription")); + const snapWidgetOpacityTitle = React.useRef(UiFramework.translate("settings.uiSettingsPage.snapWidgetOpacityTitle")); + const snapWidgetOpacityDescription = React.useRef(UiFramework.translate("settings.uiSettingsPage.snapWidgetOpacityDescription")); + const darkLabel = React.useRef(UiFramework.translate("settings.uiSettingsPage.dark")); + const lightLabel = React.useRef(UiFramework.translate("settings.uiSettingsPage.light")); + const systemPreferredLabel = React.useRef(UiFramework.translate("settings.uiSettingsPage.systemPreferred")); + const widgetOpacityTitle = React.useRef(UiFramework.translate("settings.uiSettingsPage.widgetOpacityTitle")); + const widgetOpacityDescription = React.useRef(UiFramework.translate("settings.uiSettingsPage.widgetOpacityDescription")); + + const [theme, setTheme] = React.useState(()=>UiFramework.getColorTheme()); + const [uiVersion, setUiVersion] = React.useState(()=>UiFramework.uiVersion); + const [useDragInteraction, setUseDragInteraction] = React.useState(()=>UiFramework.useDragInteraction); + const [widgetOpacity, setWidgetOpacity] = React.useState(()=>UiFramework.getWidgetOpacity()); + const [autoHideUi, setAutoHideUi] = React.useState(()=>UiShowHideManager.autoHideUi); + const [useProximityOpacity, setUseProximityOpacity] = React.useState(()=>UiShowHideManager.useProximityOpacity); + const [snapWidgetOpacity, setSnapWidgetOpacity] = React.useState(()=>UiShowHideManager.snapWidgetOpacity); + + React.useEffect(() => { + const syncIdsOfInterest = ["configurableui:set_theme", "configurableui:set_widget_opacity", + "configurableui:set-drag-interaction","configurableui:set-framework-version", SyncUiEventId.ShowHideManagerSettingChange ]; + + const handleSyncUiEvent = (args: SyncUiEventArgs) => { + // istanbul ignore else + if (syncIdsOfInterest.some((value: string): boolean => args.eventIds.has(value))) { + if (UiFramework.getColorTheme() !== theme) + setTheme(UiFramework.getColorTheme()); + if (UiShowHideManager.autoHideUi !== autoHideUi) + setAutoHideUi(UiShowHideManager.autoHideUi); + if (UiFramework.uiVersion !== uiVersion) + setUiVersion(UiFramework.uiVersion); + if (UiFramework.useDragInteraction !== useDragInteraction) + setUseDragInteraction(UiFramework.useDragInteraction); + if (UiFramework.getWidgetOpacity() !== widgetOpacity) + setWidgetOpacity(UiFramework.getWidgetOpacity()); + if (UiShowHideManager.autoHideUi !== autoHideUi) + setAutoHideUi(UiShowHideManager.autoHideUi); + if (UiShowHideManager.useProximityOpacity !== useProximityOpacity) + setUseProximityOpacity(UiShowHideManager.useProximityOpacity); + if (UiShowHideManager.snapWidgetOpacity !== snapWidgetOpacity) + setSnapWidgetOpacity(UiShowHideManager.snapWidgetOpacity); + } + }; + return SyncUiEventDispatcher.onSyncUiEvent.addListener(handleSyncUiEvent); + }, [autoHideUi, snapWidgetOpacity, theme, uiVersion, useDragInteraction, useProximityOpacity, widgetOpacity]); + + const defaultThemeOption = { label: systemPreferredLabel.current, value: SYSTEM_PREFERRED_COLOR_THEME }; + const themeOptions: Array = [ + defaultThemeOption, + { label: lightLabel.current, value: ColorTheme.Light }, + { label: darkLabel.current, value: ColorTheme.Dark }, + ]; + + const onThemeChange = React.useCallback((e: React.ChangeEvent) => { + e.preventDefault(); + UiFramework.setColorTheme(e.target.value); + }, []); + + const onAutoHideChange = React.useCallback(async () => { + UiShowHideManager.autoHideUi = !UiShowHideManager.autoHideUi; + },[]); + + const onUseProximityOpacityChange = React.useCallback(async () => { + UiShowHideManager.useProximityOpacity = !UiShowHideManager.useProximityOpacity; + },[]); + + const onSnapWidgetOpacityChange = React.useCallback(async () => { + UiShowHideManager.snapWidgetOpacity = !UiShowHideManager.snapWidgetOpacity; + },[]); + + const onWidgetOpacityChange = React.useCallback(async (values: readonly number[]) => { + // istanbul ignore else + if (values.length > 0) { + UiFramework.setWidgetOpacity(values[0]); + } + },[]); + const onToggleFrameworkVersion = React.useCallback(async () => { + UiFramework.setUiVersion(UiFramework.uiVersion === "2"?"1":"2"); + },[]); + + const onToggleDragInteraction = React.useCallback(async () => { + UiFramework.setUseDragInteraction(!UiFramework.useDragInteraction); + },[]); + + const currentTheme = UiFramework.getColorTheme(); + + return ( +
+ +