@3cr/viewer-browser 0.0.112 → 0.0.114

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@3cr/viewer-browser",
3
- "version": "0.0.112",
3
+ "version": "0.0.114",
4
4
  "main": "./dist/Viewer3CR.umd.js",
5
5
  "module": "dist/Viewer3CR.umd.js",
6
6
  "homepage": "https://docs.3cr.singular.health",
@@ -22,20 +22,30 @@
22
22
  id="close-dialog-prompt"
23
23
  data-test="closemodal"
24
24
  >
25
- <v-card class="pa-1 ma-auto position-relative" max-width="450">
25
+ <v-card
26
+ class="pa-1 ma-auto position-relative motif-background"
27
+ width="600"
28
+ theme="dark"
29
+ >
26
30
  <v-card-title>Close Viewer?</v-card-title>
27
31
  <v-card-text
28
32
  >Are you sure you want to close the Online Viewer?</v-card-text
29
33
  >
30
34
  <v-card-actions>
31
- <v-btn variant="text" color="red" @click="m_closeDialog = false">
35
+ <v-btn
36
+ variant="tonal"
37
+ color="secondary"
38
+ @click="m_closeDialog = false"
39
+ >
32
40
  Cancel
33
41
  </v-btn>
34
42
  <v-spacer />
35
- <v-btn color="primary" @click="closeModal">
43
+ <v-btn color="red" variant="tonal" @click="closeModal">
36
44
  Close without saving
37
45
  </v-btn>
38
- <v-btn color="primary" @click="closeModal"> Save Session </v-btn>
46
+ <v-btn color="primary" variant="tonal" @click="closeModal">
47
+ Save Session
48
+ </v-btn>
39
49
  </v-card-actions>
40
50
  </v-card>
41
51
  </v-dialog>
@@ -84,12 +94,19 @@
84
94
  </template>
85
95
  <v-list-item-title> Load Saved Session </v-list-item-title>
86
96
  </v-list-item>
97
+ <v-list-item @click="executeOption('OnSendTo3rdParty')">
98
+ <template v-slot:prepend>
99
+ <v-icon> send </v-icon>
100
+ </template>
101
+ <v-list-item-title>Send to 3rd Party</v-list-item-title>
102
+ </v-list-item>
87
103
  <v-list-item @click="executeOption('OnShare')">
88
104
  <template v-slot:prepend>
89
105
  <v-icon> share </v-icon>
90
106
  </template>
91
- <v-list-item-title>Share</v-list-item-title>
107
+ <v-list-item-title>Share to Mobile / VR</v-list-item-title>
92
108
  </v-list-item>
109
+
93
110
  <v-list-item
94
111
  @click="
95
112
  executeOption('OnClosePopup');
@@ -122,18 +139,21 @@
122
139
  Settings
123
140
  </v-btn>
124
141
  </template>
125
- <v-card min-width="400" class="pa-4">
142
+ <v-card width="350" class="pa-4">
126
143
  <SliderSelector
127
144
  v-model:value="scanState.Display.Brightness"
128
145
  label="Adjust Brightness"
146
+ prepend="percent"
129
147
  />
130
148
  <SliderSelector
131
149
  v-model:value="scanState.Display.Contrast"
132
150
  label="Adjust Contrast"
151
+ prepend="percent"
133
152
  />
134
153
  <SliderSelector
135
154
  v-model:value="scanState.Display.Opacity"
136
155
  label="Adjust Opacity"
156
+ prepend="percent"
137
157
  />
138
158
  <v-divider class="my-4" />
139
159
  <SliderSelector
@@ -141,18 +161,21 @@
141
161
  :min="0"
142
162
  :max="100"
143
163
  label="Pan Sensitivity"
164
+ prepend="percent"
144
165
  />
145
166
  <SliderSelector
146
167
  v-model:value="scanState.InteractionSettings.ZoomSensitivity"
147
168
  :min="0"
148
169
  :max="100"
149
170
  label="Zoom Sensitivity"
171
+ prepend="percent"
150
172
  />
151
173
  <SliderSelector
152
174
  v-model:value="scanState.InteractionSettings.RotateSensitivity"
153
175
  :min="0"
154
176
  :max="100"
155
177
  label="Rotate Sensitivity"
178
+ prepend="percent"
156
179
  />
157
180
  <SliderSelector
158
181
  v-model:value="
@@ -161,6 +184,7 @@
161
184
  :min="0"
162
185
  :max="100"
163
186
  label="Camera Rotate Sensitivity"
187
+ prepend="percent"
164
188
  />
165
189
  </v-card>
166
190
  </v-menu>
