@3cr/viewer-browser 0.0.53 → 0.0.57

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 (72) hide show
  1. package/__tests__/index.spec.ts +31 -0
  2. package/components.d.ts +3 -2
  3. package/coverage/3cr-viewer-browser/index.html +116 -0
  4. package/coverage/3cr-viewer-browser/index.ts.html +211 -0
  5. package/coverage/3cr-viewer-browser/src/App.vue.html +313 -0
  6. package/coverage/3cr-viewer-browser/src/components/WebGL3DR.vue.html +442 -0
  7. package/coverage/3cr-viewer-browser/src/components/icons/index.html +116 -0
  8. package/coverage/3cr-viewer-browser/src/components/icons/liver.vue.html +148 -0
  9. package/coverage/3cr-viewer-browser/src/components/index.html +116 -0
  10. package/coverage/3cr-viewer-browser/src/components/loading/LoadingSpinner.vue.html +556 -0
  11. package/coverage/3cr-viewer-browser/src/components/loading/index.html +116 -0
  12. package/coverage/3cr-viewer-browser/src/components/modal/MftpWebGL3DRModal.vue.html +4126 -0
  13. package/coverage/3cr-viewer-browser/src/components/modal/index.html +116 -0
  14. package/coverage/3cr-viewer-browser/src/components/selectors/ValueSelector.vue.html +331 -0
  15. package/coverage/3cr-viewer-browser/src/components/selectors/index.html +116 -0
  16. package/coverage/3cr-viewer-browser/src/components/sliders/DoubleSliderSelector.vue.html +445 -0
  17. package/coverage/3cr-viewer-browser/src/components/sliders/VerticalSliderSelector.vue.html +349 -0
  18. package/coverage/3cr-viewer-browser/src/components/sliders/index.html +131 -0
  19. package/coverage/3cr-viewer-browser/src/helpers/index.html +146 -0
  20. package/coverage/3cr-viewer-browser/src/helpers/layoutOverlayStyle.ts.html +406 -0
  21. package/coverage/3cr-viewer-browser/src/helpers/modelHelper.ts.html +412 -0
  22. package/coverage/3cr-viewer-browser/src/helpers/utils.ts.html +133 -0
  23. package/coverage/3cr-viewer-browser/src/index.html +131 -0
  24. package/coverage/3cr-viewer-browser/src/main.ts.html +124 -0
  25. package/coverage/3cr-viewer-browser/src/plugins/index.html +131 -0
  26. package/coverage/3cr-viewer-browser/src/plugins/index.ts.html +130 -0
  27. package/coverage/3cr-viewer-browser/src/plugins/vuetify.ts.html +220 -0
  28. package/coverage/base.css +224 -0
  29. package/coverage/block-navigation.js +87 -0
  30. package/coverage/favicon.png +0 -0
  31. package/coverage/index.html +251 -0
  32. package/coverage/prettify.css +1 -0
  33. package/coverage/prettify.js +2 -0
  34. package/coverage/sort-arrow-sprite.png +0 -0
  35. package/coverage/sorter.js +196 -0
  36. package/dist/Viewer3CR.js +17 -11
  37. package/dist/Viewer3CR.mjs +11911 -11157
  38. package/dist/Viewer3CR.umd.js +17 -11
  39. package/index.html +4 -1
  40. package/index.ts +46 -11
  41. package/package.json +9 -3
  42. package/src/App.vue +34 -45
  43. package/src/__tests__/app.spec.ts +27 -0
  44. package/src/components/WebGL3DR.vue +49 -37
  45. package/src/components/__tests__/webgl3dr.spec.ts +56 -0
  46. package/src/components/icons/liver.vue +21 -0
  47. package/src/components/loading/LoadingSpinner.vue +34 -12
  48. package/src/components/loading/__tests__/loading-spinner.spec.ts +11 -0
  49. package/src/components/modal/DemoModal.vue +44 -0
  50. package/src/components/modal/MftpWebGL3DRModal.vue +763 -410
  51. package/src/components/modal/__tests__/mftp-webgl-3dr-modal.spec.ts +690 -0
  52. package/src/components/selectors/__tests__/value-selector.spec.ts +35 -0
  53. package/src/components/sliders/DoubleSliderSelector.vue +30 -24
  54. package/src/components/sliders/VerticalSliderSelector.vue +25 -21
  55. package/src/components/sliders/__tests__/double-slider-selector.spec.ts +72 -0
  56. package/src/components/sliders/__tests__/vertical-slider-selector.spec.ts +61 -0
  57. package/src/helpers/__tests__/layout-overlay-style.spec.ts +288 -0
  58. package/src/helpers/__tests__/model-helper.spec.ts +118 -0
  59. package/src/helpers/__tests__/utils.spec.ts +70 -0
  60. package/src/helpers/layoutOverlayStyle.ts +50 -30
  61. package/src/plugins/__tests__/index.spec.ts +19 -0
  62. package/src/plugins/__tests__/vuetify.spec.ts +8 -0
  63. package/src/plugins/index.ts +6 -4
  64. package/src/plugins/vuetify.ts +25 -8
  65. package/test/helper.ts +35 -0
  66. package/test/setup.ts +1 -0
  67. package/tsconfig.json +5 -2
  68. package/vite.config.mts +1 -0
  69. package/vitest.config.mts +44 -0
  70. package/src/components/expansion-panels/ExpansionHeaderMiniMenu.vue +0 -19
  71. package/src/helpers/models.ts +0 -69
  72. /package/src/components/{sliders/SliderSelector.vue → selectors/ValueSelector.vue} +0 -0
