393 lines
10 KiB
Vue
393 lines
10 KiB
Vue
<template>
|
|
<div
|
|
id="app"
|
|
class="uk-height-1-1 uk-margin-remove uk-padding-remove"
|
|
:class="handleTheme"
|
|
>
|
|
<div id="tour-header"></div>
|
|
<appContent />
|
|
<v-tour
|
|
name="guidedTour"
|
|
:steps="tourSteps"
|
|
:callbacks="tourCallbacks"
|
|
:options="{ highlight: true }"
|
|
></v-tour>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
// Import components
|
|
import appContent from "./components/appContent.vue";
|
|
|
|
// Key Codes
|
|
const keyCodes = {
|
|
pgup: 33,
|
|
pgdn: 34,
|
|
left: 37,
|
|
up: 38,
|
|
right: 39,
|
|
down: 40,
|
|
enter: 13,
|
|
esc: 27,
|
|
shift: 16
|
|
};
|
|
|
|
// Export main app
|
|
export default {
|
|
name: "App",
|
|
|
|
components: {
|
|
appContent
|
|
},
|
|
|
|
data: function() {
|
|
return {
|
|
keysDown: {},
|
|
systemDark: undefined,
|
|
themeObserver: undefined,
|
|
tourSteps: [
|
|
{
|
|
target: "#tour-header", // We're using document.querySelector() under the hood
|
|
header: {
|
|
title: "Welcome to OpenFlexure eV"
|
|
},
|
|
content: `Click Next to learn how to use OpenFlexure eV`,
|
|
params: {
|
|
placement: "bottom"
|
|
}
|
|
},
|
|
{
|
|
target: "#saved-connections-grid",
|
|
header: {
|
|
title: "Saved microscopes"
|
|
},
|
|
content: `Connect to your saved microscopes for faster access`
|
|
},
|
|
{
|
|
target: "#nearby-connections-grid",
|
|
header: {
|
|
title: "Nearby microscopes"
|
|
},
|
|
content: `Connect to microscopes found on your network`
|
|
},
|
|
{
|
|
target: "#new-connection-card",
|
|
header: {
|
|
title: "New connection"
|
|
},
|
|
content: `Connect locally if you're running on a microscope, \nor open a new remote connection to a microscope`
|
|
},
|
|
{
|
|
target: "#gallery-tab-icon",
|
|
header: {
|
|
title: "Capture gallery"
|
|
},
|
|
content: `View and download your microscope images from the gallery tab`
|
|
},
|
|
{
|
|
target: "#navigate-tab-icon",
|
|
header: {
|
|
title: "Navigate around your sample"
|
|
},
|
|
content: `Move your microscope stage and perform autofocus from the navigate tab`
|
|
},
|
|
{
|
|
target: "#capture-tab-icon",
|
|
header: {
|
|
title: "Capture microscope images"
|
|
},
|
|
content: `Take images and simple tile scans from the capture tab`
|
|
},
|
|
{
|
|
target: "#settings-tab-icon",
|
|
header: {
|
|
title: "Change settings"
|
|
},
|
|
content: `Change app and microscope settings, including microscope calibration, from the settings tab`
|
|
},
|
|
{
|
|
target: "#extension-tab-divider",
|
|
header: {
|
|
title: "Microscope extensions"
|
|
},
|
|
content: `Extensions installed on your microscope will appear below this line`
|
|
}
|
|
],
|
|
tourCallbacks: {
|
|
onStop: () => {
|
|
this.setLocalStorageObj("completedTour", true);
|
|
}
|
|
}
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
isSystemDark: function() {
|
|
if (
|
|
window.matchMedia &&
|
|
window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
},
|
|
handleTheme: function() {
|
|
var isDark = false;
|
|
if (this.$store.state.globalSettings.appTheme == "dark") {
|
|
isDark = true;
|
|
} else if (this.$store.state.globalSettings.appTheme == "system") {
|
|
if (this.systemDark) {
|
|
isDark = true;
|
|
}
|
|
}
|
|
return {
|
|
"uk-light": isDark,
|
|
"uk-background-secondary": isDark
|
|
};
|
|
}
|
|
},
|
|
|
|
mounted() {
|
|
if (process.env.VUE_APP_LITEMODE == "true") {
|
|
console.log("Built lite-mode");
|
|
}
|
|
// Query CSS dark theme preference
|
|
var mql = window.matchMedia("(prefers-color-scheme: dark)");
|
|
// Check for system dark theme when mounted
|
|
if (mql.matches) {
|
|
this.systemDark = true;
|
|
}
|
|
// Create a theme observer to watch for changes
|
|
this.themeObserver = mql.addListener(e => {
|
|
if (e.matches) {
|
|
this.systemDark = true;
|
|
} else {
|
|
this.systemDark = false;
|
|
}
|
|
});
|
|
// Handle guided tour
|
|
// If the user has already completed or skipped the guided tour
|
|
var completedTour = this.getLocalStorageObj("completedTour") || false;
|
|
if (!completedTour) {
|
|
this.$tours["guidedTour"].start();
|
|
}
|
|
},
|
|
|
|
created: function() {
|
|
window.addEventListener("beforeunload", this.handleExit);
|
|
// Key events
|
|
window.addEventListener("keydown", this.keyDownMonitor);
|
|
window.addEventListener("keyup", this.keyUpMonitor);
|
|
window.addEventListener("wheel", this.wheelMonitor);
|
|
},
|
|
|
|
beforeDestroy: function() {
|
|
// Disconnect the theme observer
|
|
if (this.themeObserver) {
|
|
this.themeObserver.disconnect();
|
|
}
|
|
// Remove key listeners
|
|
window.removeEventListener("keydown", this.keyDownMonitor);
|
|
window.removeEventListener("keyup", this.keyUpMonitor);
|
|
window.removeEventListener("wheel", this.wheelMonitor);
|
|
},
|
|
|
|
methods: {
|
|
handleExit: function() {
|
|
console.log("Triggered beforeunload");
|
|
this.$root.$emit("globalTogglePreview", false);
|
|
},
|
|
|
|
// Handle global mouse wheel events to be associated with navigation
|
|
wheelMonitor: function(event) {
|
|
// Only capture scroll if the event target's parent contains the "scrollTarget" class
|
|
if (
|
|
event.target.parentNode.classList.contains("scrollTarget") ||
|
|
event.target.classList.contains("scrollTarget")
|
|
) {
|
|
var z_rel = event.deltaY / 100;
|
|
// Emit a signal to move, acted on by panelNavigate.vue
|
|
this.$root.$emit("globalMoveStepEvent", 0, 0, z_rel, false);
|
|
}
|
|
},
|
|
|
|
// Handle global key press events to be associated with navigation
|
|
keyDownMonitor: function(event) {
|
|
this.keysDown[event.keyCode] = true; //Add key to array
|
|
|
|
// Convert keyCode dict into a list of key codes
|
|
var keyCodeList = Object.keys(keyCodes).map(function(key) {
|
|
return keyCodes[key];
|
|
});
|
|
|
|
if (
|
|
// If not inside an element we want to ignore
|
|
!(event.target instanceof HTMLInputElement) &&
|
|
!event.target.classList.contains("lightbox-link") &&
|
|
// If it's a recognised key
|
|
keyCodeList.includes(event.keyCode)
|
|
) {
|
|
this.navigateKeyHandler(keyCodes);
|
|
this.captureKeyHandler(keyCodes);
|
|
}
|
|
},
|
|
|
|
keyUpMonitor: function(event) {
|
|
delete this.keysDown[event.keyCode]; //Remove key from array
|
|
},
|
|
|
|
navigateKeyHandler: function(keyCodes) {
|
|
const moveKeys = [
|
|
keyCodes.left,
|
|
keyCodes.right,
|
|
keyCodes.up,
|
|
keyCodes.down,
|
|
keyCodes.pgup,
|
|
keyCodes.pgdn
|
|
];
|
|
|
|
if (
|
|
moveKeys.some(r => Object.keys(this.keysDown).includes(r.toString()))
|
|
) {
|
|
// Calculate movement array
|
|
var x_rel = 0;
|
|
var y_rel = 0;
|
|
var z_rel = 0;
|
|
if (keyCodes.left in this.keysDown) {
|
|
x_rel = x_rel + 1;
|
|
}
|
|
if (keyCodes.right in this.keysDown) {
|
|
x_rel = x_rel - 1;
|
|
}
|
|
if (keyCodes.up in this.keysDown) {
|
|
y_rel = y_rel + 1;
|
|
}
|
|
if (keyCodes.down in this.keysDown) {
|
|
y_rel = y_rel - 1;
|
|
}
|
|
if (keyCodes.pgup in this.keysDown) {
|
|
z_rel = z_rel - 1;
|
|
}
|
|
if (keyCodes.pgdn in this.keysDown) {
|
|
z_rel = z_rel + 1;
|
|
}
|
|
// Make a position request
|
|
// Emit a signal to move, acted on by panelNavigate.vue
|
|
this.$root.$emit("globalMoveStepEvent", x_rel, y_rel, z_rel);
|
|
}
|
|
},
|
|
|
|
captureKeyHandler: function(keyCodes) {
|
|
if (keyCodes.shift in this.keysDown && keyCodes.enter in this.keysDown) {
|
|
console.log("Capturing");
|
|
this.$root.$emit("globalCaptureEvent");
|
|
}
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="less">
|
|
// Basic UIkit CSS
|
|
@import "../node_modules/uikit/src/less/uikit.less";
|
|
// Custom UIkit CSS modifications
|
|
@import "./assets/less/theme.less";
|
|
|
|
// We override the custom-electron-titlebar z-index
|
|
// UIKit lightbox must be able to draw over the titlebar
|
|
// as it currently always spawns at the root of the DOM
|
|
.titlebar,
|
|
.titlebar > * {
|
|
z-index: 1000 !important;
|
|
}
|
|
|
|
#app {
|
|
-webkit-font-smoothing: antialiased;
|
|
-moz-osx-font-smoothing: grayscale;
|
|
text-align: left;
|
|
height: 100%;
|
|
}
|
|
|
|
body,
|
|
html {
|
|
height: 100%;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.uk-disabled {
|
|
pointer-events: none;
|
|
opacity: 0.4;
|
|
}
|
|
|
|
.control-component {
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
width: 300px;
|
|
height: 100%;
|
|
padding: 0;
|
|
background-color: rgba(180, 180, 180, 0.03);
|
|
border-width: 0 1px 0 0;
|
|
border-style: solid;
|
|
border-color: rgba(180, 180, 180, 0.25);
|
|
}
|
|
|
|
.view-component {
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
height: 100%;
|
|
padding: 0;
|
|
}
|
|
|
|
// Style tour
|
|
.v-tour__target--highlighted {
|
|
box-shadow: 0px 0px 180px 40px rgba(0, 0, 0, 0.4) !important;
|
|
border-radius: 5px;
|
|
opacity: 100%;
|
|
}
|
|
|
|
.v-step {
|
|
background: @global-primary-background !important;
|
|
}
|
|
|
|
.v-step__header {
|
|
background-color: darken(@global-primary-background, 15%) !important;
|
|
}
|
|
|
|
.v-step__button {
|
|
font-size: 0.9rem !important;
|
|
}
|
|
|
|
// Change step arrow colour
|
|
// This is awful and hacky and makes me sad, but needs must
|
|
.v-step .v-step__arrow {
|
|
border-color: darken(@global-primary-background, 15%) !important;
|
|
&--dark {
|
|
border-color: darken(@global-primary-background, 15%) !important;
|
|
}
|
|
}
|
|
|
|
.v-step[x-placement^="top"] .v-step__arrow {
|
|
border-left-color: transparent !important;
|
|
border-right-color: transparent !important;
|
|
border-bottom-color: transparent !important;
|
|
}
|
|
|
|
.v-step[x-placement^="bottom"] .v-step__arrow {
|
|
border-left-color: transparent !important;
|
|
border-right-color: transparent !important;
|
|
border-top-color: transparent !important;
|
|
}
|
|
|
|
.v-step[x-placement^="right"] .v-step__arrow {
|
|
border-left-color: transparent !important;
|
|
border-top-color: transparent !important;
|
|
border-bottom-color: transparent !important;
|
|
}
|
|
|
|
.v-step[x-placement^="left"] .v-step__arrow {
|
|
border-top-color: transparent !important;
|
|
border-right-color: transparent !important;
|
|
border-bottom-color: transparent !important;
|
|
}
|
|
</style>
|