@@ -178,19 +202,25 @@
178
202
  style="min-width: 36px !important"
179
203
  :color="isLayout2x2 ? 'secondary' : 'primary'"
180
204
  @click="payloadHandler.layouts(LayoutActions.lo02)"
181
- ><v-icon>grid_view</v-icon></v-btn
182
- >
205
+ ><v-icon>grid_view</v-icon>
206
+ <v-tooltip location="bottom" activator="parent">
207
+ Change Layout to 2:2
208
+ </v-tooltip>
209
+ </v-btn>
183
210
  <v-btn
184
- class="ma-1 mr-0 pa-1"
211
+ class="ma-1 mr-1 pa-1"
185
212
  height="36"
186
213
  style="min-width: 36px !important"
187
214
  :color="isLayout1x3 ? 'secondary' : 'primary'"
188
215
  @click="payloadHandler.layouts(LayoutActions.lo03)"
189
- ><v-icon style="rotate: -90deg">view_comfy</v-icon></v-btn
216
+ ><v-icon style="rotate: -90deg">view_comfy</v-icon>
217
+ <v-tooltip location="bottom" activator="parent">
218
+ Change Layout to 1:3
219
+ </v-tooltip></v-btn
190
220
  >
191
221
  <v-btn class="" variant="flat" color="red" @click="alterValue(false)"
192
- >Close Viewer</v-btn
193
- >
222
+ >Close Viewer
223
+ </v-btn>
194
224
  </v-toolbar>
195
225
  <v-navigation-drawer
196
226
  v-model="drawer"
@@ -235,7 +265,13 @@
235
265
  <template v-slot:append>
236
266
  <v-divider></v-divider>
237
267
  <v-list>
238
- <v-tooltip right v-for="(item, index) in footerItems" :key="index">
268
+ <v-tooltip
269
+ right
270
+ v-for="(item, index) in footerItems.filter((x) =>
271
+ x.conditional()
272
+ )"
273
+ :key="index"
274
+ >
239
275
  <template #activator="{ props }">
240
276
  <v-list-item
241
277
  class="px-2"
@@ -271,7 +307,7 @@
271
307
  </v-list-item-title>
272
308
  </v-list-item>
273
309
  </template>
274
- {{ item.text }}
310
+ {{ item.tooltip }}
275
311
  </v-tooltip>
276
312
  </v-list>
277
313
  </template>
@@ -317,6 +353,9 @@
317
353
  >&nbsp;&nbsp;{{ miniMenu[0].text }}</span
318
354
  >
319
355
  <v-spacer />
356
+ <v-tooltip location="right" activator="parent">
357
+ {{ miniMenu[0].tooltip }}
358
+ </v-tooltip>
320
359
  </v-expansion-panel-title>
321
360
  <v-expansion-panel-text class="px-0">
322
361
  <DoubleSliderSelector
@@ -444,7 +483,7 @@
444
483
  >
445
484
  <div
446
485
  class="bordered-event-window"
447
- v-for="layout in scanState.Layout.PositionData"
486
+ v-for="(layout, index) in scanState.Layout.PositionData"
448
487
  :key="layout.Anchor"
449
488
  :style="{
450
489
  ...generateDivStyleForLayout(layout),
@@ -458,7 +497,12 @@
458
497
  <v-hover>
459
498
  <template v-slot:default="{ isHovering, props }">
460
499
  <div style="width: 100%; height: 100%" v-bind="props">
461
- <div class="buttons-in-view" v-show="isHovering">
500
+ <div
501
+ class="buttons-in-view"
502
+ v-show="
503
+ isHovering || menu || menus.filter((x) => x).length > 0
504
+ "
505
+ >
462
506
  <div v-if="scanState.Layout.PositionData.length !== 1">
463
507
  <v-btn
464
508
  color="transparent"
@@ -493,9 +537,7 @@
493
537
  Exit Fullscreen View
494
538
  </v-tooltip>
495
539
  </div>
496
- <div
497
- v-if="getCurrentActiveView(layout) === ScanView.Volume"
498
- >
540
+ <div>
499
541
  <v-btn
500
542
  color="transparent"
501
543
  :icon="true"
@@ -521,22 +563,23 @@
521
563
  v-if="getCurrentActiveView(layout) === ScanView.Volume"
522
564
  >
523
565
  <v-menu
566
+ v-model="menu"
524
567
  :close-on-content-click="false"
525
- :close-on-click="true"
568
+ location="end"
526
569
  >
527
570
  <template v-slot:activator="{ props }">