package/index.html CHANGED
@@ -4,7 +4,10 @@
4
4
  <head>
5
5
  <meta charset="UTF-8" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>Welcome to Vuetify 3</title>
7
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
8
+ <link href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" rel="stylesheet">
9
+
10
+ <title>Welcome to Vuetify 3</title>
8
11
  </head>
9
12
 
10
13
  <body>
package/index.ts CHANGED
@@ -1,8 +1,24 @@
1
- import {registerVersion} from "@3cr/sdk-browser";
2
- import {ComponentPublicInstance, createApp} from "vue";
3
- import {registerPlugins} from "./src/plugins";
4
- import {App as BrowserViewer} from './src/main';
1
+ import { registerVersion } from "@3cr/sdk-browser";
2
+ import { ComponentPublicInstance, createApp } from "vue";
3
+ import { registerPlugins } from "./src/plugins";
4
+ import { App as BrowserViewer } from "./src/main";
5
5
 
6
+ export type ViewerCallback = () => void;
7
+ export type ViewerAsyncCallback = () => Promise<void>;
8
+
9
+ // Note: not defining the option will not display the item
10
+ export interface LoadViewerOptions {
11
+ OnShare?: ViewerCallback | ViewerAsyncCallback | undefined;
12
+ OnScreenshot?: ViewerCallback | ViewerAsyncCallback | undefined;
13
+ OnLoadNewDicomSeries?: ViewerCallback | ViewerAsyncCallback | undefined;
14
+ OnDownloadDicomSeries?: ViewerCallback | ViewerAsyncCallback | undefined;
15
+ OnLoadSavedSession?: ViewerCallback | ViewerAsyncCallback | undefined;
16
+ OnShareToMobile?: ViewerCallback | ViewerAsyncCallback | undefined;
17
+ OnSendTo3rdParty?: ViewerCallback | ViewerAsyncCallback | undefined;
18
+ OnSaveSession?: ViewerCallback | ViewerAsyncCallback | undefined;
19
+ OnClosePopup?: ViewerCallback | ViewerAsyncCallback | undefined;
20
+ OnExitViewer?: ViewerCallback | ViewerAsyncCallback | undefined;
21
+ }
6
22
 
