diff --git a/package-lock.json b/package-lock.json
index 0c27f894..63b5f52a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10514,6 +10514,12 @@
"verror": "1.10.0"
}
},
+ "jump.js": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/jump.js/-/jump.js-1.0.2.tgz",
+ "integrity": "sha1-4GQbR/QKOPITnCX9oFAL8o5DAVo=",
+ "dev": true
+ },
"kew": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz",
@@ -12314,6 +12320,12 @@
"ts-pnp": "^1.1.6"
}
},
+ "popper.js": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
+ "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
+ "dev": true
+ },
"portfinder": {
"version": "1.0.25",
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz",
@@ -16064,6 +16076,18 @@
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
"dev": true
},
+ "vue-tour": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/vue-tour/-/vue-tour-1.3.0.tgz",
+ "integrity": "sha512-hLXA8vCCWNNjvXwEbfOagcZfJMz1a9Xi7LTN5nb/Nqpuv8RKT25dS5/HJBvHEEh1EoYQNOwohx1YA6x+2KTQbQ==",
+ "dev": true,
+ "requires": {
+ "hash-sum": "^2.0.0",
+ "jump.js": "^1.0.2",
+ "popper.js": "^1.16.0",
+ "vue": "^2.6.10"
+ }
+ },
"vuex": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.1.2.tgz",
diff --git a/package.json b/package.json
index bd1e65ae..b5a7c144 100644
--- a/package.json
+++ b/package.json
@@ -55,6 +55,7 @@
"vue": "^2.6.11",
"vue-plugin-load-script": "^1.2.0",
"vue-template-compiler": "^2.6.11",
+ "vue-tour": "^1.3.0",
"vuex": "^3.1.2"
},
"optionalDependencies": {
diff --git a/src/App.vue b/src/App.vue
index b4e42681..bb6f9510 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -4,7 +4,14 @@
class="uk-height-1-1 uk-margin-remove uk-padding-remove"
:class="handleTheme"
>
+
+
@@ -37,7 +44,80 @@ export default {
return {
keysDown: {},
systemDark: undefined,
- themeObserver: 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);
+ }
+ }
};
},
@@ -86,6 +166,12 @@ export default {
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() {
@@ -252,4 +338,56 @@ html {
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;
+}
diff --git a/src/components/appContent.vue b/src/components/appContent.vue
index fa3c79d9..1e53a2c4 100644
--- a/src/components/appContent.vue
+++ b/src/components/appContent.vue
@@ -1,6 +1,6 @@
@@ -11,7 +11,8 @@
>
bug_report
gamepad
camera_alt
settings
-
+
@@ -113,8 +118,8 @@
@@ -204,6 +209,8 @@ export default {
);
},
+ mounted: function() {},
+
beforeDestroy() {
// Then we call that function here to unwatch
if (this.unwatchStoreFunction) {
diff --git a/src/components/genericComponents/tabContent.vue b/src/components/genericComponents/tabContent.vue
index 1b09d514..394cd846 100644
--- a/src/components/genericComponents/tabContent.vue
+++ b/src/components/genericComponents/tabContent.vue
@@ -1,7 +1,7 @@
@@ -13,7 +13,7 @@ export default {
name: "TabContent",
props: {
- id: {
+ tabID: {
type: String,
required: true
},
diff --git a/src/components/genericComponents/tabIcon.vue b/src/components/genericComponents/tabIcon.vue
index 37e893ab..b1d6ed0a 100644
--- a/src/components/genericComponents/tabIcon.vue
+++ b/src/components/genericComponents/tabIcon.vue
@@ -18,7 +18,7 @@ export default {
name: "TabIcon",
props: {
- id: {
+ tabID: {
type: String,
required: true
},
@@ -45,7 +45,7 @@ export default {
return this.title;
} else {
// Get the last section of a fully qualified name
- var topName = this.id.split(".").pop();
+ var topName = this.tabID.split(".").pop();
// Make first character uppercase, then add the rest of the string
return topName.charAt(0).toUpperCase() + topName.slice(1);
}
@@ -57,7 +57,7 @@ export default {
classObject: function() {
return {
- "tabicon-active": this.currentTab == this.id,
+ "tabicon-active": this.currentTab == this.tabID,
"uk-disabled": this.requireConnection && !this.$store.getters.ready
};
}
@@ -65,7 +65,7 @@ export default {
methods: {
setThisTab(event) {
- this.$emit("set-tab", event, this.id);
+ this.$emit("set-tab", event, this.tabID);
if (this.clickCallback) {
this.clickCallback();
}
diff --git a/src/components/viewComponents/connectDisplay.vue b/src/components/viewComponents/connectDisplay.vue
index 1cee2dcc..076b7194 100644
--- a/src/components/viewComponents/connectDisplay.vue
+++ b/src/components/viewComponents/connectDisplay.vue
@@ -7,6 +7,7 @@
>