@3cr/viewer-browser 0.0.162 → 0.0.195

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/.circleci/config.yml +8 -6
  2. package/__tests__/index.spec.ts +31 -24
  3. package/components.d.ts +32 -0
  4. package/dist/Viewer3CR.js +44 -27
  5. package/dist/Viewer3CR.mjs +22731 -17742
  6. package/dist/Viewer3CR.umd.js +44 -27
  7. package/index.html +5 -8
  8. package/index.ts +7 -2
  9. package/package.json +4 -2
  10. package/playground/index.html +6 -4
  11. package/src/App.vue +12 -4
  12. package/src/__tests__/app.spec.ts +2 -13
  13. package/src/assets/magic_wand.svg +24 -0
  14. package/src/components/WebGL3DR.vue +53 -92
  15. package/src/components/__tests__/webgl3dr.spec.ts +29 -48
  16. package/src/{demo → components/demo}/DemoModal.vue +1 -1
  17. package/src/{demo → components/demo}/DemoPatientModal.vue +4 -1
  18. package/src/components/demo/__tests__/DemoModal.spec.ts +25 -0
  19. package/src/components/demo/__tests__/DemoPatientModal.spec.ts +37 -0
  20. package/src/components/demo/__tests__/options.spec.ts +25 -0
  21. package/src/components/demo/licence/DemoLicenceEnableCloudStorageModal.vue +64 -0
  22. package/src/{demo → components/demo}/licence/DemoLicenceInfoModal.vue +5 -4
  23. package/src/{demo → components/demo}/licence/DemoLicenceSendToPartyModal.vue +6 -4
  24. package/src/{demo → components/demo}/licence/DemoLicenceShareToMobileModal.vue +8 -6
  25. package/src/components/demo/licence/__tests__/DemoLicenceEnableCloudStorageModal.spec.ts +37 -0
  26. package/src/components/demo/licence/__tests__/DemoLicenceInfoModal.spec.ts +37 -0
  27. package/src/components/demo/licence/__tests__/DemoLicenceSendToPartyModal.spec.ts +36 -0
  28. package/src/components/demo/licence/__tests__/DemoLicenceShareToMobileModal.spec.ts +51 -0
  29. package/src/{demo → components/demo}/options.ts +18 -20
  30. package/src/components/demo/patient/DemoPatientEnableCloudStorageModal.vue +64 -0
  31. package/src/{demo → components/demo}/patient/DemoPatientInfoModal.vue +5 -4
  32. package/src/{demo → components/demo}/patient/DemoPatientSendToPartyModal.vue +4 -3
  33. package/src/{demo → components/demo}/patient/DemoPatientShareToMobileModal.vue +8 -6
  34. package/src/components/demo/patient/__tests__/DemoPatientEnableCloudStorageModal.spec.ts +37 -0
  35. package/src/components/demo/patient/__tests__/DemoPatientInfoModal.spec.ts +37 -0
  36. package/src/components/demo/patient/__tests__/DemoPatientSendToPartyModal.spec.ts +36 -0
  37. package/src/components/demo/patient/__tests__/DemoPatientShareToMobileModal.spec.ts +51 -0
  38. package/src/components/loading/LoadingSpinner.vue +1 -1
  39. package/src/components/modal/ActionRail.vue +96 -0
  40. package/src/components/modal/AskAI.vue +250 -0
  41. package/src/components/modal/CloseViewerModal.vue +104 -0
  42. package/src/components/modal/MftpWebGL3DRModal.vue +415 -834
  43. package/src/components/modal/ViewerActionRail.vue +123 -0
  44. package/src/components/modal/ViewerAnnotationModal.vue +115 -0
  45. package/src/components/modal/ViewerAnnotations.vue +283 -0
  46. package/src/components/modal/ViewerDisplaySettings.vue +102 -0
  47. package/src/components/modal/ViewerNavigationDrawer.vue +90 -0
  48. package/src/components/modal/ViewerNavigationDrawerContent.vue +126 -0
  49. package/src/components/modal/ViewerNavigationDrawerFooter.vue +111 -0
  50. package/src/components/modal/ViewerNavigationDrawerHeader.vue +63 -0
  51. package/src/components/modal/__tests__/CloseViewerModal.spec.ts +60 -0
  52. package/src/components/modal/__tests__/{mftp-webgl-3dr-modal.spec.ts → MftpWebGL3DRModal.spec.ts} +47 -298
  53. package/src/components/modal/__tests__/ViewerAnnotationModal.spec.ts +79 -0
  54. package/src/components/modal/__tests__/ViewerDisplaySettings.spec.ts +61 -0
  55. package/src/components/modal/__tests__/ViewerNavigationDrawer.spec.ts +32 -0
  56. package/src/components/modal/__tests__/ViewerNavigationDrawerContent.spec.ts +29 -0
  57. package/src/components/modal/__tests__/ViewerNavigationDrawerFooter.spec.ts +43 -0
  58. package/src/components/modal/__tests__/ViewerNavigationDrawerHeader.spec.ts +37 -0
  59. package/src/components/modal/actions/Action.vue +40 -0
  60. package/src/components/modal/actions/Flip3dAction.vue +79 -0
  61. package/src/components/modal/actions/FlipHorizontalAction.vue +36 -0
  62. package/src/components/modal/actions/FlipVerticalAction.vue +36 -0
  63. package/src/components/modal/actions/FullscreenAction.vue +47 -0
  64. package/src/components/modal/actions/NavigationCubeAction.vue +33 -0
  65. package/src/components/modal/actions/PanAction.vue +78 -0
  66. package/src/components/modal/actions/ResetViewAction.vue +29 -0
  67. package/src/components/modal/actions/Rotate2dAction.vue +78 -0
  68. package/src/components/modal/actions/Slice3dAction.vue +71 -0
  69. package/src/components/modal/actions/ZoomAction.vue +70 -0
  70. package/src/components/modal/actions/__tests__/Action.spec.ts +29 -0
  71. package/src/components/modal/actions/__tests__/Flip3dAction.spec.ts +48 -0
  72. package/src/components/modal/actions/__tests__/FlipHorizontalAction.spec.ts +17 -0
  73. package/src/components/modal/actions/__tests__/FlipVerticalAction.spec.ts +17 -0
  74. package/src/components/modal/actions/__tests__/FullscreenAction.spec.ts +28 -0
  75. package/src/components/modal/actions/__tests__/NavigationCubeAction.spec.ts +25 -0
  76. package/src/components/modal/actions/__tests__/PanAction.spec.ts +46 -0
  77. package/src/components/modal/actions/__tests__/ResetViewAction.spec.ts +17 -0
  78. package/src/components/modal/actions/__tests__/Rotate2dAction.spec.ts +23 -0
  79. package/src/components/modal/actions/__tests__/Slice3dAction.spec.ts +14 -0
  80. package/src/components/modal/actions/__tests__/ZoomAction.spec.ts +34 -0
  81. package/src/components/modal/composables/__tests__/useNavigationCubeObserver.spec.ts +56 -0
  82. package/src/components/modal/composables/useEventListener.ts +22 -0
  83. package/src/components/modal/composables/useNavigationCubeObserver.ts +104 -0
  84. package/src/components/selectors/ValueSelector.vue +30 -33
  85. package/src/components/selectors/__tests__/value-selector.spec.ts +1 -1
  86. package/src/components/sliders/DoubleSliderSelector.vue +79 -71
  87. package/src/components/sliders/VerticalSliderSelector.vue +12 -17
  88. package/src/components/sliders/__tests__/double-slider-selector.spec.ts +1 -1
  89. package/src/components/sliders/__tests__/vertical-slider-selector.spec.ts +1 -1
  90. package/src/dataLayer/__tests__/clamp.spec.ts +16 -0
  91. package/src/dataLayer/__tests__/eventHandlers.spec.ts +38 -0
  92. package/src/dataLayer/__tests__/getIconForPreset.spec.ts +40 -0
  93. package/src/dataLayer/__tests__/patchDataOverlay.spec.ts +88 -0
  94. package/src/dataLayer/__tests__/scanState.spec.ts +93 -0
  95. package/src/dataLayer/__tests__/useViewer3cr.spec.ts +10 -0
  96. package/src/dataLayer/__tests__/viewer3cr.spec.ts +331 -0
  97. package/src/dataLayer/clamp.ts +9 -0
  98. package/src/dataLayer/eventHandlers.ts +26 -0
  99. package/src/dataLayer/patchDataOverlay.ts +101 -0
  100. package/src/dataLayer/scanState.ts +105 -26
  101. package/src/dataLayer/useViewer3cr.ts +7 -0
  102. package/src/dataLayer/viewer3cr.ts +410 -0
  103. package/src/helpers/__tests__/layout-overlay-style.spec.ts +24 -22
  104. package/src/helpers/__tests__/model-helper.spec.ts +44 -13
  105. package/src/helpers/layoutOverlayStyle.ts +16 -27
  106. package/src/helpers/modelHelper.ts +62 -10
  107. package/src/models/Callbacks.ts +2 -2
  108. package/src/models/LoadViewerOptions.ts +2 -0
  109. package/src/models/LoadViewerPayload.ts +1 -0
  110. package/src/notifications/notification.ts +3 -4
  111. package/src/plugins/vuetify.ts +5 -0
  112. package/src/services/gpt/__tests__/gpt.service.spec.ts +27 -0
  113. package/src/services/gpt/gpt.service.ts +27 -0
  114. package/static/3cr-types-browser/index.ts +74 -0
  115. package/static/3cr-types-browser/types/Action.ts +6 -0
  116. package/static/3cr-types-browser/types/ActionData.ts +4 -0
  117. package/static/3cr-types-browser/types/AlphaKeys.ts +5 -0
  118. package/static/3cr-types-browser/types/AnchorPoint.ts +12 -0
  119. package/static/3cr-types-browser/types/CallToAction.ts +5 -0
  120. package/static/3cr-types-browser/types/ColourData.ts +7 -0
  121. package/static/3cr-types-browser/types/ColourPresetData.ts +9 -0
  122. package/static/3cr-types-browser/types/CurrentDataOverlayState.ts +6 -0
  123. package/static/3cr-types-browser/types/CurrentScanState.ts +22 -0
  124. package/static/3cr-types-browser/types/DataOverlay.ts +22 -0
  125. package/static/3cr-types-browser/types/DataOverlayActions.ts +14 -0
  126. package/static/3cr-types-browser/types/DataOverlayData.ts +8 -0
  127. package/static/3cr-types-browser/types/DataOverlayEvent.ts +8 -0
  128. package/static/3cr-types-browser/types/DecryptionKey.ts +4 -0
  129. package/static/3cr-types-browser/types/DisplaySettings.ts +10 -0
  130. package/static/3cr-types-browser/types/EmptyPayload.ts +3 -0
  131. package/static/3cr-types-browser/types/EnumPayload.ts +4 -0
  132. package/static/3cr-types-browser/types/FileManagementActions.ts +11 -0
  133. package/static/3cr-types-browser/types/FlipValue.ts +7 -0
  134. package/static/3cr-types-browser/types/FrontEndInterfaces.ts +14 -0
  135. package/static/3cr-types-browser/types/GradientKeys.ts +7 -0
  136. package/static/3cr-types-browser/types/GreyscalePresetData.ts +6 -0
  137. package/static/3cr-types-browser/types/InitialDataOverlayState.ts +6 -0
  138. package/static/3cr-types-browser/types/InitialScanState.ts +19 -0
  139. package/static/3cr-types-browser/types/InteractionType.ts +8 -0
  140. package/static/3cr-types-browser/types/InteractivityActions.ts +6 -0
  141. package/static/3cr-types-browser/types/InteractivityState.ts +4 -0
  142. package/static/3cr-types-browser/types/InvertTransformData.ts +6 -0
  143. package/static/3cr-types-browser/types/LayoutActions.ts +6 -0
  144. package/static/3cr-types-browser/types/LayoutData.ts +7 -0
  145. package/static/3cr-types-browser/types/LoadDataSet.ts +6 -0
  146. package/static/3cr-types-browser/types/LoadSessionState.ts +4 -0
  147. package/static/3cr-types-browser/types/LocalLoadDataset.ts +3 -0
  148. package/static/3cr-types-browser/types/MovementData.ts +7 -0
  149. package/static/3cr-types-browser/types/NavigationCubeActions.ts +8 -0
  150. package/static/3cr-types-browser/types/NavigationCubeData.ts +12 -0
  151. package/static/3cr-types-browser/types/NavigationCubeTransform.ts +9 -0
  152. package/static/3cr-types-browser/types/NotificationPayload.ts +7 -0
  153. package/static/3cr-types-browser/types/NotificationsActions.ts +6 -0
  154. package/static/3cr-types-browser/types/Object.ts +1 -0
  155. package/static/3cr-types-browser/types/ObjectColour.ts +7 -0
  156. package/static/3cr-types-browser/types/ObjectIcon.ts +5 -0
  157. package/static/3cr-types-browser/types/ObjectInvert.ts +7 -0
  158. package/static/3cr-types-browser/types/ObjectSize.ts +7 -0
  159. package/static/3cr-types-browser/types/ObjectSize2D.ts +7 -0
  160. package/static/3cr-types-browser/types/ObjectVisible.ts +5 -0
  161. package/static/3cr-types-browser/types/PositionData.ts +14 -0
  162. package/static/3cr-types-browser/types/PresetsActions.ts +4 -0
  163. package/static/3cr-types-browser/types/RotationValue.ts +7 -0
  164. package/static/3cr-types-browser/types/ScanMovementActions.ts +27 -0
  165. package/static/3cr-types-browser/types/ScanMovementData.ts +3 -0
  166. package/static/3cr-types-browser/types/ScanOrientationActions.ts +6 -0
  167. package/static/3cr-types-browser/types/ScanStateActions.ts +4 -0
  168. package/static/3cr-types-browser/types/ScanView.ts +6 -0
  169. package/static/3cr-types-browser/types/SettingsData.ts +12 -0
  170. package/static/3cr-types-browser/types/SlicerData.ts +9 -0
  171. package/static/3cr-types-browser/types/SliderValue.ts +4 -0
  172. package/static/3cr-types-browser/types/SlidersActions.ts +18 -0
  173. package/static/3cr-types-browser/types/Vector2Data.ts +5 -0
  174. package/static/3cr-types-browser/types/Vector3Data.ts +6 -0
  175. package/static/3cr-types-browser/types/VectorMovementData.ts +8 -0
  176. package/static/3cr-types-browser/types/ViewInteractiveMode.ts +5 -0
  177. package/static/3cr-types-browser/types/ViewOrientation.ts +8 -0
  178. package/static/3cr-types-browser/types/ViewOrientations.ts +10 -0
  179. package/static/3cr-types-browser/types/ViewSelectionActions.ts +9 -0
  180. package/static/3cr-types-browser/types/ViewToggleData.ts +7 -0
  181. package/static/3cr-types-browser/types/VolumeOrientation.ts +7 -0
  182. package/test/helper.ts +10 -1
  183. package/test/setup.ts +13 -0
  184. package/tsconfig.json +1 -0
  185. package/vite.config.mts +1 -0
  186. package/src/dataLayer/__tests__/payload-handler.spec.ts +0 -214
  187. package/src/dataLayer/payloadHandler.ts +0 -138
  188. /package/src/dataLayer/{iconData.ts → getIconForPreset.ts} +0 -0