528
- <div v-bind="{ ...props }">
529
- <v-btn color="transparent" :icon="true">
530
- <v-icon color="white">cut</v-icon>
531
- </v-btn>
532
- <v-tooltip
533
- target="cursor"
534
- location="top"
535
- activator="parent"
536
- >
537
- Slice the 3D Volume
538
- </v-tooltip>
539
- </div>
571
+ <v-tooltip location="top">
572
+ <template v-slot:activator="{ props: tooltip }">
573
+ <v-btn
574
+ v-bind="mergeProps(props, tooltip)"
575
+ color="transparent"
576
+ :icon="true"
577
+ >
578
+ <v-icon color="white">cut</v-icon>
579
+ </v-btn>
580
+ </template>
581
+ Slice the 3D Volume
582
+ </v-tooltip>
540
583
  </template>
541
584
  <v-card min-width="400" class="pb-2">
542
585
  <v-card-title>Slice into the 3D Volume</v-card-title>
@@ -562,6 +605,7 @@
562
605
  v-if="getCurrentActiveView(layout) !== ScanView.Volume"
563
606
  >
564
607
  <v-menu
608
+ v-model="menus[index]"
565
609
  :close-on-content-click="false"
566
610
  :close-on-click="true"
567
611
  offset-overflow
@@ -572,8 +616,7 @@
572
616
  <template #activator="{ props: ttprops }">
573
617
  <v-btn
574
618
  v-bind="{ ...props, ...ttprops }"
575
- icon
576
- class="icon transparent"
619
+ :icon="true"
577
620
  >
578
621
  <v-icon color="white">360</v-icon>
579
622
  </v-btn>
@@ -691,12 +734,20 @@ import {
691
734
  SlidersActions,
692
735
  ViewSelectionActions,
693
736
  } from "@3cr/types-ts";
694
- import { defineEmits, nextTick, ref, unref, watch, WatchSource } from "vue";
737
+ import {
738
+ defineEmits,
739
+ mergeProps,
740
+ nextTick,
741
+ ref,
742
+ unref,
743
+ watch,
744
+ WatchSource,
745
+ } from "vue";
695
746
 
