@3cr/viewer-browser 0.0.161 → 0.0.194
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/.circleci/config.yml +53 -0
- package/__tests__/index.spec.ts +31 -24
- package/components.d.ts +32 -0
- package/dist/Viewer3CR.js +44 -27
- package/dist/Viewer3CR.mjs +22734 -17747
- package/dist/Viewer3CR.umd.js +44 -27
- package/index.html +6 -8
- package/index.ts +9 -5
- package/package.json +5 -2
- package/playground/index.html +7 -10
- package/src/App.vue +12 -4
- package/src/__tests__/app.spec.ts +2 -13
- package/src/assets/magic_wand.svg +24 -0
- package/src/components/WebGL3DR.vue +53 -92
- package/src/components/__tests__/webgl3dr.spec.ts +29 -48
- package/src/{demo → components/demo}/DemoModal.vue +1 -1
- package/src/{demo → components/demo}/DemoPatientModal.vue +4 -1
- package/src/components/demo/__tests__/DemoModal.spec.ts +25 -0
- package/src/components/demo/__tests__/DemoPatientModal.spec.ts +37 -0
- package/src/components/demo/__tests__/options.spec.ts +25 -0
- package/src/components/demo/licence/DemoLicenceEnableCloudStorageModal.vue +64 -0
- package/src/{demo → components/demo}/licence/DemoLicenceInfoModal.vue +5 -4
- package/src/{demo → components/demo}/licence/DemoLicenceSendToPartyModal.vue +6 -4
- package/src/{demo → components/demo}/licence/DemoLicenceShareToMobileModal.vue +8 -6
- package/src/components/demo/licence/__tests__/DemoLicenceEnableCloudStorageModal.spec.ts +37 -0
- package/src/components/demo/licence/__tests__/DemoLicenceInfoModal.spec.ts +37 -0
- package/src/components/demo/licence/__tests__/DemoLicenceSendToPartyModal.spec.ts +36 -0
- package/src/components/demo/licence/__tests__/DemoLicenceShareToMobileModal.spec.ts +51 -0
- package/src/{demo → components/demo}/options.ts +18 -20
- package/src/components/demo/patient/DemoPatientEnableCloudStorageModal.vue +64 -0
- package/src/{demo → components/demo}/patient/DemoPatientInfoModal.vue +5 -4
- package/src/{demo → components/demo}/patient/DemoPatientSendToPartyModal.vue +4 -3
- package/src/{demo → components/demo}/patient/DemoPatientShareToMobileModal.vue +8 -6
- package/src/components/demo/patient/__tests__/DemoPatientEnableCloudStorageModal.spec.ts +37 -0
- package/src/components/demo/patient/__tests__/DemoPatientInfoModal.spec.ts +37 -0
- package/src/components/demo/patient/__tests__/DemoPatientSendToPartyModal.spec.ts +36 -0
- package/src/components/demo/patient/__tests__/DemoPatientShareToMobileModal.spec.ts +51 -0
- package/src/components/loading/LoadingSpinner.vue +1 -1
- package/src/components/modal/ActionRail.vue +96 -0
- package/src/components/modal/AskAI.vue +250 -0
- package/src/components/modal/CloseViewerModal.vue +104 -0
- package/src/components/modal/MftpWebGL3DRModal.vue +415 -834
- package/src/components/modal/ViewerActionRail.vue +123 -0
- package/src/components/modal/ViewerAnnotationModal.vue +115 -0
- package/src/components/modal/ViewerAnnotations.vue +283 -0
- package/src/components/modal/ViewerDisplaySettings.vue +102 -0
- package/src/components/modal/ViewerNavigationDrawer.vue +90 -0
- package/src/components/modal/ViewerNavigationDrawerContent.vue +126 -0
- package/src/components/modal/ViewerNavigationDrawerFooter.vue +111 -0
- package/src/components/modal/ViewerNavigationDrawerHeader.vue +63 -0
- package/src/components/modal/__tests__/CloseViewerModal.spec.ts +60 -0
- package/src/components/modal/__tests__/{mftp-webgl-3dr-modal.spec.ts → MftpWebGL3DRModal.spec.ts} +47 -298
- package/src/components/modal/__tests__/ViewerAnnotationModal.spec.ts +79 -0
- package/src/components/modal/__tests__/ViewerDisplaySettings.spec.ts +61 -0
- package/src/components/modal/__tests__/ViewerNavigationDrawer.spec.ts +32 -0
- package/src/components/modal/__tests__/ViewerNavigationDrawerContent.spec.ts +29 -0
- package/src/components/modal/__tests__/ViewerNavigationDrawerFooter.spec.ts +43 -0
- package/src/components/modal/__tests__/ViewerNavigationDrawerHeader.spec.ts +37 -0
- package/src/components/modal/actions/Action.vue +40 -0
- package/src/components/modal/actions/Flip3dAction.vue +79 -0
- package/src/components/modal/actions/FlipHorizontalAction.vue +36 -0
- package/src/components/modal/actions/FlipVerticalAction.vue +36 -0
- package/src/components/modal/actions/FullscreenAction.vue +47 -0
- package/src/components/modal/actions/NavigationCubeAction.vue +33 -0
- package/src/components/modal/actions/PanAction.vue +78 -0
- package/src/components/modal/actions/ResetViewAction.vue +29 -0
- package/src/components/modal/actions/Rotate2dAction.vue +78 -0
- package/src/components/modal/actions/Slice3dAction.vue +71 -0
- package/src/components/modal/actions/ZoomAction.vue +70 -0
- package/src/components/modal/actions/__tests__/Action.spec.ts +29 -0
- package/src/components/modal/actions/__tests__/Flip3dAction.spec.ts +48 -0
- package/src/components/modal/actions/__tests__/FlipHorizontalAction.spec.ts +17 -0
- package/src/components/modal/actions/__tests__/FlipVerticalAction.spec.ts +17 -0
- package/src/components/modal/actions/__tests__/FullscreenAction.spec.ts +28 -0
- package/src/components/modal/actions/__tests__/NavigationCubeAction.spec.ts +25 -0
- package/src/components/modal/actions/__tests__/PanAction.spec.ts +46 -0
- package/src/components/modal/actions/__tests__/ResetViewAction.spec.ts +17 -0
- package/src/components/modal/actions/__tests__/Rotate2dAction.spec.ts +23 -0
- package/src/components/modal/actions/__tests__/Slice3dAction.spec.ts +14 -0
- package/src/components/modal/actions/__tests__/ZoomAction.spec.ts +34 -0
- package/src/components/modal/composables/__tests__/useNavigationCubeObserver.spec.ts +56 -0
- package/src/components/modal/composables/useEventListener.ts +22 -0
- package/src/components/modal/composables/useNavigationCubeObserver.ts +104 -0
- package/src/components/selectors/ValueSelector.vue +30 -33
- package/src/components/selectors/__tests__/value-selector.spec.ts +1 -1
- package/src/components/sliders/DoubleSliderSelector.vue +79 -71
- package/src/components/sliders/VerticalSliderSelector.vue +12 -17
- package/src/components/sliders/__tests__/double-slider-selector.spec.ts +1 -1
- package/src/components/sliders/__tests__/vertical-slider-selector.spec.ts +1 -1
- package/src/dataLayer/__tests__/clamp.spec.ts +16 -0
- package/src/dataLayer/__tests__/eventHandlers.spec.ts +38 -0
- package/src/dataLayer/__tests__/getIconForPreset.spec.ts +40 -0
- package/src/dataLayer/__tests__/patchDataOverlay.spec.ts +88 -0
- package/src/dataLayer/__tests__/scanState.spec.ts +93 -0
- package/src/dataLayer/__tests__/useViewer3cr.spec.ts +10 -0
- package/src/dataLayer/__tests__/viewer3cr.spec.ts +331 -0
- package/src/dataLayer/clamp.ts +9 -0
- package/src/dataLayer/eventHandlers.ts +26 -0
- package/src/dataLayer/patchDataOverlay.ts +101 -0
- package/src/dataLayer/scanState.ts +105 -26
- package/src/dataLayer/useViewer3cr.ts +7 -0
- package/src/dataLayer/viewer3cr.ts +410 -0
- package/src/helpers/__tests__/layout-overlay-style.spec.ts +24 -22
- package/src/helpers/__tests__/model-helper.spec.ts +44 -13
- package/src/helpers/layoutOverlayStyle.ts +16 -27
- package/src/helpers/modelHelper.ts +62 -10
- package/src/models/Callbacks.ts +2 -2
- package/src/models/LoadViewerOptions.ts +2 -0
- package/src/models/LoadViewerPayload.ts +1 -0
- package/src/notifications/notification.ts +3 -4
- package/src/plugins/vuetify.ts +5 -0
- package/src/services/gpt/__tests__/gpt.service.spec.ts +27 -0
- package/src/services/gpt/gpt.service.ts +27 -0
- package/static/3cr-types-browser/index.ts +74 -0
- package/static/3cr-types-browser/types/Action.ts +6 -0
- package/static/3cr-types-browser/types/ActionData.ts +4 -0
- package/static/3cr-types-browser/types/AlphaKeys.ts +5 -0
- package/static/3cr-types-browser/types/AnchorPoint.ts +12 -0
- package/static/3cr-types-browser/types/CallToAction.ts +5 -0
- package/static/3cr-types-browser/types/ColourData.ts +7 -0
- package/static/3cr-types-browser/types/ColourPresetData.ts +9 -0
- package/static/3cr-types-browser/types/CurrentDataOverlayState.ts +6 -0
- package/static/3cr-types-browser/types/CurrentScanState.ts +22 -0
- package/static/3cr-types-browser/types/DataOverlay.ts +22 -0
- package/static/3cr-types-browser/types/DataOverlayActions.ts +14 -0
- package/static/3cr-types-browser/types/DataOverlayData.ts +8 -0
- package/static/3cr-types-browser/types/DataOverlayEvent.ts +8 -0
- package/static/3cr-types-browser/types/DecryptionKey.ts +4 -0
- package/static/3cr-types-browser/types/DisplaySettings.ts +10 -0
- package/static/3cr-types-browser/types/EmptyPayload.ts +3 -0
- package/static/3cr-types-browser/types/EnumPayload.ts +4 -0
- package/static/3cr-types-browser/types/FileManagementActions.ts +11 -0
- package/static/3cr-types-browser/types/FlipValue.ts +7 -0
- package/static/3cr-types-browser/types/FrontEndInterfaces.ts +14 -0
- package/static/3cr-types-browser/types/GradientKeys.ts +7 -0
- package/static/3cr-types-browser/types/GreyscalePresetData.ts +6 -0
- package/static/3cr-types-browser/types/InitialDataOverlayState.ts +6 -0
- package/static/3cr-types-browser/types/InitialScanState.ts +19 -0
- package/static/3cr-types-browser/types/InteractionType.ts +8 -0
- package/static/3cr-types-browser/types/InteractivityActions.ts +6 -0
- package/static/3cr-types-browser/types/InteractivityState.ts +4 -0
- package/static/3cr-types-browser/types/InvertTransformData.ts +6 -0
- package/static/3cr-types-browser/types/LayoutActions.ts +6 -0
- package/static/3cr-types-browser/types/LayoutData.ts +7 -0
- package/static/3cr-types-browser/types/LoadDataSet.ts +6 -0
- package/static/3cr-types-browser/types/LoadSessionState.ts +4 -0
- package/static/3cr-types-browser/types/LocalLoadDataset.ts +3 -0
- package/static/3cr-types-browser/types/MovementData.ts +7 -0
- package/static/3cr-types-browser/types/NavigationCubeActions.ts +8 -0
- package/static/3cr-types-browser/types/NavigationCubeData.ts +12 -0
- package/static/3cr-types-browser/types/NavigationCubeTransform.ts +9 -0
- package/static/3cr-types-browser/types/NotificationPayload.ts +7 -0
- package/static/3cr-types-browser/types/NotificationsActions.ts +6 -0
- package/static/3cr-types-browser/types/Object.ts +1 -0
- package/static/3cr-types-browser/types/ObjectColour.ts +7 -0
- package/static/3cr-types-browser/types/ObjectIcon.ts +5 -0
- package/static/3cr-types-browser/types/ObjectInvert.ts +7 -0
- package/static/3cr-types-browser/types/ObjectSize.ts +7 -0
- package/static/3cr-types-browser/types/ObjectSize2D.ts +7 -0
- package/static/3cr-types-browser/types/ObjectVisible.ts +5 -0
- package/static/3cr-types-browser/types/PositionData.ts +14 -0
- package/static/3cr-types-browser/types/PresetsActions.ts +4 -0
- package/static/3cr-types-browser/types/RotationValue.ts +7 -0
- package/static/3cr-types-browser/types/ScanMovementActions.ts +27 -0
- package/static/3cr-types-browser/types/ScanMovementData.ts +3 -0
- package/static/3cr-types-browser/types/ScanOrientationActions.ts +6 -0
- package/static/3cr-types-browser/types/ScanStateActions.ts +4 -0
- package/static/3cr-types-browser/types/ScanView.ts +6 -0
- package/static/3cr-types-browser/types/SettingsData.ts +12 -0
- package/static/3cr-types-browser/types/SlicerData.ts +9 -0
- package/static/3cr-types-browser/types/SliderValue.ts +4 -0
- package/static/3cr-types-browser/types/SlidersActions.ts +18 -0
- package/static/3cr-types-browser/types/Vector2Data.ts +5 -0
- package/static/3cr-types-browser/types/Vector3Data.ts +6 -0
- package/static/3cr-types-browser/types/VectorMovementData.ts +8 -0
- package/static/3cr-types-browser/types/ViewInteractiveMode.ts +5 -0
- package/static/3cr-types-browser/types/ViewOrientation.ts +8 -0
- package/static/3cr-types-browser/types/ViewOrientations.ts +10 -0
- package/static/3cr-types-browser/types/ViewSelectionActions.ts +9 -0
- package/static/3cr-types-browser/types/ViewToggleData.ts +7 -0
- package/static/3cr-types-browser/types/VolumeOrientation.ts +7 -0
- package/test/helper.ts +10 -1
- package/test/setup.ts +13 -0
- package/tsconfig.json +1 -0
- package/vite.config.mts +1 -0
- package/coverage/3cr-viewer-browser/index.html +0 -116
- package/coverage/3cr-viewer-browser/index.ts.html +0 -199
- package/coverage/3cr-viewer-browser/src/App.vue.html +0 -316
- package/coverage/3cr-viewer-browser/src/components/WebGL3DR.vue.html +0 -442
- package/coverage/3cr-viewer-browser/src/components/icons/index.html +0 -116
- package/coverage/3cr-viewer-browser/src/components/icons/liver.vue.html +0 -148
- package/coverage/3cr-viewer-browser/src/components/index.html +0 -116
- package/coverage/3cr-viewer-browser/src/components/loading/LoadingSpinner.vue.html +0 -622
- package/coverage/3cr-viewer-browser/src/components/loading/index.html +0 -116
- package/coverage/3cr-viewer-browser/src/components/modal/MftpWebGL3DRModal.vue.html +0 -3118
- package/coverage/3cr-viewer-browser/src/components/modal/index.html +0 -116
- package/coverage/3cr-viewer-browser/src/components/selectors/ValueSelector.vue.html +0 -358
- package/coverage/3cr-viewer-browser/src/components/selectors/index.html +0 -116
- package/coverage/3cr-viewer-browser/src/components/sliders/DoubleSliderSelector.vue.html +0 -487
- package/coverage/3cr-viewer-browser/src/components/sliders/VerticalSliderSelector.vue.html +0 -358
- package/coverage/3cr-viewer-browser/src/components/sliders/index.html +0 -131
- package/coverage/3cr-viewer-browser/src/dataLayer/iconData.ts.html +0 -118
- package/coverage/3cr-viewer-browser/src/dataLayer/index.html +0 -146
- package/coverage/3cr-viewer-browser/src/dataLayer/payloadHandler.ts.html +0 -463
- package/coverage/3cr-viewer-browser/src/dataLayer/scanState.ts.html +0 -598
- package/coverage/3cr-viewer-browser/src/helpers/index.html +0 -146
- package/coverage/3cr-viewer-browser/src/helpers/layoutOverlayStyle.ts.html +0 -406
- package/coverage/3cr-viewer-browser/src/helpers/modelHelper.ts.html +0 -412
- package/coverage/3cr-viewer-browser/src/helpers/utils.ts.html +0 -133
- package/coverage/3cr-viewer-browser/src/index.html +0 -131
- package/coverage/3cr-viewer-browser/src/main.ts.html +0 -124
- package/coverage/3cr-viewer-browser/src/models/LoadViewerOptions.ts.html +0 -166
- package/coverage/3cr-viewer-browser/src/models/index.html +0 -116
- package/coverage/3cr-viewer-browser/src/notifications/index.html +0 -116
- package/coverage/3cr-viewer-browser/src/notifications/notification.ts.html +0 -238
- package/coverage/3cr-viewer-browser/src/plugins/index.html +0 -131
- package/coverage/3cr-viewer-browser/src/plugins/index.ts.html +0 -136
- package/coverage/3cr-viewer-browser/src/plugins/vuetify.ts.html +0 -220
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -296
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -196
- package/src/dataLayer/__tests__/payload-handler.spec.ts +0 -214
- package/src/dataLayer/payloadHandler.ts +0 -138
- /package/src/dataLayer/{iconData.ts → getIconForPreset.ts} +0 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-menu data-testid="menu" v-model="menuState" :close-on-content-click="false">
|
|
3
|
+
<template #activator="{ props: menu }">
|
|
4
|
+
<Action
|
|
5
|
+
v-bind="menu"
|
|
6
|
+
text="Zoom"
|
|
7
|
+
icon="search"
|
|
8
|
+
:variant="variant"
|
|
9
|
+
/>
|
|
10
|
+
</template>
|
|
11
|
+
<v-list>
|
|
12
|
+
<v-list-item data-testid="zoom-in" prepend-icon="zoom_in" @click.stop="zoomIn">
|
|
13
|
+
<v-list-item-title>Zoom In</v-list-item-title>
|
|
14
|
+
</v-list-item>
|
|
15
|
+
<v-list-item data-testid="zoom-out" prepend-icon="zoom_out" @click.stop="zoomOut">
|
|
16
|
+
<v-list-item-title>Zoom Out</v-list-item-title>
|
|
17
|
+
</v-list-item>
|
|
18
|
+
</v-list>
|
|
19
|
+
</v-menu>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script setup lang="ts">
|
|
23
|
+
import { ScanView } from "@3cr/types-ts";
|
|
24
|
+
import { computed, ref } from "vue";
|
|
25
|
+
import { useViewer3cr } from "@/dataLayer/useViewer3cr";
|
|
26
|
+
|
|
27
|
+
interface Props {
|
|
28
|
+
view: ScanView;
|
|
29
|
+
variant?: 'button' | 'menu-item';
|
|
30
|
+
percentage?: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type Emits = {
|
|
34
|
+
'update:modal': [value: boolean];
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
38
|
+
variant: 'button',
|
|
39
|
+
percentage: 10
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const emit = defineEmits<Emits>();
|
|
43
|
+
|
|
44
|
+
const viewer3cr = useViewer3cr();
|
|
45
|
+
const menu = ref<boolean>(false);
|
|
46
|
+
|
|
47
|
+
const menuState = computed({
|
|
48
|
+
get(): boolean {
|
|
49
|
+
return menu.value;
|
|
50
|
+
},
|
|
51
|
+
set(value: boolean): void {
|
|
52
|
+
menu.value = value;
|
|
53
|
+
emit('update:modal', value);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
async function zoomOut(): Promise<void> {
|
|
58
|
+
await viewer3cr.zoomOut(props.view, props.percentage);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function zoomIn(): Promise<void> {
|
|
62
|
+
await viewer3cr.zoomIn(props.view, props.percentage);
|
|
63
|
+
}
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<style scoped lang="scss">
|
|
67
|
+
.v-list-item {
|
|
68
|
+
user-select: none;
|
|
69
|
+
}
|
|
70
|
+
</style>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { expect, describe, it, vi } from "vitest";
|
|
2
|
+
import { mountVuetify } from "~/helper";
|
|
3
|
+
import Action from "@/components/modal/actions/Action.vue";
|
|
4
|
+
|
|
5
|
+
describe('Action tests', () => {
|
|
6
|
+
it('should render as button', async () => {
|
|
7
|
+
const props = { text: '', icon: '', variant: 'button', onClick: vi.fn() };
|
|
8
|
+
const wrapper = mountVuetify(Action, props);
|
|
9
|
+
const button = wrapper.findComponent('[data-testid="button"]');
|
|
10
|
+
expect(button).toBeTruthy();
|
|
11
|
+
await button.trigger('click');
|
|
12
|
+
expect(props.onClick).toHaveBeenCalled();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should render as menu-item', async () => {
|
|
16
|
+
const props = { text: '', icon: '', variant: 'menu-item', onClick: vi.fn() };
|
|
17
|
+
const wrapper = mountVuetify(Action, props);
|
|
18
|
+
const menuItem = wrapper.findComponent('[data-testid="menu-item"]');
|
|
19
|
+
expect(menuItem).toBeTruthy();
|
|
20
|
+
await menuItem.trigger('click');
|
|
21
|
+
expect(props.onClick).toHaveBeenCalled();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should render nothing', async () => {
|
|
25
|
+
const props = { text: '', icon: '', variant: 'invalid', onClick: vi.fn() };
|
|
26
|
+
const wrapper = mountVuetify(Action, props);
|
|
27
|
+
expect(wrapper.text()).toBe('');
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {describe, expect, it, vi} from "vitest";
|
|
2
|
+
import {ScanView} from "@3cr/types-ts";
|
|
3
|
+
import {mountVuetify} from "~/helper";
|
|
4
|
+
import {DOMWrapper} from "@vue/test-utils";
|
|
5
|
+
import Flip3dAction from "@/components/modal/actions/Flip3dAction.vue";
|
|
6
|
+
import {Viewer3cr} from "@/dataLayer/viewer3cr";
|
|
7
|
+
|
|
8
|
+
describe('Flip3dAction tests', () => {
|
|
9
|
+
const spy = vi.spyOn(Viewer3cr.prototype, 'invertTransform').mockResolvedValue();
|
|
10
|
+
const props = { view: ScanView.Volume };
|
|
11
|
+
const wrapper = mountVuetify(Flip3dAction, props);
|
|
12
|
+
|
|
13
|
+
it('should flip sagittal', async () => {
|
|
14
|
+
const activator = wrapper.findComponent('[data-testid="activator"]');
|
|
15
|
+
await activator.trigger('click');
|
|
16
|
+
const menu = new DOMWrapper(document.querySelector('[data-testid="menu"]'));
|
|
17
|
+
const sagittal = menu.findComponent('[data-testid="sagittal"]');
|
|
18
|
+
await sagittal.trigger('click');
|
|
19
|
+
expect(spy).toHaveBeenCalledWith(true, false, false);
|
|
20
|
+
await activator.trigger('click');
|
|
21
|
+
await sagittal.trigger('click');
|
|
22
|
+
expect(spy).toHaveBeenCalledWith(false, false, false);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should flip coronal', async () => {
|
|
26
|
+
const activator = wrapper.findComponent('[data-testid="activator"]');
|
|
27
|
+
await activator.trigger('click');
|
|
28
|
+
const menu = new DOMWrapper(document.querySelector('[data-testid="menu"]'));
|
|
29
|
+
const coronal = menu.findComponent('[data-testid="coronal"]');
|
|
30
|
+
await coronal.trigger('click');
|
|
31
|
+
expect(spy).toHaveBeenCalledWith(false, true, false);
|
|
32
|
+
await activator.trigger('click');
|
|
33
|
+
await coronal.trigger('click');
|
|
34
|
+
expect(spy).toHaveBeenCalledWith(false, false, false);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should flip transverse', async () => {
|
|
38
|
+
const activator = wrapper.findComponent('[data-testid="activator"]');
|
|
39
|
+
await activator.trigger('click');
|
|
40
|
+
const menu = new DOMWrapper(document.querySelector('[data-testid="menu"]'));
|
|
41
|
+
const transverse = menu.findComponent('[data-testid="transverse"]');
|
|
42
|
+
await transverse.trigger('click');
|
|
43
|
+
expect(spy).toHaveBeenCalledWith(false, false, true);
|
|
44
|
+
await activator.trigger('click');
|
|
45
|
+
await transverse.trigger('click');
|
|
46
|
+
expect(spy).toHaveBeenCalledWith(false, false, false);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { mountVuetify } from "~/helper";
|
|
3
|
+
import { ScanView } from "@3cr/types-ts";
|
|
4
|
+
import FlipHorizontalAction from "@/components/modal/actions/FlipHorizontalAction.vue";
|
|
5
|
+
import {Viewer3cr} from "@/dataLayer/viewer3cr";
|
|
6
|
+
|
|
7
|
+
describe('FlipHorizontalAction tests', () => {
|
|
8
|
+
const spy = vi.spyOn(Viewer3cr.prototype, 'flipHorizontally').mockResolvedValue();
|
|
9
|
+
const props = { view: ScanView.Sagittal };
|
|
10
|
+
|
|
11
|
+
it('should flip horizontally', async () => {
|
|
12
|
+
const wrapper = mountVuetify(FlipHorizontalAction, props);
|
|
13
|
+
const action = wrapper.findComponent('[data-testid="button"]');
|
|
14
|
+
await action.trigger('click');
|
|
15
|
+
expect(spy).toHaveBeenCalledWith(props.view, true);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { mountVuetify } from "~/helper";
|
|
3
|
+
import { ScanView } from "@3cr/types-ts";
|
|
4
|
+
import FlipVerticalAction from "@/components/modal/actions/FlipVerticalAction.vue";
|
|
5
|
+
import {Viewer3cr} from "@/dataLayer/viewer3cr";
|
|
6
|
+
|
|
7
|
+
describe('FlipVerticalAction tests', () => {
|
|
8
|
+
const spy = vi.spyOn(Viewer3cr.prototype, 'flipVertically').mockResolvedValue();
|
|
9
|
+
const props = { view: ScanView.Sagittal };
|
|
10
|
+
|
|
11
|
+
it('should flip horizontally', async () => {
|
|
12
|
+
const wrapper = mountVuetify(FlipVerticalAction, props);
|
|
13
|
+
const action = wrapper.findComponent('[data-testid="button"]');
|
|
14
|
+
await action.trigger('click');
|
|
15
|
+
expect(spy).toHaveBeenCalledWith(props.view, true);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { mountVuetify } from "~/helper";
|
|
3
|
+
import { CurrentScanState, LayoutActions, ScanView } from "@3cr/types-ts";
|
|
4
|
+
import { Viewer3cr } from "@/dataLayer/viewer3cr";
|
|
5
|
+
import { previousLayout, scanState } from "@/dataLayer/scanState";
|
|
6
|
+
import FullscreenAction from "../FullscreenAction.vue";
|
|
7
|
+
|
|
8
|
+
describe('FullscreenAction tests', () => {
|
|
9
|
+
const props = { view: ScanView.Coronal };
|
|
10
|
+
const layoutsSpy = vi.spyOn(Viewer3cr.prototype, 'layouts').mockResolvedValue();
|
|
11
|
+
const viewSpy = vi.spyOn(Viewer3cr.prototype, 'viewSelection').mockResolvedValue();
|
|
12
|
+
|
|
13
|
+
it('should fullscreen', async () => {
|
|
14
|
+
const wrapper = mountVuetify(FullscreenAction, props);
|
|
15
|
+
const button = wrapper.find('[data-testid="button"]');
|
|
16
|
+
await button.trigger('click');
|
|
17
|
+
expect(layoutsSpy).toHaveBeenCalledWith(LayoutActions.lo01);
|
|
18
|
+
expect(viewSpy).toHaveBeenCalledWith(`vs_0${props.view + 1}`);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should exit fullscreen', async () => {
|
|
22
|
+
scanState.value = { Layout: { PositionData: [{}] } } as CurrentScanState;
|
|
23
|
+
const wrapper = mountVuetify(FullscreenAction, props);
|
|
24
|
+
const button = wrapper.find('[data-testid="button"]');
|
|
25
|
+
await button.trigger('click');
|
|
26
|
+
expect(layoutsSpy).toHaveBeenCalledWith(previousLayout.value);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { Viewer3cr } from "@/dataLayer/viewer3cr";
|
|
3
|
+
import { mountVuetify } from "~/helper";
|
|
4
|
+
import { scanState } from "@/dataLayer/scanState";
|
|
5
|
+
import { CurrentScanState } from "@3cr/types-ts";
|
|
6
|
+
import NavigationCubeAction from "@/components/modal/actions/NavigationCubeAction.vue";
|
|
7
|
+
|
|
8
|
+
describe('NavigationCubeAction tests', () => {
|
|
9
|
+
const navCubeInteractivitySpy = vi.spyOn(Viewer3cr.prototype, 'setNavCubeInteractivity').mockResolvedValue();
|
|
10
|
+
|
|
11
|
+
it('should show navigation cube', async () => {
|
|
12
|
+
const wrapper = mountVuetify(NavigationCubeAction);
|
|
13
|
+
const switchCmp = wrapper.findComponent('[data-testid="switch"]');
|
|
14
|
+
await switchCmp.trigger('click');
|
|
15
|
+
expect(navCubeInteractivitySpy).toHaveBeenCalledWith(true);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should hide navigation cube', async () => {
|
|
19
|
+
scanState.value = { NavigationCube: { Interactivity: { Value: true } } } as CurrentScanState;
|
|
20
|
+
const wrapper = mountVuetify(NavigationCubeAction);
|
|
21
|
+
const switchWrapper = wrapper.findComponent('[data-testid="switch"]');
|
|
22
|
+
await switchWrapper.trigger('click');
|
|
23
|
+
expect(navCubeInteractivitySpy).toHaveBeenCalledWith(false);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { mountVuetify } from "~/helper";
|
|
3
|
+
import { ScanView } from "@3cr/types-ts";
|
|
4
|
+
import { Viewer3cr } from "@/dataLayer/viewer3cr";
|
|
5
|
+
import PanAction from "@/components/modal/actions/PanAction.vue";
|
|
6
|
+
|
|
7
|
+
describe('PanAction tests', () => {
|
|
8
|
+
const spy = vi.spyOn(Viewer3cr.prototype, 'hoverOverCanvas').mockResolvedValue();
|
|
9
|
+
const element = { addEventListener: vi.fn(), removeEventListener: vi.fn() };
|
|
10
|
+
const windowAddSpy = vi.spyOn(window, 'addEventListener');
|
|
11
|
+
// const windowRemoveSpy = vi.spyOn(window, 'removeEventListener');
|
|
12
|
+
|
|
13
|
+
it('should setup event listeners', async () => {
|
|
14
|
+
const props = { view: ScanView.Volume, element };
|
|
15
|
+
mountVuetify(PanAction, props);
|
|
16
|
+
expect(element.addEventListener).toHaveBeenCalledWith('mousedown', expect.anything());
|
|
17
|
+
expect(element.addEventListener).toHaveBeenCalledWith('mouseenter', expect.anything());
|
|
18
|
+
expect(element.addEventListener).toHaveBeenCalledWith('mouseleave', expect.anything());
|
|
19
|
+
expect(windowAddSpy).toHaveBeenCalledWith('mousemove', expect.anything());
|
|
20
|
+
expect(windowAddSpy).toHaveBeenCalledWith('mouseup', expect.anything());
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should disable unity input while active', async () => {
|
|
24
|
+
const props = { view: ScanView.Volume, element };
|
|
25
|
+
const wrapper = mountVuetify(PanAction, props);
|
|
26
|
+
const button = wrapper.findComponent('[data-testid="button"]');
|
|
27
|
+
await button.trigger('click');
|
|
28
|
+
expect(spy).toHaveBeenCalledWith(false);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should call event handlers', async () => {
|
|
32
|
+
const element = document.createElement('div');
|
|
33
|
+
document.body.appendChild(element);
|
|
34
|
+
const props = { view: ScanView.Volume, element };
|
|
35
|
+
const wrapper = mountVuetify(PanAction, props);
|
|
36
|
+
const button = wrapper.findComponent('[data-testid="button"]');
|
|
37
|
+
await button.trigger('click');
|
|
38
|
+
element.dispatchEvent(new MouseEvent('mousedown', { clientX: 0, clientY: 0 }));
|
|
39
|
+
window.dispatchEvent(new MouseEvent('mousemove', { clientX: 0, clientY: 0 }));
|
|
40
|
+
window.dispatchEvent(new MouseEvent('mouseup', { clientX: 0, clientY: 0 }));
|
|
41
|
+
element.dispatchEvent(new MouseEvent('mouseenter', { clientX: 0, clientY: 0 }));
|
|
42
|
+
element.dispatchEvent(new MouseEvent('mouseleave', { clientX: 0, clientY: 0 }));
|
|
43
|
+
expect(spy).toHaveBeenCalledWith(false);
|
|
44
|
+
expect(spy).toHaveBeenCalledWith(true);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { Viewer3cr } from "@/dataLayer/viewer3cr";
|
|
3
|
+
import { mountVuetify } from "~/helper";
|
|
4
|
+
import { ViewSelectionActions } from "@3cr/types-ts";
|
|
5
|
+
import ResetViewAction from "@/components/modal/actions/ResetViewAction.vue";
|
|
6
|
+
|
|
7
|
+
describe('ResetViewAction tests', () => {
|
|
8
|
+
const viewSelectionSpy = vi.spyOn(Viewer3cr.prototype, 'viewSelection').mockResolvedValue();
|
|
9
|
+
|
|
10
|
+
it('should reset view', async () => {
|
|
11
|
+
const wrapper = mountVuetify(ResetViewAction);
|
|
12
|
+
const button = wrapper.findComponent('[data-testid="button"]');
|
|
13
|
+
await button.trigger('click');
|
|
14
|
+
expect(viewSelectionSpy).toHaveBeenCalledWith(ViewSelectionActions.vs06);
|
|
15
|
+
expect(viewSelectionSpy).toHaveBeenCalledWith(ViewSelectionActions.vs05);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { mountVuetify } from "~/helper";
|
|
3
|
+
import { ScanView } from "@3cr/types-ts";
|
|
4
|
+
import { DOMWrapper } from "@vue/test-utils";
|
|
5
|
+
import { Viewer3cr } from "@/dataLayer/viewer3cr";
|
|
6
|
+
import Rotate2dAction from "@/components/modal/actions/Rotate2dAction.vue";
|
|
7
|
+
|
|
8
|
+
describe('Rotate2dAction tests', () => {
|
|
9
|
+
it('should apply rotation', async () => {
|
|
10
|
+
const props = { view: ScanView.Sagittal };
|
|
11
|
+
const spy = vi.spyOn(Viewer3cr.prototype, 'rotateByDeg').mockResolvedValue();
|
|
12
|
+
const wrapper = mountVuetify(Rotate2dAction, props);
|
|
13
|
+
const button = wrapper.findComponent('[data-testid="button"]');
|
|
14
|
+
await button.trigger('click');
|
|
15
|
+
|
|
16
|
+
const menu = new DOMWrapper(document.querySelector('[data-testid="menu"]'));
|
|
17
|
+
const textField = menu.findComponent('[data-testid="rotation"]');
|
|
18
|
+
await textField.setValue(10);
|
|
19
|
+
const rotate = menu.findComponent('[data-testid="rotate"]');
|
|
20
|
+
await rotate.trigger('click');
|
|
21
|
+
expect(spy).toHaveBeenCalledWith(props.view, 10);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {describe, expect, it} from "vitest";
|
|
2
|
+
import {mountVuetify} from "~/helper";
|
|
3
|
+
import Slice3dAction from "@/components/modal/actions/Slice3dAction.vue";
|
|
4
|
+
import {DOMWrapper} from "@vue/test-utils";
|
|
5
|
+
|
|
6
|
+
describe('Slice3dAction tests', () => {
|
|
7
|
+
it('should open slice menu', async () => {
|
|
8
|
+
const wrapper = mountVuetify(Slice3dAction);
|
|
9
|
+
const button = wrapper.findComponent('[data-testid="button"]');
|
|
10
|
+
await button.trigger('click');
|
|
11
|
+
const menu = new DOMWrapper(document.querySelector('[data-testid="menu"]'));
|
|
12
|
+
expect(menu.exists()).toBeTruthy();
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {describe, expect, it, vi} from "vitest";
|
|
2
|
+
import { ScanView } from "@3cr/types-ts";
|
|
3
|
+
import { mountVuetify } from "~/helper";
|
|
4
|
+
import { Viewer3cr } from "@/dataLayer/viewer3cr";
|
|
5
|
+
import ZoomAction from "@/components/modal/actions/ZoomAction.vue";
|
|
6
|
+
import {DOMWrapper} from "@vue/test-utils";
|
|
7
|
+
|
|
8
|
+
describe('ZoomAction tests', () => {
|
|
9
|
+
const props = { view: ScanView.Sagittal, percentage: 25 };
|
|
10
|
+
const zoomInSpy = vi.spyOn(Viewer3cr.prototype, 'zoomIn');
|
|
11
|
+
const zoomOutSpy = vi.spyOn(Viewer3cr.prototype, 'zoomOut');
|
|
12
|
+
|
|
13
|
+
it('should zoom in', async () => {
|
|
14
|
+
const wrapper = mountVuetify(ZoomAction, props);
|
|
15
|
+
const button = wrapper.findComponent('[data-testid="button"]');
|
|
16
|
+
await button.trigger('click');
|
|
17
|
+
|
|
18
|
+
const menu = new DOMWrapper(document.querySelector('[data-testid="menu"]'));
|
|
19
|
+
const zoomIn = menu.findComponent('[data-testid="zoom-in"]');
|
|
20
|
+
await zoomIn.trigger('click');
|
|
21
|
+
expect(zoomInSpy).toHaveBeenCalledWith(props.view, props.percentage);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should zoom out', async () => {
|
|
25
|
+
const wrapper = mountVuetify(ZoomAction, props);
|
|
26
|
+
const button = wrapper.findComponent('[data-testid="button"]');
|
|
27
|
+
await button.trigger('click');
|
|
28
|
+
|
|
29
|
+
const menu = new DOMWrapper(document.querySelector('[data-testid="menu"]'));
|
|
30
|
+
const zoomOut = menu.findComponent('[data-testid="zoom-out"]');
|
|
31
|
+
await zoomOut.trigger('click');
|
|
32
|
+
expect(zoomOutSpy).toHaveBeenCalledWith(props.view, props.percentage);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { useNavigationCubeObserver } from "@/components/modal/composables/useNavigationCubeObserver";
|
|
3
|
+
import { isFullscreen, isLayout1x3, isLayout2x2, scanState } from "@/dataLayer/scanState";
|
|
4
|
+
import { Viewer3cr } from "@/dataLayer/viewer3cr";
|
|
5
|
+
import { AnchorPoint, CurrentScanState, ScanView } from "@3cr/types-ts";
|
|
6
|
+
import { flushPromises } from "@vue/test-utils";
|
|
7
|
+
|
|
8
|
+
function setupMocks(state: CurrentScanState, fullscreen: boolean, layout1x3: boolean, layout2x2: boolean): void {
|
|
9
|
+
vi.spyOn(scanState, 'value', 'get').mockReturnValue(state);
|
|
10
|
+
vi.spyOn(isFullscreen, 'value', 'get').mockReturnValue(fullscreen);
|
|
11
|
+
vi.spyOn(isLayout1x3, 'value', 'get').mockReturnValue(layout1x3);
|
|
12
|
+
vi.spyOn(isLayout2x2, 'value', 'get').mockReturnValue(layout2x2);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe('useNavigationCubeObserver tests', () => {
|
|
16
|
+
vi.spyOn(ResizeObserver.prototype, 'observe').mockReturnValue()
|
|
17
|
+
vi.spyOn(ResizeObserver.prototype, 'unobserve').mockReturnValue();
|
|
18
|
+
|
|
19
|
+
const canvas = { clientWidth: 100, clientHeight: 100 } as HTMLCanvasElement;
|
|
20
|
+
const fullscreen2d = { CurrentView: ScanView.Sagittal, NavigationCube: { Interactivity: { Value: true } } } as CurrentScanState;
|
|
21
|
+
const fullscreen3d = { CurrentView: ScanView.Volume, NavigationCube: { Interactivity: { Value: true } } } as CurrentScanState;
|
|
22
|
+
|
|
23
|
+
const visibilitySpy = vi.spyOn(Viewer3cr.prototype, 'setNavCubeVisibility');
|
|
24
|
+
const positionSizeSpy = vi.spyOn(Viewer3cr.prototype, 'setNavCubePositionSize');
|
|
25
|
+
|
|
26
|
+
it('should update on 3d fullscreen changes', async () => {
|
|
27
|
+
setupMocks(fullscreen3d, true, false, false);
|
|
28
|
+
useNavigationCubeObserver(canvas);
|
|
29
|
+
await flushPromises();
|
|
30
|
+
expect(visibilitySpy).toBeCalledWith(true);
|
|
31
|
+
expect(positionSizeSpy).toBeCalledWith(AnchorPoint.TOP_LEFT, 90, 90, 10, 10);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should update on 2d fullscreen changes', async () => {
|
|
35
|
+
setupMocks(fullscreen2d, true, false, false);
|
|
36
|
+
useNavigationCubeObserver(canvas);
|
|
37
|
+
await flushPromises();
|
|
38
|
+
expect(visibilitySpy).toBeCalledWith(false);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should update on 1x3 layout changes', async () => {
|
|
42
|
+
setupMocks(fullscreen3d, false, true, false);
|
|
43
|
+
useNavigationCubeObserver(canvas);
|
|
44
|
+
await flushPromises();
|
|
45
|
+
expect(visibilitySpy).toBeCalledWith(true);
|
|
46
|
+
expect(positionSizeSpy).toBeCalledWith(AnchorPoint.TOP_LEFT, 56, 90, 10, 10);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should update on 2x2 layout changes', async () => {
|
|
50
|
+
setupMocks(fullscreen3d, false, false, true);
|
|
51
|
+
useNavigationCubeObserver(canvas);
|
|
52
|
+
await flushPromises();
|
|
53
|
+
expect(visibilitySpy).toBeCalledWith(true);
|
|
54
|
+
expect(positionSizeSpy).toBeCalledWith(AnchorPoint.TOP_LEFT, 40, 40, 10, 10);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { isRef, onBeforeUnmount, onMounted, Ref, unref, watch } from 'vue';
|
|
2
|
+
|
|
3
|
+
type Target = Ref<EventTarget> | EventTarget;
|
|
4
|
+
type EventType = keyof HTMLElementEventMap;
|
|
5
|
+
type Handler = (e: Event) => void | Promise<void>;
|
|
6
|
+
|
|
7
|
+
export function useEventListener(target: Target, event: EventType, handler: Handler) {
|
|
8
|
+
if (isRef(target)) {
|
|
9
|
+
watch(target, (value, oldValue) => {
|
|
10
|
+
oldValue?.removeEventListener(event, handler);
|
|
11
|
+
value?.addEventListener(event, handler);
|
|
12
|
+
});
|
|
13
|
+
} else {
|
|
14
|
+
onMounted(() => {
|
|
15
|
+
target.addEventListener(event, handler);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
onBeforeUnmount(() => {
|
|
20
|
+
unref(target)?.removeEventListener(event, handler);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { isFullscreen, isLayout1x3, isLayout2x2, scanState } from "@/dataLayer/scanState";
|
|
2
|
+
import { AnchorPoint, ScanView } from "@3cr/types-ts";
|
|
3
|
+
import { useViewer3cr } from "@/dataLayer/useViewer3cr";
|
|
4
|
+
import { ComputedRef, WatchStopHandle, computed, watch } from "vue";
|
|
5
|
+
|
|
6
|
+
const isNavigationCubeEnabled = computed(() => {
|
|
7
|
+
return scanState.value.NavigationCube.Interactivity.Value;
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
function getCanvasDimensions(canvas: HTMLCanvasElement): [number, number] {
|
|
11
|
+
const width = canvas.clientWidth * window.devicePixelRatio;
|
|
12
|
+
const height = canvas.clientHeight * window.devicePixelRatio;
|
|
13
|
+
return [width, height];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getNavigationCubeSize(canvas: HTMLCanvasElement): number {
|
|
17
|
+
const [width] = getCanvasDimensions(canvas);
|
|
18
|
+
return Math.floor(width * 0.1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function useNavigationCubeObserver(canvas: HTMLCanvasElement): ResizeObserver {
|
|
22
|
+
const viewer3cr = useViewer3cr();
|
|
23
|
+
|
|
24
|
+
let refresh: (() => Promise<void>) | undefined;
|
|
25
|
+
let handles: WatchStopHandle[] = [];
|
|
26
|
+
|
|
27
|
+
async function onFullscreen(): Promise<void> {
|
|
28
|
+
const [width, height] = getCanvasDimensions(canvas);
|
|
29
|
+
const navCubeSize = getNavigationCubeSize(canvas);
|
|
30
|
+
if (scanState.value.CurrentView === ScanView.Volume) {
|
|
31
|
+
const xOffset = width - navCubeSize;
|
|
32
|
+
const yOffset = height - navCubeSize;
|
|
33
|
+
await viewer3cr.setNavCubeVisibility(true);
|
|
34
|
+
await viewer3cr.setNavCubePositionSize(AnchorPoint.TOP_LEFT, xOffset, yOffset, navCubeSize, navCubeSize);
|
|
35
|
+
} else {
|
|
36
|
+
await viewer3cr.setNavCubeVisibility(false);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function onLayout1x3(): Promise<void> {
|
|
41
|
+
const [width, height] = getCanvasDimensions(canvas);
|
|
42
|
+
const navCubeSize = getNavigationCubeSize(canvas);
|
|
43
|
+
const xOffset = Math.floor(width - (height / 3) - navCubeSize);
|
|
44
|
+
const yOffset = height - navCubeSize;
|
|
45
|
+
await viewer3cr.setNavCubeVisibility(true);
|
|
46
|
+
await viewer3cr.setNavCubePositionSize(AnchorPoint.TOP_LEFT, xOffset, yOffset, navCubeSize, navCubeSize);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function onLayout2x2(): Promise<void> {
|
|
50
|
+
const [width, height] = getCanvasDimensions(canvas);
|
|
51
|
+
const navCubeSize = getNavigationCubeSize(canvas);
|
|
52
|
+
const xOffset = Math.floor((width / 2) - navCubeSize);
|
|
53
|
+
const yOffset = Math.floor((height / 2) - navCubeSize);
|
|
54
|
+
await viewer3cr.setNavCubeVisibility(true);
|
|
55
|
+
await viewer3cr.setNavCubePositionSize(AnchorPoint.TOP_LEFT, xOffset, yOffset, navCubeSize, navCubeSize);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function createWatch(ref: ComputedRef<boolean>, callback: () => Promise<void>): WatchStopHandle {
|
|
59
|
+
return watch(ref, async (value: boolean) => {
|
|
60
|
+
if (value) {
|
|
61
|
+
refresh = callback;
|
|
62
|
+
await refresh();
|
|
63
|
+
}
|
|
64
|
+
}, { immediate: true });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function enable(): void {
|
|
68
|
+
if (handles.length === 0) {
|
|
69
|
+
handles.push(createWatch(isFullscreen, onFullscreen));
|
|
70
|
+
handles.push(createWatch(isLayout1x3, onLayout1x3));
|
|
71
|
+
handles.push(createWatch(isLayout2x2, onLayout2x2));
|
|
72
|
+
handles.push(watch([
|
|
73
|
+
() => scanState.value.CurrentView,
|
|
74
|
+
() => scanState.value.NavigationCube.Interactivity.Value
|
|
75
|
+
], async () => {
|
|
76
|
+
if (refresh) {
|
|
77
|
+
await refresh();
|
|
78
|
+
}
|
|
79
|
+
}));
|
|
80
|
+
observer.observe(canvas);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function disable(): Promise<void> {
|
|
85
|
+
if (handles.length > 0) {
|
|
86
|
+
handles.forEach(handle => handle());
|
|
87
|
+
handles = [];
|
|
88
|
+
observer.unobserve(canvas);
|
|
89
|
+
await viewer3cr.setNavCubeVisibility(false);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const observer = new ResizeObserver(async () => {
|
|
94
|
+
if (refresh) {
|
|
95
|
+
await refresh();
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
watch(isNavigationCubeEnabled, (value: boolean) => {
|
|
100
|
+
value ? enable() : disable();
|
|
101
|
+
}, { immediate: true });
|
|
102
|
+
|
|
103
|
+
return observer;
|
|
104
|
+
}
|
|
@@ -1,6 +1,31 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="single-slider-selector">
|
|
3
|
+
<v-row no-gutters align="center" justify="space-between">
|
|
4
|
+
<v-col>
|
|
5
|
+
<span class="text-caption">
|
|
6
|
+
{{ label }}
|
|
7
|
+
</span>
|
|
8
|
+
</v-col>
|
|
9
|
+
<v-col cols="4">
|
|
10
|
+
<v-text-field
|
|
11
|
+
:model-value="sliderValue"
|
|
12
|
+
@update:modelValue="sliderValue = toNumber($event)"
|
|
13
|
+
class="mt-0 pt-0"
|
|
14
|
+
hide-details
|
|
15
|
+
variant="outlined"
|
|
16
|
+
density="compact"
|
|
17
|
+
type="number"
|
|
18
|
+
:append-inner-icon="prepend"
|
|
19
|
+
></v-text-field>
|
|
20
|
+
</v-col>
|
|
21
|
+
</v-row>
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
1
25
|
<script setup lang="ts">
|
|
2
|
-
import { computed
|
|
26
|
+
import { computed } from "vue";
|
|
3
27
|
import { toNumber } from "@/helpers/utils";
|
|
28
|
+
import { clamp } from "@/dataLayer/clamp";
|
|
4
29
|
|
|
5
30
|
export interface Props {
|
|
6
31
|
value: number;
|
|
@@ -21,52 +46,24 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
21
46
|
step: 1,
|
|
22
47
|
prepend: "",
|
|
23
48
|
});
|
|
49
|
+
|
|
24
50
|
const emit = defineEmits<{
|
|
25
51
|
"update:value": [number];
|
|
26
52
|
}>();
|
|
53
|
+
|
|
27
54
|
const sliderValue = computed({
|
|
28
55
|
get() {
|
|
29
56
|
return Math.floor(props.value);
|
|
30
57
|
},
|
|
31
58
|
set(value: number) {
|
|
32
|
-
|
|
33
|
-
if (value > props.max) val = props.max;
|
|
34
|
-
if (value < props.min) val = props.min;
|
|
59
|
+
const val = clamp(value, props.min, props.max);
|
|
35
60
|
emit("update:value", val);
|
|
36
61
|
},
|
|
37
62
|
});
|
|
38
63
|
|
|
39
|
-
defineExpose({
|
|
40
|
-
sliderValue,
|
|
41
|
-
});
|
|
64
|
+
defineExpose({ sliderValue });
|
|
42
65
|
</script>
|
|
43
66
|
|
|
44
|
-
<template>
|
|
45
|
-
<div class="single-slider-selector">
|
|
46
|
-
<v-row no-gutters align="center" justify="space-between">
|
|
47
|
-
<v-col>
|
|
48
|
-
<span class="text-caption">
|
|
49
|
-
{{ label }}
|
|
50
|
-
</span>
|
|
51
|
-
</v-col>
|
|
52
|
-
<v-col cols="4">
|
|
53
|
-
<v-text-field
|
|
54
|
-
:model-value="sliderValue"
|
|
55
|
-
@update:modelValue="sliderValue = toNumber($event)"
|
|
56
|
-
outlined
|
|
57
|
-
dense
|
|
58
|
-
class="mt-0 pt-0"
|
|
59
|
-
hide-details
|
|
60
|
-
variant="outlined"
|
|
61
|
-
density="compact"
|
|
62
|
-
type="number"
|
|
63
|
-
:append-inner-icon="prepend"
|
|
64
|
-
></v-text-field>
|
|
65
|
-
</v-col>
|
|
66
|
-
</v-row>
|
|
67
|
-
</div>
|
|
68
|
-
</template>
|
|
69
|
-
|
|
70
67
|
<style>
|
|
71
68
|
.single-slider-selector
|
|
72
69
|
.v-text-field.v-text-field--enclosed:not(.v-text-field--rounded)
|