package/index.html CHANGED
@@ -18,24 +18,21 @@
18
18
  import {registerViewer, loadViewer} from './index.ts';
19
19
 
20
20
  const { StatsigClient, SessionReplay, runStatsigAutoCapture, runStatsigSessionReplay } = window.Statsig;
21
+
21
22
  const client = new StatsigClient(
22
23
  "client-YaAjxOTTRbuSN3peDAwXEzErieXpI744a3OHDgonzvV",
23
24
  { userID: undefined },
24
- {environment: {tier: 'production'}}
25
+ {environment: {tier: 'development'}}
25
26
  );
26
-
27
- // const replayer = new SessionReplay(client);
28
- // console.log(replayer)
27
+ client.initializeSync()
29
28
 
30
29
  runStatsigAutoCapture(client);
31
30
  window.playgroundStatsigClient = client
32
- // runStatsigSessionReplay(client);
33
-
34
31
 
35
32
 
36
- const dynamicConfig = window.playgroundStatsigClient.getDynamicConfig("3cr_viewer_version");
33
+ const dynamicConfig = client.getDynamicConfig("3cr_viewer_version");
37
34
  const testVersion = (window.location.href.includes('test.') || window.location.href.includes('localhost')) && new URLSearchParams(window.location.search).get("build_version")