696
747
  import {
697
748
  checkIsDemo,
698
749
  demoType,
699
- executeDemoOption,
750
+ getDemoOption,
700
751
  isDemo,
701
752
  m_demo,
702
753
  m_demoPatient,
@@ -732,6 +783,7 @@ import {
732
783
  windowSlider,
733
784
  } from "@/dataLayer/scanState";
734
785
  import { getIconForPreset } from "@/dataLayer/iconData";
786
+ import { ViewerAsyncCallback, ViewerCallback } from "@/models/Callbacks";
735
787
 
736
788
  export interface Props {
737
789
  payload?: LoadViewerPayload;
@@ -755,6 +807,8 @@ const props = withDefaults(defineProps<Props>(), {
755
807
  const web_gl = ref<typeof WebGL3DR | null>(null);
756
808
  const payloadHandler = new PayloadHandler(web_gl);
757
809
  const value = ref<boolean>(false);
810
+ const menu = ref<boolean>(false);
811
+ const menus = ref<Array<boolean>>([false, false, false, false]);
758
812
  const drawer = ref<boolean>(true);
759
813
  const drawerCollapsed = ref<boolean>(true);
760
814
  const scanLoading = ref<boolean>(true);
@@ -763,30 +817,39 @@ const openPanels = ref<number>(0);
763
817
  const footerItems = ref([
764
818
  {
765
819
  text: "Reset Scan",
820
+ tooltip: "Resets your scan to original position and settings in all views",
766
821
  icon: "refresh",
767
822
  color: "red",
768
823
  click: async () => {
769
824
  await payloadHandler.viewSelection(ViewSelectionActions.vs05);
770
825
  await payloadHandler.viewSelection(ViewSelectionActions.vs06);
771
826
  },
827
+ conditional: () => true,
772
828
  },
773
829
  {
774
830
  text: "Send to 3rd Party",
831
+ tooltip:
832
+ "Securely share your loaded scan with 3rd parties via email with option to anonymise",
775
833
  icon: "send",
776
834
  color: "blue",
777
835
  click: async () => executeOption("OnSendTo3rdParty"),
836
+ conditional: () => getOption("OnSendTo3rdParty") !== undefined,
778
837
  },
779
838
  {
780
839
  text: "Share to Mobile / VR",
840
+ tooltip:
841
+ "Share your loaded scan with the 3Dicom Mobile and VR applications to download within 7 days",
781
842
  icon: "share",
782
843
  color: "yellow",
783
844
  click: async () => executeOption("OnShareToMobile"),
845
+ conditional: () => getOption("OnShareToMobile") !== undefined,
784
846
  },
785
847
  {
786
848
  text: "Screenshot View",
787
849
  icon: "screenshot_region",
788
850
  color: "green",
789
851
  click: async () => executeOption("OnScreenshot"),
852
+ conditional: () => getOption("OnScreenshot") !== undefined,
790
853
  },
791
854
  ]);
792
855
  const miniMenu = ref([
@@ -794,22 +857,26 @@ const miniMenu = ref([
794
857
  icon: "display_settings",
795
858
  text: "Tissue Density",
796
859
  tooltip:
797
- "Tissue Density - Change the view of density within the scan, allowing you to see through a certain density of particles",
860
+ "Change the range of visible anatomy in the scan by only showing areas with certain density of tissue",
798
861
  },
799
862
  ]);
800
863
  const rotationDeg = ref<number>(0);
801
864
  const stateOverlay = ref<boolean>(false);
802
865
  const m_closeDialog = ref<boolean>(false);
803
866
 
804
- function executeOption(key: keyof LoadViewerOptions) {
867
+ function getOption(
868
+ key: keyof LoadViewerOptions
869
+ ): ViewerCallback | ViewerAsyncCallback | undefined {
805
870
  if (unref(isDemo)) {
806
- executeDemoOption(key);
871
+ return getDemoOption(key);
807
872
  } else {
808
- const functionToExecute = unref(props.options)[key];
809
- console.log("executeOption", key, functionToExecute);
810
- if (functionToExecute !== undefined) {
811
- functionToExecute();
812
- }
873
+ return unref(props.options)[key];
874
+ }
875
+ }
876
+ function executeOption(key: keyof LoadViewerOptions) {
877
+ const functionToExecute = getOption(key);
878
+ if (functionToExecute !== undefined) {
879
+ functionToExecute();
813
880
  }
814
881
  }
815
882
 
@@ -49,7 +49,7 @@ defineExpose({
49
49
  {{ label }}
50
50
  </span>
51
51
  </v-col>
52
- <v-col cols="6">
52
+ <v-col cols="4">
53
53
  <v-text-field
54
54
  :model-value="sliderValue"
55
55
  @update:modelValue="sliderValue = toNumber($event)"
@@ -60,7 +60,7 @@ defineExpose({
60
60
  variant="outlined"
61
61
  density="compact"
62
62
  type="number"
63
- :append-icon="prepend"
63
+ :append-inner-icon="prepend"
64
64
  ></v-text-field>
65
65
  </v-col>
66
66
  </v-row>
@@ -88,4 +88,8 @@ defineExpose({
88
88
  .single-slider-selector .v-slider__thumb-label {
89
89
  color: black;
90
90
  }
91
+ .single-slider-selector {
92
+ margin-top: 4px;
93
+ margin-bottom: 4px;
94
+ }
91
95
  </style>
@@ -1,6 +1,7 @@
1
1
  import { computed, ref, unref } from "vue";
2
2
  import { LoadViewerOptions } from "@/models/LoadViewerOptions";
3
3
  import { LoadViewerPayload } from "@/models/LoadViewerPayload";
4
+ import { ViewerAsyncCallback, ViewerCallback } from "@/models/Callbacks";
4
5
 
5
6
  export function checkIsDemo(payload: LoadViewerPayload) {
6
7
  isDemo.value =
@@ -37,9 +38,7 @@ export const demoLicenceOptions: LoadViewerOptions = {
37
38
  OnShare: () => {
38
39
  m_demo.value = true;
39
40
  },
40
- OnScreenshot: () => {
41
- m_demo.value = true;
42
- },
41
+ OnScreenshot: undefined,
43
42
  };
44
43
 
45
44
  export const demoPatientOptions: LoadViewerOptions = {
@@ -68,9 +67,7 @@ export const demoPatientOptions: LoadViewerOptions = {
68
67
  OnShare: () => {
69
68
  m_demoPatient.value = true;
70
69
  },
71
- OnScreenshot: () => {
72
- m_demoPatient.value = true;
73
- },
70
+ OnScreenshot: undefined,
74
71
  };
75
72
 
76
73
  export const demoType = computed(() => {
@@ -91,3 +88,12 @@ export function executeDemoOption(key: keyof LoadViewerOptions) {
91
88
  }
92
89
  }
93
90
  }
91
+ export function getDemoOption(
92
+ key: keyof LoadViewerOptions
93
+ ): ViewerCallback | ViewerAsyncCallback | undefined {
94
+ if (unref(demoType) === "licence") {
95
+ return demoLicenceOptions[key];
96
+ } else if (unref(demoType) === "patient") {
97
+ return demoPatientOptions[key];
98
+ }
99
+ }