7
23
  export interface LoadViewerPayload {
8
24
  Url: string;
@@ -13,26 +29,45 @@ export interface MftpDecryptionKey {
13
29
  Iv: string;
14
30
  }
15
31
 
16
- let mountedApp: ComponentPublicInstance | undefined = undefined
32
+ export const defaultOptions: LoadViewerOptions = {
33
+ OnClosePopup: () => Promise.resolve(),
34
+ OnExitViewer: () => Promise.resolve(),
35
+ OnLoadNewDicomSeries: () => Promise.resolve(),
36
+ OnDownloadDicomSeries: () => Promise.resolve(),
37
+ OnLoadSavedSession: () => Promise.resolve(),
38
+ OnSaveSession: () => Promise.resolve(),
39
+ OnSendTo3rdParty: () => Promise.resolve(),
40
+ OnShareToMobile: () => Promise.resolve(),
41
+ OnShare: () => Promise.resolve(),
42
+ OnScreenshot: () => Promise.resolve(),
43
+ };
44
+
45
+ let mountedApp: ComponentPublicInstance | undefined = undefined;
17
46
 
18
47
  export async function registerViewer(version: string) {
19
- const newElement = document.createElement('div')
48
+ const newElement = document.createElement("div");
20
49
 
21
50
  newElement.style.width = "0";
22
51
  newElement.style.height = "0";
23
52
 
24
53
  document.body.appendChild(newElement);
25
54
 
26
- const app = createApp(BrowserViewer)
27
- registerPlugins(app)
55
+ const app = createApp(BrowserViewer);
56
+ registerPlugins(app);
28
57
 
29
58
  mountedApp = app.mount(newElement);
30
59
  await registerVersion(version);
31
60
  }
32
61
 
33
- export async function loadViewer(payload: LoadViewerPayload | undefined = undefined): Promise<void> {
62
+ // TODO: accept callbacks for each function we want the parent to handle
63
+ export async function loadViewer(
64
+ payload: LoadViewerPayload | undefined = undefined,
65
+ options: LoadViewerOptions = defaultOptions
66
+ ): Promise<void> {
34
67
  if (!mountedApp) {
35
- throw new Error('Please call `registerViewer(version: string, idSelector: string)` first');
68
+ throw new Error(
69
+ "Please call `registerViewer(version: string, idSelector: string)` first"
70
+ );
36
71
  }
37
- await (mountedApp as any).loadInstance(payload)
72
+ await (mountedApp as any).loadInstance(payload, options);
38
73
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@3cr/viewer-browser",
3
- "version": "0.0.53",
3
+ "version": "0.0.57",
4
4
  "main": "./dist/Viewer3CR.umd.js",
5
5
  "module": "dist/Viewer3CR.umd.js",
6
6
  "homepage": "https://docs.3cr.singular.health",
@@ -20,7 +20,8 @@
20
20
  },
21
21
  "dependencies": {
22
22
  "@3cr/sdk-browser": "^1.0.13",
23
- "@3cr/types-ts": "^1.0.3",
23
+ "@3cr/types-ts": "^1.0.9",
24
+ "@kyvg/vue3-notification": "^3.2.1",
24
25
  "@mdi/font": "6.2.95",
25
26
  "@mdi/js": "^7.4.47",
26
27
  "roboto-fontface": "*",
@@ -31,11 +32,16 @@
31
32
  "@babel/types": "^7.24.0",
32
33
  "@types/node": "^20.11.25",
33
34
  "@vitejs/plugin-vue": "^5.0.4",
34
- "@vue/test-utils": "^2.4.1",
35
+ "@vitest/coverage-istanbul": "^1.4.0",
36
+ "@vitest/coverage-v8": "^1.4.0",
37
+ "@vitest/ui": "^1.4.0",
38
+ "@vue/test-utils": "^2.4.5",
35
39
  "aws-sdk": "^2.1594.0",
40
+ "jsdom": "^24.0.0",
36
41
  "material-design-icons-iconfont": "^6.7.0",
37
42
  "mime-types": "^2.1.35",
38
43
  "randomstring": "^1.3.0",
44
+ "resize-observer-polyfill": "^1.5.1",
39
45
  "sass": "^1.71.1",
40
46
  "typescript": "^5.4.2",
41
47
  "unplugin-fonts": "^1.1.1",
package/src/App.vue CHANGED
@@ -1,70 +1,59 @@
1
1
  <template>
2
- <v-app style="height: 0; width: 0;">
2
+ <v-app style="height: 0; width: 0">
3
3
  <MftpWebGL3DRModal ref="mftpWebGL3DRModal" :payload="payload" />
4
4
  </v-app>
5
+ <Notifications />
5
6
  </template>
6
7
 
7
8
  <script setup lang="ts">
8
-
9
9
  import MftpWebGL3DRModal from "@/components/modal/MftpWebGL3DRModal.vue";
10
- import {ref, unref} from "vue";
11
- import {LoadViewerPayload} from "../index";
10
+ import { ref, unref } from "vue";
11
+ import { defaultOptions, LoadViewerOptions, LoadViewerPayload } from "../index";
12
+ import { Notifications } from "@kyvg/vue3-notification";
12
13
 
13
14
  const payload = ref<LoadViewerPayload>({
14
- Url:"https://webgl-3dr.singular.health/test_scans/01440d4e-8b04-4b90-bb2c-698535ce16d6/CHEST.3vxl",
15
- DecryptionKey:{
16
- Iv:"XEloSh+OcO7TG77au6HjPw==",
17
- Key:"KUc722X1y4w42M+jCf9a3+6EGz66z7UMWK3m2aMqGxM="
18
- }
15
+ Url: "https://webgl-3dr.singular.health/test_scans/01440d4e-8b04-4b90-bb2c-698535ce16d6/CHEST.3vxl",
16
+ DecryptionKey: {
17
+ Iv: "XEloSh+OcO7TG77au6HjPw==",
18
+ Key: "KUc722X1y4w42M+jCf9a3+6EGz66z7UMWK3m2aMqGxM=",
19
+ },
19
20
  });
20
- // const payload = ref<LoadViewerPayload>(
21
- // {
22
- // Url:"https://webgl-3dr.singular.health/test_scans/8bdddee1-e581-485d-827d-6aa12eef2fc8/Head+Axial+Axial.3vxl",
23
- // DecryptionKey: {
24
- // Iv:"x856FgjpYDsRhIa3BFj5cg==",
25
- // Key:"OWjSMiL/ewUV1V6fGybhKcTyiysTPsIMp2DjdVoOUGI="
26
- // }
27
- // }
28
- // );
29
- const mftpWebGL3DRModal = ref<typeof MftpWebGL3DRModal | null>(null)
21
+ const options = ref<LoadViewerOptions>(defaultOptions);
22
+
23
+ const mftpWebGL3DRModal = ref<typeof MftpWebGL3DRModal | null>(null);
30
24
  defineExpose({
31
- loadInstance
32
- })
25
+ loadInstance,
26
+ });
33
27
 
34
- async function loadInstance(payloadIncoming: LoadViewerPayload | undefined = undefined) {
28
+ async function loadInstance(
29
+ payloadIncoming: LoadViewerPayload | undefined,
30
+ optionsIncoming: LoadViewerOptions
31
+ ) {
35
32
  payload.value = payloadIncoming || payload.value;
33
+ options.value = optionsIncoming;
36
34
 
37
- unref(mftpWebGL3DRModal)?.alterValue(true)
35
+ unref(mftpWebGL3DRModal)?.alterValue(true);
38
36
  }
39
37
  </script>
40
38
  <style>
41
-
42
- .material-icons-outlined, .material-symbols-outlined {
43
- font-variation-settings:
44
- 'FILL' 0,
45
- 'wght' 300,
46
- 'GRAD' 0,
47
- 'opsz' 24;
39
+ .material-icons-outlined,
40
+ .material-symbols-outlined {
41
+ font-variation-settings: "FILL" 0, "wght" 300, "GRAD" 0, "opsz" 24;
48
42
  }
49
- .material-icons, .material-symbols {
50
- font-variation-settings:
51
- 'FILL' 0,
52
- 'wght' 300,
53
- 'GRAD' 0,
54
- 'opsz' 24;
43
+ .material-icons,
44
+ .material-symbols {
45
+ font-variation-settings: "FILL" 0, "wght" 300, "GRAD" 0, "opsz" 24;
55
46
  }
56
- .material-icons-filled, .material-symbols-filled {
57
- font-variation-settings:
58
- 'FILL' 1,
59
- 'wght' 300,
60
- 'GRAD' 0,
61
- 'opsz' 24 !important;
47
+ .material-icons-filled,
48
+ .material-symbols-filled {
49
+ font-variation-settings: "FILL" 1, "wght" 300, "GRAD" 0, "opsz" 24 !important;
62
50
  }
63
51
  .overflow-y-scroll {
64
52
  overflow-y: scroll;
65
53
  }
66
- .material-icons-outlined, .material-icons {
67
- font-family: 'Material Symbols Outlined' !important;
54
+ .material-icons-outlined,
55
+ .material-icons {
56
+ font-family: "Material Symbols Outlined" !important;
68
57
  font-weight: normal;
69
58
  font-style: normal;
70
59
  font-size: 24px;
@@ -75,7 +64,7 @@ async function loadInstance(payloadIncoming: LoadViewerPayload | undefined = und
75
64
  white-space: nowrap;
76
65
  word-wrap: normal;
77
66
  direction: ltr;
78
- -webkit-font-feature-settings: 'liga';
67
+ -webkit-font-feature-settings: "liga";
79
68
  -webkit-font-smoothing: antialiased;
80
69
  }
81
70
  .transparent {
@@ -0,0 +1,27 @@
1
+ import { expect, describe, it, vi } from "vitest";
2
+ import { shallowMountVuetify } from "~/helper";
3
+ import App from "@/App.vue";
4
+
5
+ const wrapper = shallowMountVuetify(App);
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
+ describe("App.vue", () => {
20
+ it("should inflate component", () => {
21
+ expect(wrapper).toBeTruthy();
22
+ });
23
+ it("should loadScan", async () => {
24
+ await wrapper.vm.loadInstance(undefined, {});
25
+ expect(wrapper).toBeTruthy();
26
+ });
27
+ });
@@ -1,5 +1,10 @@
1
1
  <template>
2
- <div id="screenshotWindow" class="" @mouseover="emit('hover', true)" @mouseout="emit('hover', false)">
2
+ <div
3
+ id="screenshotWindow"
4
+ class=""
5
+ @mouseover="emit('hover', true)"
6
+ @mouseout="emit('hover', false)"
7
+ >
3
8
  <canvas
4
9
  id="unity-canvas"
5
10
  width="100%"
@@ -19,10 +24,19 @@
19
24
  </div>
20
25
  </template>
21
26
  <script setup lang="ts">
22
- import {defineEmits, nextTick, onMounted, ref} from 'vue';
27
+ import { defineEmits, nextTick, onMounted, ref } from "vue";
23
28
 
24
- import { createInstance, executePayload, registerOnPayloadHandler } from '@3cr/sdk-browser';
25
- import {FrontEndInterfaces, FrontEndPayload} from '@3cr/sdk-browser/types/payload';
29
+ import {
30
+ createInstance,
31
+ executePayload,
32
+ registerOnPayloadHandler,
33
+ } from "@3cr/sdk-browser";
34
+ import {
35
+ FrontEndInterfaces,
36
+ FrontEndPayload,
37
+ } from "@3cr/sdk-browser/types/payload";
38
+
39
+ const unityInstance = ref<any>(null);
26
40
 
27
41
  const emit = defineEmits<{
28
42
  instance_loaded: [void];
@@ -32,76 +46,74 @@ const emit = defineEmits<{
32
46
 
33
47
  defineExpose({
34
48
  sendPayload,
35
- snap,
36
-
49
+ receiveMessageFromUnity,
37
50
  });
38
51
 
39
52
  onMounted(async () => {
40
- await nextTick()
41
- const canvas = document.querySelector('#unity-canvas') as HTMLCanvasElement;
53
+ await nextTick();
54
+ const canvas = document.querySelector("#unity-canvas") as HTMLCanvasElement;
42
55
 
43
56
  await registerOnPayloadHandler(receiveMessageFromUnity);
44
57
 
58
+ /* c8 ignore start */
45
59
  if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) {
46
60
  // Mobile device style: fill the whole browser client area with the game canvas:
47
- const meta = document.createElement('meta');
48
- meta.name = 'viewport';
49
- meta.content = 'width=device-width, height=device-height, initial-scale=1.0, user-scalable=no, shrink-to-fit=yes';
50
- document.getElementsByTagName('head')[0].appendChild(meta);
61
+ const meta = document.createElement("meta");
62
+ meta.name = "viewport";
63
+ meta.content =
64
+ "width=device-width, height=device-height, initial-scale=1.0, user-scalable=no, shrink-to-fit=yes";
65
+ document.getElementsByTagName("head")[0].appendChild(meta);
51
66
 
52
- canvas.style.width = '100%';
53
- canvas.style.height = '80vh';
54
- canvas.style.position = 'fixed';
67
+ canvas.style.width = "100%";
68
+ canvas.style.height = "80vh";
69
+ canvas.style.position = "fixed";
55
70
 
56
- document.body.style.textAlign = 'left';
71
+ document.body.style.textAlign = "left";
57
72
  }
58
- await nextTick()
73
+ /* c8 ignore stop */
74
+ await nextTick();
59
75
  unityInstance.value = await createInstance(canvas);
60
76
 
61
- await nextTick()
77
+ await nextTick();
62
78
 
63
- if (navigator.storage && navigator.storage.estimate) {
79
+ /* c8 ignore start */
80
+ if (navigator.storage) {
64
81
  const quota = await navigator.storage.estimate();
65
82
  if (quota) {
66
83
  const percentageUsed = (quota.usage! / quota.quota!) * 100;
67
84
  console.log(`You've used ${percentageUsed}% of the available storage.`);
68
85
  const remaining = quota.quota! - quota.usage!;
69
86
  console.log(`You can write up to ${remaining} more bytes.`);
70
-
71
87
  }
72
88
  }
73
- emit('instance_loaded')
89
+ /* c8 ignore stop */
90
+
91
+ emit("instance_loaded");
74
92
  // Overlay scroll events to the container instance
75
- const fixedDiv = document.getElementById('parent-canvas');
76
- fixedDiv?.addEventListener('wheel', function (e: WheelEvent) {
93
+ const fixedDiv = document.getElementById("parent-canvas");
94
+ /* c8 ignore start */
95
+ fixedDiv?.addEventListener("wheel", function (e: WheelEvent) {
77
96
  e.preventDefault();
78
- const evt = new WheelEvent('wheel', {
97
+ const evt = new WheelEvent("wheel", {
79
98
  deltaY: e.deltaY,
80
99
  });
81
100
  canvas.dispatchEvent(evt);
82
101
  });
83
102
  fixedDiv?.addEventListener(
84
- 'contextmenu',
103
+ "contextmenu",
85
104
  function (ev) {
86
105
  ev.preventDefault();
87
- const evt = new MouseEvent('contextmenu', {});
106
+ const evt = new MouseEvent("contextmenu", {});
88
107
  canvas.dispatchEvent(evt);
89
108
  },
90
- false,
109
+ false
91
110
  );
92
- })
111
+ /* c8 ignore stop */
112
+ });
93
113
  async function sendPayload(payload: string) {
94
114
  await executePayload(JSON.parse(payload));
95
115
  }
96
116
  function receiveMessageFromUnity(json: FrontEndPayload) {
97
- emit('on_payload', json.Interface, json.Action, json.Message);
98
- }
99
- function snap() {
117
+ emit("on_payload", json.Interface, json.Action, json.Message);
100
118
  }
101
- // function snapCanvas(canvas: HTMLCanvasElement) {
102
- // // this.$emit('screenshot', canvas);
103
- // }
104
-
105
- const unityInstance = ref<any>(null);
106
-
107
119
  </script>
@@ -0,0 +1,56 @@
1
+ import { expect, test, vi } from "vitest";
2
+ import WebGL3DR from "../WebGL3DR.vue";
3
+ import { registerOnPayloadHandler, createInstance } from "@3cr/sdk-browser";
4
+ 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);
19
+
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,
31
+ });
32
+
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
+ });
49
+
50
+ test("mouseout", () => {
51
+ const parentCanvas = wrapper.find("#screenshotWindow");
52
+ parentCanvas.trigger("mouseover");
53
+ parentCanvas.trigger("mouseout");
54
+
55
+ expect(wrapper.emitted()["hover"]).toStrictEqual([[true], [true], [false]]);
56
+ });
@@ -0,0 +1,21 @@
1
+ <template>
2
+ <svg
3
+ version="1.1"
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ x="0px"
6
+ y="0px"
7
+ viewBox="0 0 100 125"
8
+ >
9
+ <g>
10
+ <path
11
+ d="M58.355,24.95c0,0-1.507,2.912-1.83,7.202c-0.263,3.507,0.686,11.987,1.338,14.719
12
+ c0.776,3.244,2.289,4.015,5.194,4.081c2.904,0.064,9.256-3.611,10.933-4.511c1.539-0.824,9.579-5.862,18.858-15.958
13
+ c0.565-0.615,3.947-5.196,0.141-6.338c-2.401-0.721-28.486-2.227-31.397-2.028c-2.912,0.199-15.201-0.224-17.393-0.566
14
+ c-1.672-0.26-15.038-2.543-26.261,3.018C6.715,30.128,4.773,37.822,4.773,45.939c0,6.012,2.826,9.781,5.405,18.088
15
+ c2.386,7.685,4.956,13.356,5.994,15.462c0.351,0.71,2.582,2.7,4.718,1.69c2.137-1.009,15.874-17.219,20.704-20.546
16
+ c4.832-3.325,14.231-5.497,14.323-9.52c0.091-4.022-0.587-20.948-2.675-22.053c-2.088-1.105-0.901-3.289-0.451-3.445
17
+ C54.538,25.009,58.692,24.486,58.355,24.95z"
18
+ />
19
+ </g>
20
+ </svg>
21
+ </template>
@@ -1,8 +1,11 @@
1
1
  <script setup lang="ts">
2
- import {ref} from "vue";
3
-
4
- const text = ref<string>('Loading Online Viewer')
2
+ export interface Props {
3
+ text: string;
4
+ }
5
5
 
6
+ const props = withDefaults(defineProps<Props>(), {
7
+ text: "Loading Online Viewer",
8
+ });
6
9
  </script>
7
10
 
8
11
  <template>
@@ -15,7 +18,10 @@ const text = ref<string>('Loading Online Viewer')
15
18
  <div class="circle"></div>
16
19
  </div>
17
20
  </div>
18
- <div class="mx-auto text-center white--text text-h3" v-html="text"></div>
21
+ <div
22
+ class="mx-auto text-center text-white text-h3"
23
+ v-html="props.text"
24
+ ></div>
19
25
  </div>
20
26
  </template>
21
27
 
@@ -50,9 +56,20 @@ const text = ref<string>('Loading Online Viewer')
50
56
  --in: 80%;
51
57
  --ar: #8799a4;
52
58
  --dt: #ffffff;
53
- --shadow: drop-shadow(0vmin 0vmin 0.5vmin rgba(0, 0, 0, 0.35)) drop-shadow(0vmin 1vmin 0.5vmin rgba(0, 0, 0, 0.09));
54
- --cross: linear-gradient(0deg, #fff0 calc(50% - 2px), #000 calc(50% - 1px) calc(50% + 1px), #fff0 calc(50% + 2px)),
55
- linear-gradient(90deg, #fff0 calc(50% - 2px), #000 calc(50% - 1px) calc(50% + 1px), #fff0 calc(50% + 2px));
59
+ --shadow: drop-shadow(0vmin 0vmin 0.5vmin rgba(0, 0, 0, 0.35))
60
+ drop-shadow(0vmin 1vmin 0.5vmin rgba(0, 0, 0, 0.09));
61
+ --cross: linear-gradient(
62
+ 0deg,
63
+ #fff0 calc(50% - 2px),
64
+ #000 calc(50% - 1px) calc(50% + 1px),
65
+ #fff0 calc(50% + 2px)
66
+ ),
67
+ linear-gradient(
68
+ 90deg,
69
+ #fff0 calc(50% - 2px),
70
+ #000 calc(50% - 1px) calc(50% + 1px),
71
+ #fff0 calc(50% + 2px)
72
+ );
56
73
  border: 6vmin solid var(--ar);
57
74
  width: var(--in);
58
75
  height: var(--in);
@@ -64,7 +81,8 @@ const text = ref<string>('Loading Online Viewer')
64
81
  top: 15vmin;
65
82
  right: -10vmin;
66
83
  animation: spin-bot var(--sp) ease 0s infinite;
67
- background-image: var(--cross), radial-gradient(var(--dt) 5.5vmin, #fff0 calc(5.5vmin + 1px));
84
+ background-image: var(--cross),
85
+ radial-gradient(var(--dt) 5.5vmin, #fff0 calc(5.5vmin + 1px));
68
86
  background-repeat: no-repeat;
69
87
  background-size: 3vmin 1vmin, 1vmin 3vmin, 100% 100%;
70
88
  background-position: center center;
@@ -76,7 +94,8 @@ const text = ref<string>('Loading Online Viewer')
76
94
  top: -2vmin;
77
95
  animation: spin-top var(--sp) ease 0s infinite;
78
96
  transform: rotate(-45deg);
79
- background-image: var(--cross), radial-gradient(var(--dt) 1.25vmin, #fff0 calc(1.25vmin + 1px));
97
+ background-image: var(--cross),
98
+ radial-gradient(var(--dt) 1.25vmin, #fff0 calc(1.25vmin + 1px));
80
99
  right: -4vmin;
81
100
  filter: hue-rotate(10deg) var(--shadow);
82
101
  background-size: 1.4vmin 1vmin, 1vmin 1.4vmin, 100% 100%;
@@ -88,7 +107,8 @@ const text = ref<string>('Loading Online Viewer')
88
107
  left: -13vmin;
89
108
  transform: rotate(175deg);
90
109
  animation: spin-left var(--sp) ease calc(var(--sp) / 4) infinite;
91
- background-image: var(--cross), radial-gradient(var(--dt) 9vmin, #fff0 calc(9vmin + 1px));
110
+ background-image: var(--cross),
111
+ radial-gradient(var(--dt) 9vmin, #fff0 calc(9vmin + 1px));
92
112
  filter: hue-rotate(20deg) var(--shadow);
93
113
  background-size: 5vmin 1vmin, 1vmin 5vmin, 100% 100%;
94
114
  }
@@ -98,8 +118,10 @@ const text = ref<string>('Loading Online Viewer')
98
118
  top: 35vmin;
99
119
  left: -6vmin;
100
120
  transform: rotate(-280deg);
101
- animation: spin-last var(--sp) ease calc(calc(calc(var(--sp) / 4) + var(--sp)) * -1) infinite;
102
- background-image: var(--cross), radial-gradient(var(--dt) 2.5vmin, #fff0 calc(2.5vmin + 1px));
121
+ animation: spin-last var(--sp) ease
122
+ calc(calc(calc(var(--sp) / 4) + var(--sp)) * -1) infinite;
123
+ background-image: var(--cross),
124
+ radial-gradient(var(--dt) 2.5vmin, #fff0 calc(2.5vmin + 1px));
103
125
  filter: hue-rotate(30deg) var(--shadow);
104
126
  background-size: 2vmin 1vmin, 1vmin 2vmin, 100% 100%;
105
127
  }
@@ -0,0 +1,11 @@
1
+ import { expect, describe, it } from "vitest";
2
+ import { mountVuetify } from "~/helper";
3
+ import LoadingSpinner from "@/components/loading/LoadingSpinner.vue";
4
+
5
+ const wrapper = mountVuetify(LoadingSpinner);
6
+
7
+ describe("LoadingSpinner.vue", () => {
8
+ it("should inflate component", () => {
9
+ expect(wrapper).toBeTruthy();
10
+ });
11
+ });
@@ -0,0 +1,44 @@
1
+ <script setup lang="ts">
2
+ import { computed } from "vue";
3
+
4
+ export interface Props {
5
+ modal: boolean;
6
+ }
7
+
8
+ const emit = defineEmits<{
9
+ "update:modal": [value: boolean];
10
+ }>();
11
+
12
+ const props = withDefaults(defineProps<Props>(), {
13
+ modal: false,
14
+ });
15
+
16
+ const modalState = computed({
17
+ get() {
18
+ return props.modal;
19
+ },
20
+ set(value) {
21
+ emit("update:modal", value);
22
+ },
23
+ });
24
+ </script>
25
+
26
+ <template>
27
+ <v-dialog v-model:model-value="modalState">
28
+ <v-card class="pa-1 ma-auto position-relative" max-width="450">
29
+ <v-card-title>Demo Instance</v-card-title>
30
+ <v-card-text>
31
+ This instance of 3Dicom Online Viewer is a Demo instance.
32
+ </v-card-text>
33
+ <v-card-text>
34
+ Please contact us if you would like to integrate this viewer into your
35
+ platform
36
+ </v-card-text>
37
+ <v-card-text> support@singular.health </v-card-text>
38
+ <v-card-actions>
39
+ <v-spacer />
40
+ <v-btn color="primary" @click="modalState = false"> Thanks! </v-btn>
41
+ </v-card-actions>
42
+ </v-card>
43
+ </v-dialog>
44
+ </template>