38
- const version = testVersion ?? dynamicConfig.value["sdkVersion"] ?? "1.0.0_381";
35
+ const version = testVersion ?? dynamicConfig.value["sdkVersion"] ?? "1.1.0";
39
36
 
40
37
  client.initializeAsync().then(() => {
41
38
  registerViewer(version).then(() => {
package/index.ts CHANGED
@@ -57,11 +57,16 @@ export async function loadViewer(
57
57
  await (mountedApp as any).loadInstance(payload, options);
58
58
  }
59
59
 
60
+ export async function loadSession(url: string): Promise<void> {
61
+ if (!mountedApp) {
62
+ throw new Error("Please call `registerViewer(version: string)` first");
63
+ }
64
+ await (mountedApp as any).loadSession(url);
65
+ }
66
+
60
67
  export async function ejectViewer() {
61
- console.log("Ejected Viewer");
62
68
  const modal = document.getElementById(modalId);
63
69
  const styleSheet = document.getElementById(injectedStyleId);
64
-
65
70
  modal?.remove();
66
71
  styleSheet?.remove();
67
72
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@3cr/viewer-browser",
3
- "version": "0.0.162",
3
+ "version": "0.0.195",
4
4
  "main": "./dist/Viewer3CR.umd.js",
5
5
  "module": "dist/Viewer3CR.umd.js",
6
6
  "homepage": "https://docs.3cr.singular.health",
@@ -22,10 +22,12 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@3cr/sdk-browser": "^1.0.13",
25
- "@3cr/types-ts": "^1.0.9",
25
+ "@3cr/types-ts": "file:static/3cr-types-browser",
26
26
  "@kyvg/vue3-notification": "^3.2.1",
27
27
  "@mdi/font": "6.2.95",
28
28
  "@mdi/js": "^7.4.47",
29
+ "axios": "^1.7.2",
30
+ "intro.js": "^7.2.0",
29
31
  "logrocket": "^8.1.0",
30
32
  "roboto-fontface": "*",
31
33
  "vue": "^3.4.21",
@@ -80,17 +80,19 @@
80
80
  <script type="module">
81
81
 
82
82
  const { StatsigClient, runStatsigAutoCapture } = window.Statsig;
83
+ const isTest = (window.location.href.includes('test.') || window.location.href.includes('localhost'))
83
84
  const client = new StatsigClient(
84
85
  "client-YaAjxOTTRbuSN3peDAwXEzErieXpI744a3OHDgonzvV",
85
86
  { userID: undefined },
86
- {environment: {tier: 'production'}}
87
+ {environment: {tier: isTest ? 'development' : 'production'}}
87
88
  );
89
+ client.initializeSync()
88
90
  runStatsigAutoCapture(client);
89
91
  window.playgroundStatsigClient = client
90
92
 
91
- const dynamicConfig = window.playgroundStatsigClient.getDynamicConfig("3cr_viewer_version");
92
- const testVersion = (window.location.href.includes('test.') || window.location.href.includes('localhost')) && new URLSearchParams(window.location.search).get("build_version")
93
- const version = testVersion ?? dynamicConfig.value["sdkVersion"] ?? "1.0.0_381";
93
+ const dynamicConfig = client.getDynamicConfig("3cr_viewer_version");
94
+ const testVersion = (isTest) && new URLSearchParams(window.location.search).get("build_version")
95
+ const version = testVersion || dynamicConfig.value["sdkVersion"] ?? "1.1.0";
94
96
 
95
97
  client.initializeAsync().then(() => {
96
98
  window.registerViewer(version).then(() => {
package/src/App.vue CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  defaultLoadViewerOptions,
21
21
  LoadViewerOptions,
22
22
  } from "@/models/LoadViewerOptions";
23
- import { isDemo } from "@/demo/options";
23
+ import { isDemo } from "@/components/demo/options";
24
24
  import LogRocket from "logrocket";
25
25
 
26
26
  if (isDemo.value) {
@@ -34,12 +34,10 @@ const payload = ref<LoadViewerPayload>({
34
34
  Key: "KUc722X1y4w42M+jCf9a3+6EGz66z7UMWK3m2aMqGxM=",
35
35
  },
36
36
  });
37
+
37
38
  const options = ref<LoadViewerOptions>(defaultLoadViewerOptions);
38
39
 
39
40
  const mftpWebGL3DRModal = ref<typeof MftpWebGL3DRModal | null>(null);
40
- defineExpose({
41
- loadInstance,
42
- });
43
41
 
44
42
  async function loadInstance(
45
43
  payloadIncoming: LoadViewerPayload | undefined,
@@ -51,7 +49,17 @@ async function loadInstance(
51
49
  await nextTick();
52
50
  unref(mftpWebGL3DRModal)?.alterValue(true);
53
51
  }
52
+
53
+ async function loadSession(url: string): Promise<void> {
54
+ await unref(mftpWebGL3DRModal)?.loadSession(url);
55
+ }
56
+
57
+ defineExpose({
58
+ loadInstance,
59
+ loadSession,
60
+ });
54
61
  </script>
62
+
55
63
  <style>
56
64
  .material-icons-outlined,
57
65
  .material-symbols-outlined {
@@ -1,25 +1,14 @@
1
- import { expect, describe, it, vi } from "vitest";
1
+ import { expect, describe, it } from "vitest";
2
2
  import { shallowMountVuetify } from "~/helper";
3
3
  import App from "@/App.vue";
4
4
 
5
5
  const wrapper = shallowMountVuetify(App);
6
6
 
7
- vi.mock("@3cr/sdk-browser", async (importOriginal) => {
8
- const mod = (await importOriginal()) as object;
9
- return {
10
- ...mod,
11
- // replace some exports
12
- registerVersion: vi.fn(),
13
- createInstance: vi.fn(),
14
- executePayload: vi.fn(),
15
- registerOnPayloadHandler: vi.fn(),
16
- };
17
- });
18
-
19
7
  describe("App.vue", () => {
20
8
  it("should inflate component", () => {
21
9
  expect(wrapper).toBeTruthy();
22
10
  });
11
+
23
12
  it("should loadScan", async () => {
24
13
  await wrapper.vm.loadInstance(undefined, {});
25
14
  expect(wrapper).toBeTruthy();
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+
3
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
5
+ <svg width="800px" height="800px" viewBox="-3 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
6
+ <g id="icomoon-ignore">
7
+ </g>
8
+ <path d="M-0.007 28.236l13.916-13.916 0.754 0.754-13.916 13.916-0.754-0.754z" fill="#000000">
9
+
10
+ <path d="M9.973 10.453h4.267v1.067h-4.267v-1.067z" fill="#000000">
11
+
12
+ <path d="M21.707 10.453h4.267v1.067h-4.267v-1.067z" fill="#000000">
13
+
14
+ <path d="M17.44 14.72h1.067v4.267h-1.067v-4.267z" fill="#000000">
15
+
16
+ <path d="M17.44 2.987h1.067v4.267h-1.067v-4.267z" fill="#000000">
17
+
18
+ <path d="M23.991 5.717l-3.017 3.017-0.754-0.754 3.017-3.017 0.754 0.754z" fill="#000000">
19
+
20
+ <path d="M23.246 17.042l-3.017-3.017 0.754-0.754 3.017 3.017-0.754 0.754z" fill="#000000">
21
+
22
+ <path d="M14.986 8.741l-3.017-3.017 0.754-0.754 3.017 3.017-0.754 0.754z" fill="#000000">
23
+
24
+ </svg>
@@ -1,82 +1,46 @@
1
1
  <template>
2
- <div
3
- id="screenshotWindow"
4
- class=""
5
- @mouseover="emit('hover', true)"
6
- @mouseout="emit('hover', false)"
7
- >
2
+ <div id="webgl-container">
8
3
  <canvas
9
- id="unity-canvas"
4
+ id="canvas"
5
+ ref="canvas"
10
6
  width="100%"
11
- height="calc(100vh - 48px)"
7
+ height="100%"
12
8
  tabindex="-1"
13
- style="
14
- width: 100%;
15
- height: calc(100vh - 50px);
16
- background-size: cover !important;
17
- "
18
9
  >
19
10
  </canvas>
20
- <div id="parent-canvas" ref="parent_canvas">
11
+ <div id="overlay" ref="overlay">
21
12
  <slot></slot>
22
13
  </div>
23
14
  </div>
24
15
  </template>
25
- <script setup lang="ts">
26
- import { defineEmits, nextTick, onMounted, ref } from "vue";
27
16
 
28
- import {
29
- createInstance,
30
- executePayload,
31
- registerOnPayloadHandler,
32
- } from "@3cr/sdk-browser";
33
- import {
34
- FrontEndInterfaces,
35
- FrontEndPayload,
36
- } from "@3cr/sdk-browser/types/payload";
17
+ <script setup lang="ts">
18
+ import { onMounted, ref } from "vue";
19
+ import { useViewer3cr } from '@/dataLayer/useViewer3cr';
37
20
 
38
- const unityInstance = ref<any>(null);
21
+ const viewer3cr = useViewer3cr();
22
+ const canvas = ref<HTMLCanvasElement>();
23
+ const overlay = ref<HTMLDivElement>();
39
24
 
40
25
  const emit = defineEmits<{
41
26
  instance_loaded: [void];
42
- on_payload: [FrontEndInterfaces, string, string];
43
- hover: [boolean];
44
27
  }>();
45
28
 
46
- defineExpose({
47
- sendPayload,
48
- receiveMessageFromUnity,
49
- });
50
-
51
29
  onMounted(async () => {
52
- await nextTick();
53
- const canvas = document.querySelector("#unity-canvas") as HTMLCanvasElement;
54
-
55
- await registerOnPayloadHandler(receiveMessageFromUnity);
30
+ if (!canvas.value || !overlay.value) {
31
+ throw new Error('One or more canvases failed to load');
32
+ }
56
33
 
57
34
  /* c8 ignore start */
58
35
  if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) {
59
36
  // Mobile device style: fill the whole browser client area with the game canvas:
60
37
  const meta = document.createElement("meta");
61
38
  meta.name = "viewport";
62
- meta.content =
63
- "width=device-width, height=device-height, initial-scale=1.0, user-scalable=no, shrink-to-fit=yes";
39
+ meta.content = "width=device-width, height=device-height, initial-scale=1.0, user-scalable=no, shrink-to-fit=yes";
64
40
  document.getElementsByTagName("head")[0].appendChild(meta);
65
-
66
- canvas.style.width = "100%";
67
- canvas.style.height = "80vh";
68
- canvas.style.position = "fixed";
69
-
70
- document.body.style.textAlign = "left";
71
41
  }
72
- /* c8 ignore stop */
73
- await nextTick();
74
- unityInstance.value = await createInstance(canvas);
75
-
76
- await nextTick();
77
42
 
78
- /* c8 ignore start */
79
- if (navigator.storage) {
43
+ if (navigator.storage && navigator.storage.estimate) {
80
44
  const quota = await navigator.storage.estimate();
81
45
  if (quota) {
82
46
  const percentageUsed = (quota.usage! / quota.quota!) * 100;
@@ -87,55 +51,52 @@ onMounted(async () => {
87
51
  }
88
52
  /* c8 ignore stop */
89
53
 
90
- emit("instance_loaded");
91
54
  // Overlay scroll events to the container instance
92
- const fixedDiv = document.getElementById("parent-canvas");
93
55
  /* c8 ignore start */
94
- fixedDiv?.addEventListener("wheel", function (e: WheelEvent) {
95
- e.preventDefault();
96
- const evt = new WheelEvent("wheel", {
97
- ...e,
98
- deltaY: e.deltaY,
99
- });
100
- canvas.dispatchEvent(evt);
101
- });
102
- function injectEventListener(eventType: keyof HTMLElementEventMap) {
103
- fixedDiv?.addEventListener(eventType, function (e: Event) {
56
+ function injectEventListener(type: keyof HTMLElementEventMap) {
57
+ overlay.value!.addEventListener(type, (e: Event) => {
104
58
  e.preventDefault();
105
- const evt = new MouseEvent(eventType, {
106
- ...e,
107
- });
108
- canvas.dispatchEvent(evt);
59
+ canvas.value!.dispatchEvent(new MouseEvent(type, e));
109
60
  });
110
61
  }
111
- injectEventListener("mousedown");
112
- injectEventListener("mouseup");
113
- injectEventListener("mouseout");
114
- injectEventListener("mouseover");
115
- injectEventListener("mouseenter");
116
- injectEventListener("mouseleave");
117
- injectEventListener("mousemove");
118
- injectEventListener("contextmenu");
119
- injectEventListener("dblclick");
120
62
 
121
- fixedDiv?.addEventListener(
122
- "*",
123
- function (ev) {
124
- console.log(`${ev.type}`);
125
- ev.preventDefault();
126
- const evt = new Event(ev.type, { ...ev });
127
- canvas.dispatchEvent(evt);
128
- }
129
- // false
130
- );
63
+ overlay.value!.addEventListener("wheel", (e: WheelEvent) => {
64
+ e.preventDefault();
65
+ canvas.value!.dispatchEvent(new WheelEvent(e.type, e));
66
+ });
67
+
68
+ injectEventListener('click');
69
+ injectEventListener('dblclick');
70
+ injectEventListener('auxclick');
71
+ injectEventListener('mousedown');
72
+ injectEventListener('mousemove');
73
+ injectEventListener('mouseup');
74
+ injectEventListener('mouseout');
75
+ injectEventListener('mouseover');
76
+ injectEventListener('mouseenter');
77
+ injectEventListener('mouseleave');
78
+ injectEventListener('contextmenu');
79
+
80
+ await viewer3cr.register(canvas.value);
81
+ emit("instance_loaded");
131
82
 
132
83
  /* c8 ignore stop */
133
84
  });
134
85
 
135
- async function sendPayload(payload: string) {
136
- await executePayload(JSON.parse(payload));
86
+ defineExpose({ canvas });
87
+ </script>
88
+
89
+ <style scoped lang="scss">
90
+ #webgl-container {
91
+ position: relative;
92
+ width: 100%;
93
+ height: 100%;
137
94
  }
138
- function receiveMessageFromUnity(json: FrontEndPayload) {
139
- emit("on_payload", json.Interface, json.Action, json.Message);
95
+
96
+ #canvas, #overlay {
97
+ position: absolute;
98
+ inset: 0;
99
+ width: 100%;
100
+ height: 100%;
140
101
  }
141
- </script>
102
+ </style>
@@ -1,56 +1,37 @@
1
- import { expect, test, vi } from "vitest";
2
- import WebGL3DR from "../WebGL3DR.vue";
1
+ import { expect, describe, it } from "vitest";
3
2
  import { registerOnPayloadHandler, createInstance } from "@3cr/sdk-browser";
4
3
  import { mountVuetify } from "~/helper";
5
- import { FileManagementActions, FrontEndInterfaces } from "@3cr/types-ts";
6
-
7
- vi.mock("@3cr/sdk-browser", async (importOriginal) => {
8
- const mod = (await importOriginal()) as object;
9
- return {
10
- ...mod,
11
- registerVersion: vi.fn(),
12
- createInstance: vi.fn(),
13
- executePayload: vi.fn(),
14
- registerOnPayloadHandler: vi.fn(),
15
- };
16
- });
17
-
18
- const wrapper = mountVuetify(WebGL3DR);
4
+ import { nextTick } from "vue";
5
+ import WebGL3DR from "../WebGL3DR.vue";
19
6
 
20
- test("registers payload handler", () => {
21
- expect(registerOnPayloadHandler).toHaveBeenCalled();
22
- });
23
- test("creates instance", () => {
24
- expect(createInstance).toHaveBeenCalled();
25
- });
26
- test("creates instance", () => {
27
- wrapper.vm.receiveMessageFromUnity({
28
- Action: FileManagementActions.fm01 as any,
29
- Message: "testing message",
30
- Interface: FrontEndInterfaces.file_management as any,
7
+ describe('WebGL3DR spec', () => {
8
+ it("creates instance", async () => {
9
+ mountVuetify(WebGL3DR);
10
+ await nextTick();
11
+ expect(createInstance).toHaveBeenCalled();
12
+ expect(registerOnPayloadHandler).toHaveBeenCalled();
31
13
  });
32
14
 
33
- expect(wrapper.emitted()["on_payload"]).toStrictEqual([
34
- ["file_management", "fm_01", "testing message"],
35
- ]);
36
- });
37
-
38
- test("scrolling", () => {
39
- const parentCanvas = wrapper.find("#parent-canvas");
40
- parentCanvas.trigger("wheel");
41
- });
42
-
43
- test("mouseover", () => {
44
- const parentCanvas = wrapper.find("#screenshotWindow");
45
- parentCanvas.trigger("mouseover");
46
-
47
- expect(wrapper.emitted()["hover"]).toStrictEqual([[true]]);
48
- });
15
+ it("should emit wheel event", () => {
16
+ const wrapper = mountVuetify(WebGL3DR);
17
+ const overlay = wrapper.find("#overlay");
18
+ overlay.trigger("wheel");
19
+ expect(wrapper.emitted()["wheel"]).toBeTruthy();
20
+ });
49
21
 
50
- test("mouseout", () => {
51
- const parentCanvas = wrapper.find("#screenshotWindow");
52
- parentCanvas.trigger("mouseover");
53
- parentCanvas.trigger("mouseout");
22
+ it("should emit mouseover event", () => {
23
+ const wrapper = mountVuetify(WebGL3DR);
24
+ const overlay = wrapper.find("#overlay");
25
+ overlay.trigger("mouseover");
26
+ expect(wrapper.emitted()["mouseover"]).toBeTruthy();
27
+ });
54
28
 
55
- expect(wrapper.emitted()["hover"]).toStrictEqual([[true], [true], [false]]);
29
+ it("should emit mouseout event", () => {
30
+ const wrapper = mountVuetify(WebGL3DR);
31
+ const overlay = wrapper.find("#overlay");
32
+ overlay.trigger("mouseover");
33
+ overlay.trigger("mouseout");
34
+ expect(wrapper.emitted()["mouseover"]).toBeTruthy();
35
+ expect(wrapper.emitted()["mouseout"]).toBeTruthy();
36
+ });
56
37
  });
@@ -37,7 +37,7 @@ const modalState = computed({
37
37
  <v-card-text> support@singular.health </v-card-text>
38
38
  <v-card-actions>
39
39
  <v-spacer />
40
- <v-btn color="primary" @click="modalState = false"> Thanks! </v-btn>
40
+ <v-btn data-testid="close" color="primary" @click="modalState = false"> Thanks! </v-btn>
41
41
  </v-card-actions>
42
42
  </v-card>
43
43
  </v-dialog>
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { computed } from "vue";
3
+ import {PRICING_URL} from "@/components/demo/options";
3
4
 
4
5
  export interface Props {
5
6
  modal: boolean;
@@ -39,6 +40,7 @@ function openUrl(url: string) {
39
40
  </v-card-text>
40
41
  <v-card-actions>
41
42
  <v-btn
43
+ data-testid="close"
42
44
  color="error"
43
45
  @click="modalState = false"
44
46
  :disabled="!isModalOpen"
@@ -47,9 +49,10 @@ function openUrl(url: string) {
47
49
  </v-btn>
48
50
  <v-spacer />
49
51
  <v-btn
52
+ data-testid="select-plan"
50
53
  variant="tonal"
51
54
  color="success"
52
- @click="openUrl('https://3dicomviewer.com/pricing')"
55
+ @click="openUrl(PRICING_URL)"
53
56
  >
54
57
  Select a Plan
55
58
  </v-btn>
@@ -0,0 +1,25 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {mountVuetify, openAllModals} from "~/helper";
3
+ import DemoModal from "@/components/demo/DemoModal.vue";
4
+
5
+ describe('DemoModal tests', () => {
6
+ it('should mount', () => {
7
+ const props = { modal: true };
8
+ const wrapper = mountVuetify(DemoModal, props);
9
+ expect(wrapper).toBeTruthy();
10
+ });
11
+
12
+ it('should close', async () => {
13
+ const props = { modal: true };
14
+ const wrapper = mountVuetify(DemoModal, props);
15
+ const button = wrapper.findComponent('[data-testid="close"]');
16
+ await button.trigger('click');
17
+ expect(wrapper.emitted()['update:modal']).toBeTruthy();
18
+ });
19
+
20
+ it('should open via input', async () => {
21
+ const props = { modal: false };
22
+ const wrapper = mountVuetify(DemoModal, props);
23
+ await openAllModals(wrapper);
24
+ });
25
+ });
@@ -0,0 +1,37 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { mountVuetify, openAllModals } from "~/helper";
3
+ import { PRICING_URL } from "@/components/demo/options";
4
+ import DemoPatientModal from "@/components/demo/DemoPatientModal.vue";
5
+
6
+ describe('DemoPatientModal tests', () => {
7
+ it('should mount', () => {
8
+ const props = { modal: true, isModalOpen: true };
9
+ const wrapper = mountVuetify(DemoPatientModal, props);
10
+ expect(wrapper).toBeTruthy();
11
+ });
12
+
13
+ it('should close', async () => {
14
+ const props = { modal: true, isModalOpen: true };
15
+ const wrapper = mountVuetify(DemoPatientModal, props);
16
+ const button = wrapper.findComponent('[data-testid="close"]');
17
+ await button.trigger('click');
18
+ expect(wrapper.emitted()['update:modal']).toBeTruthy();
19
+ });
20
+
21
+ it('should open pricing url', async () => {
22
+ vi.stubGlobal('open', vi.fn());
23
+ const spy = vi.spyOn(window, 'open');
24
+ const props = { modal: true, isModalOpen: true };
25
+ const wrapper = mountVuetify(DemoPatientModal, props);
26
+ const button = wrapper.findComponent('[data-testid="select-plan"]');
27
+ await button.trigger('click');
28
+ expect(spy).toHaveBeenCalledWith(PRICING_URL, '_self');
29
+ vi.restoreAllMocks();
30
+ });
31
+
32
+ it('should open via input', async () => {
33
+ const props = { modal: false, isModalOpen: true };
34
+ const wrapper = mountVuetify(DemoPatientModal, props);
35
+ await openAllModals(wrapper);
36
+ });
37
+ });
@@ -0,0 +1,25 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { demoLicenceOptions, demoPatientOptions } from "@/components/demo/options";
3
+ import { LoadViewerOptions } from "@/models/LoadViewerOptions";
4
+
5
+ describe('options tests', () => {
6
+ it('should call demo license options', () => {
7
+ Object.keys(demoLicenceOptions).forEach(key => {
8
+ const k = key as keyof LoadViewerOptions;
9
+ const fn = demoLicenceOptions[k];
10
+ if (fn) {
11
+ expect(fn).not.toThrow();
12
+ }
13
+ });
14
+ });
15
+
16
+ it('should call demo patient options', () => {
17
+ Object.keys(demoPatientOptions).forEach(key => {
18
+ const k = key as keyof LoadViewerOptions;
19
+ const fn = demoPatientOptions[k];
20
+ if (fn) {
21
+ expect(fn).not.toThrow();
22
+ }
23
+ });
24
+ });
25
+ });
@@ -0,0 +1,64 @@
1
+ <template>
2
+ <v-dialog width="600" v-model:model-value="modalState">
3
+ <v-card theme="dark" class="motif-background">
4
+ <v-card-title class="text-center font-weight-bold">
5
+ Enable Cloud Storage
6
+ </v-card-title>
7
+ <v-card-text>
8
+ <p style="text-align: center;">Enable annual storage for just <b>5 credits</b>.</p>
9
+ <br />
10
+ <div>
11
+ <v-row no-gutters v-for="(benefit, idx) in benefits" :key="idx">
12
+ <v-col class="d-flex justify-end" cols="2">
13
+ <v-icon class="mr-2" color="success" icon="check" />
14
+ </v-col>
15
+ <v-col>
16
+ {{ benefit }}
17
+ </v-col>
18
+ </v-row>
19
+ </div>
20
+ </v-card-text>
21
+ <v-card-actions>
22
+ <v-btn data-testid="close" color="red" @click="modalState = false">
23
+ Continue with Demo
24
+ </v-btn>
25
+ <v-spacer />
26
+ <v-btn data-testid="confirm" variant="flat" color="success" @click="openUrl(PRICING_URL)">
27
+ Enable Cloud Storage
28
+ </v-btn>
29
+ </v-card-actions>
30
+ </v-card>
31
+ </v-dialog>
32
+ </template>
33
+
34
+ <script setup lang="ts">
35
+ import { computed } from 'vue';
36
+ import { PRICING_URL, openUrl } from '@/components/demo/options';
37
+
38
+ interface Props {
39
+ modal?: boolean;
40
+ }
41
+
42
+ type Emits = {
43
+ 'update:modal': [value: boolean];
44
+ };
45
+
46
+ const props = defineProps<Props>();
47
+
48
+ const emit = defineEmits<Emits>();
49
+
50
+ const benefits = [
51
+ 'Secure storage',
52
+ 'Access your scan from any device',
53
+ 'Keep your medical imaging records at your fingertips'
54
+ ];
55
+
56
+ const modalState = computed({
57
+ get(): boolean {
58
+ return props.modal;
59
+ },
60
+ set(value: boolean): void {
61
+ emit('update:modal', value);
62
+ }
63
+ });
64
+ </script>