@3cr/viewer-browser 0.0.55 → 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/components.d.ts +1 -0
- package/coverage/3cr-viewer-browser/index.html +1 -1
- package/coverage/3cr-viewer-browser/index.ts.html +1 -1
- package/coverage/3cr-viewer-browser/src/App.vue.html +1 -1
- package/coverage/3cr-viewer-browser/src/components/WebGL3DR.vue.html +1 -1
- package/coverage/3cr-viewer-browser/src/components/icons/index.html +1 -1
- package/coverage/3cr-viewer-browser/src/components/icons/liver.vue.html +1 -1
- package/coverage/3cr-viewer-browser/src/components/index.html +1 -1
- package/coverage/3cr-viewer-browser/src/components/loading/LoadingSpinner.vue.html +1 -1
- package/coverage/3cr-viewer-browser/src/components/loading/index.html +1 -1
- package/coverage/3cr-viewer-browser/src/components/modal/MftpWebGL3DRModal.vue.html +205 -10
- package/coverage/3cr-viewer-browser/src/components/modal/index.html +19 -19
- package/coverage/3cr-viewer-browser/src/components/selectors/ValueSelector.vue.html +1 -1
- package/coverage/3cr-viewer-browser/src/components/selectors/index.html +1 -1
- package/coverage/3cr-viewer-browser/src/components/sliders/DoubleSliderSelector.vue.html +1 -1
- package/coverage/3cr-viewer-browser/src/components/sliders/VerticalSliderSelector.vue.html +1 -1
- package/coverage/3cr-viewer-browser/src/components/sliders/index.html +1 -1
- package/coverage/3cr-viewer-browser/src/helpers/index.html +1 -1
- package/coverage/3cr-viewer-browser/src/helpers/layoutOverlayStyle.ts.html +1 -1
- package/coverage/3cr-viewer-browser/src/helpers/modelHelper.ts.html +1 -1
- package/coverage/3cr-viewer-browser/src/helpers/utils.ts.html +1 -1
- package/coverage/3cr-viewer-browser/src/index.html +1 -1
- package/coverage/3cr-viewer-browser/src/main.ts.html +1 -1
- package/coverage/3cr-viewer-browser/src/plugins/index.html +1 -1
- package/coverage/3cr-viewer-browser/src/plugins/index.ts.html +1 -1
- package/coverage/3cr-viewer-browser/src/plugins/vuetify.ts.html +1 -1
- package/coverage/index.html +19 -19
- package/dist/Viewer3CR.js +12 -12
- package/dist/Viewer3CR.mjs +11467 -10882
- package/dist/Viewer3CR.umd.js +12 -12
- package/index.ts +33 -2
- package/package.json +2 -1
- package/src/App.vue +33 -36
- package/src/__tests__/app.spec.ts +1 -1
- package/src/components/loading/LoadingSpinner.vue +34 -12
- package/src/components/modal/DemoModal.vue +44 -0
- package/src/components/modal/MftpWebGL3DRModal.vue +134 -49
- package/src/plugins/index.ts +6 -4
package/index.ts
CHANGED
|
@@ -3,6 +3,23 @@ import { ComponentPublicInstance, createApp } from "vue";
|
|
|
3
3
|
import { registerPlugins } from "./src/plugins";
|
|
4
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
|
+
}
|
|
22
|
+
|
|
6
23
|
export interface LoadViewerPayload {
|
|
7
24
|
Url: string;
|
|
8
25
|
DecryptionKey: MftpDecryptionKey;
|
|
@@ -12,6 +29,19 @@ export interface MftpDecryptionKey {
|
|
|
12
29
|
Iv: string;
|
|
13
30
|
}
|
|
14
31
|
|
|
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
|
+
|
|
15
45
|
let mountedApp: ComponentPublicInstance | undefined = undefined;
|
|
16
46
|
|
|
17
47
|
export async function registerViewer(version: string) {
|
|
@@ -31,12 +61,13 @@ export async function registerViewer(version: string) {
|
|
|
31
61
|
|
|
32
62
|
// TODO: accept callbacks for each function we want the parent to handle
|
|
33
63
|
export async function loadViewer(
|
|
34
|
-
payload: LoadViewerPayload | undefined = undefined
|
|
64
|
+
payload: LoadViewerPayload | undefined = undefined,
|
|
65
|
+
options: LoadViewerOptions = defaultOptions
|
|
35
66
|
): Promise<void> {
|
|
36
67
|
if (!mountedApp) {
|
|
37
68
|
throw new Error(
|
|
38
69
|
"Please call `registerViewer(version: string, idSelector: string)` first"
|
|
39
70
|
);
|
|
40
71
|
}
|
|
41
|
-
await (mountedApp as any).loadInstance(payload);
|
|
72
|
+
await (mountedApp as any).loadInstance(payload, options);
|
|
42
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",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@3cr/sdk-browser": "^1.0.13",
|
|
23
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": "*",
|
package/src/App.vue
CHANGED
|
@@ -1,62 +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
|
});
|
|
21
|
+
const options = ref<LoadViewerOptions>(defaultOptions);
|
|
20
22
|
|
|
21
|
-
const mftpWebGL3DRModal = ref<typeof MftpWebGL3DRModal | null>(null)
|
|
23
|
+
const mftpWebGL3DRModal = ref<typeof MftpWebGL3DRModal | null>(null);
|
|
22
24
|
defineExpose({
|
|
23
|
-
loadInstance
|
|
24
|
-
})
|
|
25
|
+
loadInstance,
|
|
26
|
+
});
|
|
25
27
|
|
|
26
|
-
async function loadInstance(
|
|
28
|
+
async function loadInstance(
|
|
29
|
+
payloadIncoming: LoadViewerPayload | undefined,
|
|
30
|
+
optionsIncoming: LoadViewerOptions
|
|
31
|
+
) {
|
|
27
32
|
payload.value = payloadIncoming || payload.value;
|
|
33
|
+
options.value = optionsIncoming;
|
|
28
34
|
|
|
29
|
-
unref(mftpWebGL3DRModal)?.alterValue(true)
|
|
35
|
+
unref(mftpWebGL3DRModal)?.alterValue(true);
|
|
30
36
|
}
|
|
31
37
|
</script>
|
|
32
38
|
<style>
|
|
33
|
-
|
|
34
|
-
.material-
|
|
35
|
-
font-variation-settings:
|
|
36
|
-
'FILL' 0,
|
|
37
|
-
'wght' 300,
|
|
38
|
-
'GRAD' 0,
|
|
39
|
-
'opsz' 24;
|
|
39
|
+
.material-icons-outlined,
|
|
40
|
+
.material-symbols-outlined {
|
|
41
|
+
font-variation-settings: "FILL" 0, "wght" 300, "GRAD" 0, "opsz" 24;
|
|
40
42
|
}
|
|
41
|
-
.material-icons,
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
'wght' 300,
|
|
45
|
-
'GRAD' 0,
|
|
46
|
-
'opsz' 24;
|
|
43
|
+
.material-icons,
|
|
44
|
+
.material-symbols {
|
|
45
|
+
font-variation-settings: "FILL" 0, "wght" 300, "GRAD" 0, "opsz" 24;
|
|
47
46
|
}
|
|
48
|
-
.material-icons-filled,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
'wght' 300,
|
|
52
|
-
'GRAD' 0,
|
|
53
|
-
'opsz' 24 !important;
|
|
47
|
+
.material-icons-filled,
|
|
48
|
+
.material-symbols-filled {
|
|
49
|
+
font-variation-settings: "FILL" 1, "wght" 300, "GRAD" 0, "opsz" 24 !important;
|
|
54
50
|
}
|
|
55
51
|
.overflow-y-scroll {
|
|
56
52
|
overflow-y: scroll;
|
|
57
53
|
}
|
|
58
|
-
.material-icons-outlined,
|
|
59
|
-
|
|
54
|
+
.material-icons-outlined,
|
|
55
|
+
.material-icons {
|
|
56
|
+
font-family: "Material Symbols Outlined" !important;
|
|
60
57
|
font-weight: normal;
|
|
61
58
|
font-style: normal;
|
|
62
59
|
font-size: 24px;
|
|
@@ -67,7 +64,7 @@ async function loadInstance(payloadIncoming: LoadViewerPayload | undefined = und
|
|
|
67
64
|
white-space: nowrap;
|
|
68
65
|
word-wrap: normal;
|
|
69
66
|
direction: ltr;
|
|
70
|
-
-webkit-font-feature-settings:
|
|
67
|
+
-webkit-font-feature-settings: "liga";
|
|
71
68
|
-webkit-font-smoothing: antialiased;
|
|
72
69
|
}
|
|
73
70
|
.transparent {
|
|
@@ -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,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>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<!-- /* c8 ignore start */ -->
|
|
2
2
|
<template>
|
|
3
|
+
<DemoModal v-model:modal="m_demo" />
|
|
3
4
|
<v-dialog
|
|
4
5
|
id="cr-viewer"
|
|
5
6
|
class="pa-0 ma-0 overflow-y-auto overflow-x-hidden"
|
|
@@ -11,7 +12,7 @@
|
|
|
11
12
|
@input="alterValue"
|
|
12
13
|
>
|
|
13
14
|
<v-dialog
|
|
14
|
-
v-model="m_closeDialog"
|
|
15
|
+
v-model:model-value="m_closeDialog"
|
|
15
16
|
id="close-dialog-prompt"
|
|
16
17
|
data-test="closemodal"
|
|
17
18
|
>
|
|
@@ -59,41 +60,36 @@
|
|
|
59
60
|
</template>
|
|
60
61
|
<v-card class="">
|
|
61
62
|
<v-list>
|
|
62
|
-
<v-list-item
|
|
63
|
+
<v-list-item @click="executeOption('OnLoadNewDicomSeries')">
|
|
63
64
|
<template v-slot:prepend>
|
|
64
65
|
<v-icon> upload </v-icon>
|
|
65
66
|
</template>
|
|
66
|
-
<v-list-item-title
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
<v-list-item-title>Load New DICOM Series</v-list-item-title>
|
|
68
|
+
</v-list-item>
|
|
69
|
+
<v-list-item @click="executeOption('OnDownloadDicomSeries')">
|
|
70
|
+
<template v-slot:prepend>
|
|
71
|
+
<v-icon> download </v-icon>
|
|
72
|
+
</template>
|
|
73
|
+
<v-list-item-title>Download DICOM Series</v-list-item-title>
|
|
71
74
|
</v-list-item>
|
|
72
|
-
|
|
73
|
-
<!-- <v-list-item-icon><v-icon> download </v-icon></v-list-item-icon>-->
|
|
74
|
-
<!-- <v-list-item-title>Download DICOM Series</v-list-item-title>-->
|
|
75
|
-
<!-- </v-list-item>-->
|
|
76
|
-
<v-list-item disabled>
|
|
75
|
+
<v-list-item @click="executeOption('OnLoadSavedSession')">
|
|
77
76
|
<template v-slot:prepend>
|
|
78
77
|
<v-icon> sync </v-icon>
|
|
79
78
|
</template>
|
|
80
|
-
<v-list-item-title
|
|
81
|
-
>Load Saved Session <v-chip x-small color="success"
|
|
82
|
-
>Coming Soon</v-chip
|
|
83
|
-
></v-list-item-title
|
|
84
|
-
>
|
|
79
|
+
<v-list-item-title> Load Saved Session </v-list-item-title>
|
|
85
80
|
</v-list-item>
|
|
86
|
-
<v-list-item
|
|
81
|
+
<v-list-item @click="executeOption('OnShare')">
|
|
87
82
|
<template v-slot:prepend>
|
|
88
83
|
<v-icon> share </v-icon>
|
|
89
84
|
</template>
|
|
90
|
-
<v-list-item-title
|
|
91
|
-
>Share <v-chip x-small color="success"
|
|
92
|
-
>Coming Soon</v-chip
|
|
93
|
-
></v-list-item-title
|
|
94
|
-
>
|
|
85
|
+
<v-list-item-title>Share</v-list-item-title>
|
|
95
86
|
</v-list-item>
|
|
96
|
-
<v-list-item
|
|
87
|
+
<v-list-item
|
|
88
|
+
@click="
|
|
89
|
+
executeOption('OnClosePopup');
|
|
90
|
+
alterValue(false);
|
|
91
|
+
"
|
|
92
|
+
>
|
|
97
93
|
<template v-slot:prepend>
|
|
98
94
|
<v-icon> close </v-icon>
|
|
99
95
|
</template>
|
|
@@ -164,7 +160,9 @@
|
|
|
164
160
|
</v-menu>
|
|
165
161
|
|
|
166
162
|
<v-spacer />
|
|
167
|
-
<div class="font-weight-bold">
|
|
163
|
+
<div class="font-weight-bold">
|
|
164
|
+
<span v-if="isDemo">[DEMO] </span>Not for Diagnostic Use
|
|
165
|
+
</div>
|
|
168
166
|
<v-spacer />
|
|
169
167
|
<v-btn
|
|
170
168
|
class="ma-1 mr-0 pa-1"
|
|
@@ -427,6 +425,7 @@
|
|
|
427
425
|
:class="!(instanceLoaded && !scanLoading) && 'no-pointer-events'"
|
|
428
426
|
v-if="value"
|
|
429
427
|
ref="web_gl"
|
|
428
|
+
id="webgl-container"
|
|
430
429
|
@on_payload="handleOnPayload"
|
|
431
430
|
@instance_loaded="load"
|
|
432
431
|
@hover="hoverOverCanvas"
|
|
@@ -442,6 +441,7 @@
|
|
|
442
441
|
? 'grab !important'
|
|
443
442
|
: 'default',
|
|
444
443
|
}"
|
|
444
|
+
:data-box-internal="layout.DefaultView"
|
|
445
445
|
>
|
|
446
446
|
<v-hover>
|
|
447
447
|
<template v-slot:default="{ isHovering, props }">
|
|
@@ -632,10 +632,7 @@
|
|
|
632
632
|
</div>
|
|
633
633
|
|
|
634
634
|
<LoadingSpinner v-if="!instanceLoaded" />
|
|
635
|
-
<LoadingSpinner
|
|
636
|
-
v-if="scanLoading"
|
|
637
|
-
text="Rendering your scan in <span class='sub-type'>3DICOM</span>"
|
|
638
|
-
/>
|
|
635
|
+
<LoadingSpinner v-if="scanLoading" text="Rendering your scan in 3D" />
|
|
639
636
|
<v-textarea
|
|
640
637
|
v-if="stateOverlay"
|
|
641
638
|
style="
|
|
@@ -683,11 +680,18 @@ import {
|
|
|
683
680
|
InitialScanState,
|
|
684
681
|
PositionData,
|
|
685
682
|
ScanView,
|
|
683
|
+
InteractivityActions,
|
|
684
|
+
NotificationsActions,
|
|
685
|
+
NotificationPayload,
|
|
686
686
|
} from "@3cr/types-ts";
|
|
687
687
|
|
|
688
688
|
import { toNumber } from "@/helpers/utils";
|
|
689
689
|
import { computed, ref, unref, watch, defineEmits, nextTick } from "vue";
|
|
690
|
-
import {
|
|
690
|
+
import {
|
|
691
|
+
defaultOptions,
|
|
692
|
+
LoadViewerOptions,
|
|
693
|
+
LoadViewerPayload,
|
|
694
|
+
} from "../../../index";
|
|
691
695
|
|
|
692
696
|
const emit = defineEmits<{
|
|
693
697
|
instanceLoaded: [void];
|
|
@@ -696,6 +700,7 @@ const emit = defineEmits<{
|
|
|
696
700
|
|
|
697
701
|
export interface Props {
|
|
698
702
|
payload?: LoadViewerPayload;
|
|
703
|
+
options?: LoadViewerOptions;
|
|
699
704
|
}
|
|
700
705
|
|
|
701
706
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -706,7 +711,55 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
706
711
|
Key: "OWjSMiL/ewUV1V6fGybhKcTyiysTPsIMp2DjdVoOUGI=",
|
|
707
712
|
},
|
|
708
713
|
}),
|
|
714
|
+
options: () => defaultOptions,
|
|
709
715
|
});
|
|
716
|
+
const isDemo = ref<boolean>(false);
|
|
717
|
+
let m_demo = ref<boolean>(false);
|
|
718
|
+
|
|
719
|
+
const demoOptions: LoadViewerOptions = {
|
|
720
|
+
OnClosePopup: () => Promise.resolve(),
|
|
721
|
+
OnExitViewer: () => {
|
|
722
|
+
m_demo.value = true;
|
|
723
|
+
},
|
|
724
|
+
OnLoadNewDicomSeries: () => {
|
|
725
|
+
m_demo.value = true;
|
|
726
|
+
},
|
|
727
|
+
OnDownloadDicomSeries: () => {
|
|
728
|
+
m_demo.value = true;
|
|
729
|
+
},
|
|
730
|
+
OnLoadSavedSession: () => {
|
|
731
|
+
m_demo.value = true;
|
|
732
|
+
},
|
|
733
|
+
OnSaveSession: () => {
|
|
734
|
+
m_demo.value = true;
|
|
735
|
+
},
|
|
736
|
+
OnSendTo3rdParty: () => {
|
|
737
|
+
m_demo.value = true;
|
|
738
|
+
},
|
|
739
|
+
OnShareToMobile: () => {
|
|
740
|
+
m_demo.value = true;
|
|
741
|
+
},
|
|
742
|
+
OnShare: () => {
|
|
743
|
+
m_demo.value = true;
|
|
744
|
+
},
|
|
745
|
+
OnScreenshot: () => {
|
|
746
|
+
m_demo.value = true;
|
|
747
|
+
},
|
|
748
|
+
};
|
|
749
|
+
|
|
750
|
+
function executeOption(key: keyof LoadViewerOptions) {
|
|
751
|
+
if (unref(isDemo)) {
|
|
752
|
+
const functionToExecute = demoOptions[key];
|
|
753
|
+
if (functionToExecute !== undefined) {
|
|
754
|
+
functionToExecute();
|
|
755
|
+
}
|
|
756
|
+
} else {
|
|
757
|
+
const functionToExecute = unref(props.options)[key];
|
|
758
|
+
if (functionToExecute !== undefined) {
|
|
759
|
+
functionToExecute();
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
710
763
|
|
|
711
764
|
const emptyPayload = { Version: "1.1.0" };
|
|
712
765
|
|
|
@@ -735,21 +788,19 @@ const footerItems = ref([
|
|
|
735
788
|
text: "Send to 3rd Party",
|
|
736
789
|
icon: "send",
|
|
737
790
|
color: "blue",
|
|
738
|
-
click: async () =>
|
|
791
|
+
click: async () => executeOption("OnSendTo3rdParty"),
|
|
739
792
|
},
|
|
740
793
|
{
|
|
741
794
|
text: "Share to Mobile / VR",
|
|
742
795
|
icon: "share",
|
|
743
796
|
color: "yellow",
|
|
744
|
-
click: async () =>
|
|
797
|
+
click: async () => executeOption("OnShareToMobile"),
|
|
745
798
|
},
|
|
746
799
|
{
|
|
747
800
|
text: "Screenshot View",
|
|
748
801
|
icon: "screenshot_region",
|
|
749
802
|
color: "green",
|
|
750
|
-
click: async () =>
|
|
751
|
-
await snap();
|
|
752
|
-
},
|
|
803
|
+
click: async () => executeOption("OnScreenshot"),
|
|
753
804
|
},
|
|
754
805
|
]);
|
|
755
806
|
|
|
@@ -1021,11 +1072,16 @@ async function sliderHandler(action: string, value: number) {
|
|
|
1021
1072
|
function closeModal() {
|
|
1022
1073
|
m_closeDialog.value = false;
|
|
1023
1074
|
value.value = false;
|
|
1075
|
+
executeOption("OnExitViewer");
|
|
1024
1076
|
}
|
|
1025
1077
|
function alterValue(val: boolean) {
|
|
1026
1078
|
if (!val) {
|
|
1027
1079
|
m_closeDialog.value = true;
|
|
1028
1080
|
return;
|
|
1081
|
+
} else {
|
|
1082
|
+
isDemo.value =
|
|
1083
|
+
props.payload.Url ===
|
|
1084
|
+
"https://webgl-3dr.singular.health/test_scans/01440d4e-8b04-4b90-bb2c-698535ce16d6/CHEST.3vxl";
|
|
1029
1085
|
}
|
|
1030
1086
|
|
|
1031
1087
|
value.value = val;
|
|
@@ -1118,21 +1174,50 @@ async function i_fileManagement(action: string, message: string) {
|
|
|
1118
1174
|
drawerCollapsed.value = false;
|
|
1119
1175
|
await hoverOverCanvas(false);
|
|
1120
1176
|
}
|
|
1177
|
+
|
|
1178
|
+
import { useNotification } from "@kyvg/vue3-notification";
|
|
1179
|
+
|
|
1180
|
+
const { notify } = useNotification();
|
|
1181
|
+
|
|
1121
1182
|
async function i_notifications(action: string, message: string) {
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1183
|
+
const notification = JSON.parse(message) as NotificationPayload;
|
|
1184
|
+
|
|
1185
|
+
switch (notification.Action) {
|
|
1186
|
+
case InteractivityActions.in01:
|
|
1187
|
+
case InteractivityActions.in02:
|
|
1188
|
+
case InteractivityActions.in03:
|
|
1189
|
+
case InteractivityActions.in04:
|
|
1190
|
+
return;
|
|
1191
|
+
default:
|
|
1192
|
+
}
|
|
1193
|
+
if (notification.Action.startsWith("sl")) return;
|
|
1194
|
+
|
|
1195
|
+
let type = "success";
|
|
1196
|
+
switch (action) {
|
|
1197
|
+
// Muting no01
|
|
1198
|
+
case NotificationsActions.no01:
|
|
1199
|
+
type = "success";
|
|
1200
|
+
return;
|
|
1201
|
+
|
|
1202
|
+
case NotificationsActions.no02:
|
|
1203
|
+
type = "warn";
|
|
1204
|
+
break;
|
|
1205
|
+
|
|
1206
|
+
case NotificationsActions.no03:
|
|
1207
|
+
type = "error";
|
|
1208
|
+
break;
|
|
1209
|
+
|
|
1210
|
+
// Muting no04
|
|
1211
|
+
case NotificationsActions.no04:
|
|
1212
|
+
type = "info";
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
notify({
|
|
1217
|
+
title: notification.Code,
|
|
1218
|
+
text: notification.Action,
|
|
1219
|
+
type,
|
|
1220
|
+
});
|
|
1136
1221
|
}
|
|
1137
1222
|
const isLayout2x2 = computed(() => {
|
|
1138
1223
|
return (
|
package/src/plugins/index.ts
CHANGED
|
@@ -5,11 +5,13 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
// Plugins
|
|
8
|
-
import vuetify from
|
|
8
|
+
import vuetify from "./vuetify";
|
|
9
9
|
|
|
10
10
|
// Types
|
|
11
|
-
import type { App } from
|
|
11
|
+
import type { App } from "vue";
|
|
12
|
+
import Notifications from "@kyvg/vue3-notification";
|
|
12
13
|
|
|
13
|
-
export function registerPlugins
|
|
14
|
-
app.use(vuetify)
|
|
14
|
+
export function registerPlugins(app: App) {
|
|
15
|
+
app.use(vuetify);
|
|
16
|
+
app.use(Notifications);
|
|
15
17
|
}
|