@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.
- package/__tests__/index.spec.ts +31 -0
- package/components.d.ts +3 -2
- package/coverage/3cr-viewer-browser/index.html +116 -0
- package/coverage/3cr-viewer-browser/index.ts.html +211 -0
- package/coverage/3cr-viewer-browser/src/App.vue.html +313 -0
- package/coverage/3cr-viewer-browser/src/components/WebGL3DR.vue.html +442 -0
- package/coverage/3cr-viewer-browser/src/components/icons/index.html +116 -0
- package/coverage/3cr-viewer-browser/src/components/icons/liver.vue.html +148 -0
- package/coverage/3cr-viewer-browser/src/components/index.html +116 -0
- package/coverage/3cr-viewer-browser/src/components/loading/LoadingSpinner.vue.html +556 -0
- package/coverage/3cr-viewer-browser/src/components/loading/index.html +116 -0
- package/coverage/3cr-viewer-browser/src/components/modal/MftpWebGL3DRModal.vue.html +4126 -0
- package/coverage/3cr-viewer-browser/src/components/modal/index.html +116 -0
- package/coverage/3cr-viewer-browser/src/components/selectors/ValueSelector.vue.html +331 -0
- package/coverage/3cr-viewer-browser/src/components/selectors/index.html +116 -0
- package/coverage/3cr-viewer-browser/src/components/sliders/DoubleSliderSelector.vue.html +445 -0
- package/coverage/3cr-viewer-browser/src/components/sliders/VerticalSliderSelector.vue.html +349 -0
- package/coverage/3cr-viewer-browser/src/components/sliders/index.html +131 -0
- package/coverage/3cr-viewer-browser/src/helpers/index.html +146 -0
- package/coverage/3cr-viewer-browser/src/helpers/layoutOverlayStyle.ts.html +406 -0
- package/coverage/3cr-viewer-browser/src/helpers/modelHelper.ts.html +412 -0
- package/coverage/3cr-viewer-browser/src/helpers/utils.ts.html +133 -0
- package/coverage/3cr-viewer-browser/src/index.html +131 -0
- package/coverage/3cr-viewer-browser/src/main.ts.html +124 -0
- package/coverage/3cr-viewer-browser/src/plugins/index.html +131 -0
- package/coverage/3cr-viewer-browser/src/plugins/index.ts.html +130 -0
- package/coverage/3cr-viewer-browser/src/plugins/vuetify.ts.html +220 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +251 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +196 -0
- package/dist/Viewer3CR.js +17 -11
- package/dist/Viewer3CR.mjs +11911 -11157
- package/dist/Viewer3CR.umd.js +17 -11
- package/index.html +4 -1
- package/index.ts +46 -11
- package/package.json +9 -3
- package/src/App.vue +34 -45
- package/src/__tests__/app.spec.ts +27 -0
- package/src/components/WebGL3DR.vue +49 -37
- package/src/components/__tests__/webgl3dr.spec.ts +56 -0
- package/src/components/icons/liver.vue +21 -0
- package/src/components/loading/LoadingSpinner.vue +34 -12
- package/src/components/loading/__tests__/loading-spinner.spec.ts +11 -0
- package/src/components/modal/DemoModal.vue +44 -0
- package/src/components/modal/MftpWebGL3DRModal.vue +763 -410
- package/src/components/modal/__tests__/mftp-webgl-3dr-modal.spec.ts +690 -0
- package/src/components/selectors/__tests__/value-selector.spec.ts +35 -0
- package/src/components/sliders/DoubleSliderSelector.vue +30 -24
- package/src/components/sliders/VerticalSliderSelector.vue +25 -21
- package/src/components/sliders/__tests__/double-slider-selector.spec.ts +72 -0
- package/src/components/sliders/__tests__/vertical-slider-selector.spec.ts +61 -0
- package/src/helpers/__tests__/layout-overlay-style.spec.ts +288 -0
- package/src/helpers/__tests__/model-helper.spec.ts +118 -0
- package/src/helpers/__tests__/utils.spec.ts +70 -0
- package/src/helpers/layoutOverlayStyle.ts +50 -30
- package/src/plugins/__tests__/index.spec.ts +19 -0
- package/src/plugins/__tests__/vuetify.spec.ts +8 -0
- package/src/plugins/index.ts +6 -4
- package/src/plugins/vuetify.ts +25 -8
- package/test/helper.ts +35 -0
- package/test/setup.ts +1 -0
- package/tsconfig.json +5 -2
- package/vite.config.mts +1 -0
- package/vitest.config.mts +44 -0
- package/src/components/expansion-panels/ExpansionHeaderMiniMenu.vue +0 -19
- package/src/helpers/models.ts +0 -69
- /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
|
-
|
|
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
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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.
|
|
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.
|
|
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
|
-
"@
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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(
|
|
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-
|
|
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,
|
|
50
|
-
|
|
51
|
-
|
|
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,
|
|
57
|
-
|
|
58
|
-
|
|
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,
|
|
67
|
-
|
|
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:
|
|
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
|
|
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
|
|
27
|
+
import { defineEmits, nextTick, onMounted, ref } from "vue";
|
|
23
28
|
|
|
24
|
-
import {
|
|
25
|
-
|
|
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
|
-
|
|
36
|
-
|
|
49
|
+
receiveMessageFromUnity,
|
|
37
50
|
});
|
|
38
51
|
|
|
39
52
|
onMounted(async () => {
|
|
40
|
-
await nextTick()
|
|
41
|
-
const canvas = document.querySelector(
|
|
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(
|
|
48
|
-
meta.name =
|
|
49
|
-
meta.content =
|
|
50
|
-
|
|
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 =
|
|
53
|
-
canvas.style.height =
|
|
54
|
-
canvas.style.position =
|
|
67
|
+
canvas.style.width = "100%";
|
|
68
|
+
canvas.style.height = "80vh";
|
|
69
|
+
canvas.style.position = "fixed";
|
|
55
70
|
|
|
56
|
-
document.body.style.textAlign =
|
|
71
|
+
document.body.style.textAlign = "left";
|
|
57
72
|
}
|
|
58
|
-
|
|
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
|
-
|
|
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
|
-
|
|
89
|
+
/* c8 ignore stop */
|
|
90
|
+
|
|
91
|
+
emit("instance_loaded");
|
|
74
92
|
// Overlay scroll events to the container instance
|
|
75
|
-
const fixedDiv = document.getElementById(
|
|
76
|
-
|
|
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(
|
|
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
|
-
|
|
103
|
+
"contextmenu",
|
|
85
104
|
function (ev) {
|
|
86
105
|
ev.preventDefault();
|
|
87
|
-
const evt = new MouseEvent(
|
|
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(
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
|
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))
|
|
54
|
-
|
|
55
|
-
|
|
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),
|
|
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),
|
|
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),
|
|
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
|
|
102
|
-
|
|
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>
|