Merge branch 'ui-plugin-system-testing' into 'master'

GUI plugin system

See merge request openflexure/openflexure-microscope-jsclient!33
This commit is contained in:
Joel Collins 2019-06-21 12:12:27 +00:00
commit 3f458f220c
32 changed files with 1128 additions and 564 deletions

3
.env.development.web Normal file
View file

@ -0,0 +1,3 @@
NODE_ENV=development
VUE_APP_PLATFORM=web
VUE_APP_TARGET=web

48
package-lock.json generated
View file

@ -1,6 +1,6 @@
{ {
"name": "openflexure-ev", "name": "openflexure-ev",
"version": "1.1.0-beta.1", "version": "1.1.0-beta.3",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -5187,7 +5187,8 @@
"ansi-regex": { "ansi-regex": {
"version": "2.1.1", "version": "2.1.1",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"aproba": { "aproba": {
"version": "1.2.0", "version": "1.2.0",
@ -5208,12 +5209,14 @@
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
"concat-map": "0.0.1" "concat-map": "0.0.1"
@ -5228,17 +5231,20 @@
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"console-control-strings": { "console-control-strings": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
@ -5355,7 +5361,8 @@
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.3",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
@ -5367,6 +5374,7 @@
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"number-is-nan": "^1.0.0" "number-is-nan": "^1.0.0"
} }
@ -5381,6 +5389,7 @@
"version": "3.0.4", "version": "3.0.4",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
} }
@ -5388,12 +5397,14 @@
"minimist": { "minimist": {
"version": "0.0.8", "version": "0.0.8",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"minipass": { "minipass": {
"version": "2.3.5", "version": "2.3.5",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",
"yallist": "^3.0.0" "yallist": "^3.0.0"
@ -5412,6 +5423,7 @@
"version": "0.5.1", "version": "0.5.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
} }
@ -5492,7 +5504,8 @@
"number-is-nan": { "number-is-nan": {
"version": "1.0.1", "version": "1.0.1",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
@ -5504,6 +5517,7 @@
"version": "1.4.0", "version": "1.4.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
} }
@ -5589,7 +5603,8 @@
"safe-buffer": { "safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"safer-buffer": { "safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
@ -5625,6 +5640,7 @@
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"code-point-at": "^1.0.0", "code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0", "is-fullwidth-code-point": "^1.0.0",
@ -5644,6 +5660,7 @@
"version": "3.0.1", "version": "3.0.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"ansi-regex": "^2.0.0" "ansi-regex": "^2.0.0"
} }
@ -5687,12 +5704,14 @@
"wrappy": { "wrappy": {
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"yallist": { "yallist": {
"version": "3.0.3", "version": "3.0.3",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
} }
} }
}, },
@ -7288,6 +7307,11 @@
"object-visit": "^1.0.0" "object-visit": "^1.0.0"
} }
}, },
"material-design-icons": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/material-design-icons/-/material-design-icons-3.0.1.tgz",
"integrity": "sha1-mnHEh0chjrylHlGmbaaCA4zct78="
},
"md5.js": { "md5.js": {
"version": "1.3.5", "version": "1.3.5",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",

View file

@ -1,6 +1,6 @@
{ {
"name": "openflexure-ev", "name": "openflexure-ev",
"version": "1.1.0-beta.2", "version": "1.1.0-beta.4",
"private": true, "private": true,
"description": "An electron-based user client for the OpenFlexure Microscope Server", "description": "An electron-based user client for the OpenFlexure Microscope Server",
"author": "OpenFlexure <contact@openflexure.org> (https://www.openflexure.org)", "author": "OpenFlexure <contact@openflexure.org> (https://www.openflexure.org)",
@ -13,7 +13,7 @@
"scripts": { "scripts": {
"build:web": "vue-cli-service build --mode production.web", "build:web": "vue-cli-service build --mode production.web",
"build:app": "vue-cli-service build --mode production.app", "build:app": "vue-cli-service build --mode production.app",
"serve": "vue-cli-service serve", "serve": "vue-cli-service serve --mode development.web",
"start": "npm run build:app && electron app/app.js", "start": "npm run build:app && electron app/app.js",
"dist:linux": "electron-builder --linux --config app/builder-config-x86_64.yaml", "dist:linux": "electron-builder --linux --config app/builder-config-x86_64.yaml",
"dist:raspi": "electron-builder --linux --config app/builder-config-raspi.yaml", "dist:raspi": "electron-builder --linux --config app/builder-config-raspi.yaml",
@ -27,7 +27,8 @@
"custom-electron-titlebar": "^3.0.8", "custom-electron-titlebar": "^3.0.8",
"electron-context-menu": "^0.11.0", "electron-context-menu": "^0.11.0",
"electron-store": "^3.2.0", "electron-store": "^3.2.0",
"electron-updater": "^4.0.6" "electron-updater": "^4.0.6",
"material-design-icons": "^3.0.1"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "^3.3.0", "@vue/cli-plugin-babel": "^3.3.0",
@ -38,7 +39,7 @@
"electron-builder": "^20.39.0", "electron-builder": "^20.39.0",
"less": "^3.9.0", "less": "^3.9.0",
"less-loader": "^4.1.0", "less-loader": "^4.1.0",
"uikit": "3.1.1", "uikit": "^3.1.1",
"vue": "^2.5.21", "vue": "^2.5.21",
"vue-template-compiler": "^2.6.10", "vue-template-compiler": "^2.6.10",
"vuex": "^3.0.1" "vuex": "^3.0.1"

View file

@ -1,82 +1,41 @@
<template> <template>
<div id="app" v-bind:class="handleTheme"> <div id="app" v-bind:class="handleTheme">
<!-- Grid managing whole app -->
<div uk-grid class="uk-height-1-1 uk-margin-remove uk-padding-remove" margin=0> <div uk-grid class="uk-height-1-1 uk-margin-remove uk-padding-remove" margin=0>
<div id="sidebar-container" v-bind:class="{ 'overlay-panel': this.window.width<850 }" class="uk-padding-remove uk-first-column uk-inline uk-height-1-1"> <panelLeft/>
<div id="overlay-toggle"> <panelRight/>
<a href="" class="uk-icon-button uk-box-shadow-small uk-box-shadow-hover-medium action-btn-outline" uk-icon="menu" uk-toggle="target: #left-panel-container; animation: uk-animation-slide-left-small, uk-animation-slide-left-small" ></a>
</div>
<div id="left-panel-container" class="uk-padding-remove uk-card uk-card-default uk-width-auto uk-height-1-1" v-bind:class="{ 'uk-card-secondary': $store.state.globalSettings.darkMode }">
<panelLeft/>
</div>
</div>
<div id="main-panel-container" class="uk-padding-remove uk-height-1-1 uk-width-expand">
<panelDisplay/>
</div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
// Import axios for HTTP requests // Import axios for HTTP requests
import axios from 'axios' import axios from 'axios'
// Import basic UIkit
import UIkit from 'uikit';
// Import components // Import components
import panelLeft from './components/panelLeft.vue' import panelLeft from './components/panelLeft.vue'
import panelDisplay from './components/panelDisplay.vue' import panelRight from './components/panelRight.vue'
// Export main app // Export main app
export default { export default {
name: 'app', name: 'app',
components: { components: {
panelLeft, panelRight,
panelDisplay panelLeft
}, },
data: function () { data: function () {
return { return {}
window: {
width: 0,
height: 0
}
}
}, },
created: function () { created: function () {
var context = this
function handleSidebarEvent(context, event){
if (event.target.id == 'left-panel-container') {
console.log("Sidebar hidden")
context.$root.$emit('globalResizePreview')
}
}
UIkit.util.on(document, 'hidden', '#left-panel-container', function (e) {
handleSidebarEvent(context, e)
})
UIkit.util.on(document, 'shown', '#left-panel-container', function (e) {
handleSidebarEvent(context, e)
})
window.addEventListener('resize', this.handleResize)
this.handleResize();
window.addEventListener('beforeunload', this.handleExit) window.addEventListener('beforeunload', this.handleExit)
}, },
beforeDestroy: function () {
window.removeEventListener('resize', this.handleResize)
},
methods: { methods: {
handleResize: function(event) {
this.window.width = window.innerWidth;
this.window.height = window.innerHeight;
},
handleExit: function(event) { handleExit: function(event) {
console.log("Triggered beforeunload") console.log("Triggered beforeunload")
this.$root.$emit('globalTogglePreview', false) this.$root.$emit('globalTogglePreview', false)
@ -114,38 +73,9 @@ body, html {
overflow-x: hidden; overflow-x: hidden;
} }
.overlay-panel {
position: fixed;
z-index: 99;
}
#overlay-toggle {
width: 0px;
height: 30px;
z-index: 999;
position: absolute;
right: -20px;
top: 24px;
}
.action-btn-outline {
border: 1px solid lightgray;
}
.uk-light .uk-icon-button {
background-color: rgb(52, 52, 52);
}
.uk-light .uk-icon-button:hover, .uk-light .uk-icon-button:focus {
background-color: rgb(70, 70, 70);
}
.uk-light .uk-card-default {
background: #222
}
.uk-disabled { .uk-disabled {
pointer-events: none; pointer-events: none;
opacity: 0.5; opacity: 0.4;
} }
</style> </style>

View file

@ -10,6 +10,8 @@
// Custom OpenFlexure theming // Custom OpenFlexure theming
@global-primary-background: #C32280; @global-primary-background: #C32280;
@inverse-primary-muted-color: lighten(@global-primary-background, 15%);
// UIkit // UIkit
// ======================================================================== // ========================================================================
@ -123,6 +125,10 @@
@inverse-global-color: fade(@global-inverse-color, 80%); @inverse-global-color: fade(@global-inverse-color, 80%);
@inverse-global-muted-color: fade(@global-inverse-color, 60%); @inverse-global-muted-color: fade(@global-inverse-color, 60%);
//
// Paper
//
@paper-border-radius: 4px;
/* ======================================================================== /* ========================================================================
@ -137,103 +143,14 @@
@sidebar-right-left-xl: 60px; @sidebar-right-left-xl: 60px;
/* HTML
========================================================================== */
/* /* UIkit modifiers
* Force vertical scrollbar
* 1. `overflow-x` has to be on the `body` element for Safari to prevent horizontal scrolling on touch
This causes a random bug showing a vertical scrollbar in combination with some fonts like `Poppins`
This is why `overflow-y` must also be set to `hidden`
*/
html { overflow-y: scroll; }
/* 1 */
body { overflow: hidden; }
/* Documentation Sidebars
========================================================================== */
/*
* Sidebar Left
*/
.tm-sidebar-left {
position: fixed;
top: @navbar-nav-item-height;
bottom: 0;
box-sizing: border-box;
width: @sidebar-left-width !important;
padding: 40px 40px 60px 40px;
border-right: 1px @global-border solid;
overflow: auto;
}
/*
* Sidebar Right
*/
.tm-sidebar-right {
position: absolute;
top: 0;
left: ~'calc(100% + @{sidebar-right-left})';
width: @sidebar-right-width;
}
/* Tablet landscape and bigger */
@media (min-width: @breakpoint-medium) {
.tm-sidebar-left + .tm-main { padding-left: @sidebar-left-width; }
}
/* Desktop and bigger */
@media (min-width: @breakpoint-large) {
.tm-sidebar-right { left: ~'calc(100% + @{sidebar-right-left})'; }
.tm-sidebar-left + .tm-main { padding-left: @sidebar-left-width - (@sidebar-right-width + @sidebar-right-left); }
}
/* Large screen and bigger */
@media (min-width: 1400px) {
.tm-sidebar-left {
width: @sidebar-left-width-xl !important;
padding: 45px 45px 60px 45px;
}
.tm-sidebar-right { left: ~'calc(100% + @{sidebar-right-left-xl})'; }
.tm-sidebar-left + .tm-main { padding-left: @sidebar-left-width-xl - (@sidebar-right-width + @sidebar-right-left-xl); }
}
/* UIkit modifier
========================================================================== */ ========================================================================== */
/* /*
* Navbar * Navbar
*/ */
.tm-navbar-container:not(.uk-navbar-transparent) {
background: linear-gradient(to left, #28a5f5, @global-primary-background);
}
.tm-navbar-container .uk-navbar-nav > li > a,
.tm-navbar-container .uk-navbar-item:not(.uk-logo),
.tm-navbar-container .uk-navbar-toggle {
height: 70px;
font-size: 13px;
}
// Color Mode
.tm-navbar-container:not(.uk-navbar-transparent):extend(.uk-light all) {}
// Line Mode // Line Mode
@navbar-nav-item-line-margin-vertical: 20px; @navbar-nav-item-line-margin-vertical: 20px;
@navbar-nav-item-line-margin-horizontal: @navbar-nav-item-padding-horizontal; @navbar-nav-item-line-margin-horizontal: @navbar-nav-item-padding-horizontal;
@ -241,271 +158,115 @@ body { overflow: hidden; }
@navbar-nav-item-line-background: currentColor; @navbar-nav-item-line-background: currentColor;
@navbar-nav-item-line-transition-duration: 0.3s; @navbar-nav-item-line-transition-duration: 0.3s;
.tm-navbar-container .uk-navbar-nav > li > a { .uk-navbar-dropdown.uk-open {
position: relative; border-radius: @paper-border-radius;
&::before {
content: '';
display: block;
position: absolute;
left: @navbar-nav-item-line-margin-horizontal;
right: ~'calc(102% - @{navbar-nav-item-line-margin-horizontal})';
bottom: @navbar-nav-item-line-margin-vertical;
height: @navbar-nav-item-line-height;
background-color: @navbar-nav-item-line-background;
transition: @navbar-nav-item-line-transition-duration ease-in-out;
transition-property: right;
}
}
.tm-navbar-container .uk-navbar-nav > li > a:hover::before { right: @navbar-nav-item-line-margin-horizontal; }
/*
* Nav
*/
.tm-nav > li.uk-active > a { position: relative; }
.tm-nav > li.uk-active > a:before {
content: "";
position: absolute;
top: 15px;
left: -25px;
width: 15px;
border-top: 1px solid @global-primary-background;
}
.tm-nav .uk-nav-header {
padding: 8px 0;
border-bottom: 1px solid @global-border;
} }
/* /*
* Subnav * Cards
*/ */
/* Tablet landscape and bigger */ .uk-card {
@media (min-width: @breakpoint-medium) { border-radius: @paper-border-radius;
.tm-subnav { margin-left: -50px; }
.tm-subnav > * { padding-left: 50px; }
} }
.tm-subnav > * > :first-child { text-transform: capitalize; } .uk-card-media-top img {
border-radius: @paper-border-radius @paper-border-radius 0 0;
/*
* Label (Changelog()
*/
.tm-label-changelog {
width: 80px;
margin-top: 3px;
} }
/* .uk-card-default .uk-card-footer {
* Button border-top: 1px solid rgba(180, 180, 180, 0.25);
*/ }
.uk-button {
border-radius: 2px;
}
.tm-button-default, .uk-card-primary .uk-card-footer {
.tm-button-primary { border-radius: 500px; } border-top: 1px solid rgba(0, 0, 0, 0.075);
}
.tm-button-default {}
.tm-button-large { line-height: 48px; }
.tm-button-primary {}
.hook-inverse() { .hook-inverse() {
// Override background colour in dark mode
.tm-button-default { .uk-card-default {
color: @inverse-global-color; background-color: #2a2a2a !important;
border-color: @inverse-global-muted-color;
} }
.tm-button-primary { // Lighten on hover to show depth in dark mode
background: #fff !important; .uk-card-default:hover {
color: @global-primary-background !important; transition: background-color @animation-fast-duration ease;
background-color: #333 !important;
} }
.tm-button-primary:hover { box-shadow: 0 10px 40px rgba(30,135,240,1); }
} }
/*
* Section
*/
.tm-section-primary {
background: linear-gradient(to left top, #28a5f5, @global-primary-background) 0 0 no-repeat;
}
.tm-section-intro {
background: linear-gradient(to left top, #28a5f5, @global-primary-background) 0 0 no-repeat,
#fff;
background-size: 100% 75%;
}
/*
* Heading
*/
.tm-h4,
.tm-h5,
.tm-h6 {
font-family: Montserrat;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 2px;
color: @global-muted-color;
}
.tm-h4 { font-size: 14px; }
.tm-h5 { font-size: 12px; }
.tm-h6 { font-size: 11px; }
.hook-inverse() {
.tm-h4,
.tm-h5,
.tm-h6 { color: rgba(255, 255, 255, 0.7); }
}
/*
* Text
*/
.tm-text-large { font-size: 18px; }
/*
* Box shadow
*/
.tm-box-shadow-medium {
box-shadow: 2px 12px 35px rgba(0,0,0,0.1),
0 1px 6px rgba(0,0,0,0.05);
}
.tm-box-shadow-large {
box-shadow: -40px 40px 160px 0 rgba(0,0,0,0.08),
-8px 8px 15px 0 rgba(120,120,120,0.04),
3px 3px 30px 0 rgba(0,0,0,0.04);
}
.tm-box-shadow-modal { box-shadow: 3px 3px 40px 0 rgba(0,0,0,0.06); }
/*
* Overlay
*/
.tm-overlay-default { background: fade(@global-background, 50%); }
/* /*
* Modal * Modal
*/ */
.uk-modal-dialoge {
.tm-modal-dialog { background: @global-muted-background; } border-radius: @paper-border-radius;
box-shadow: 0 14px 25px rgba(0, 0, 0, 0.16);
.tm-modal-header { padding: 20px 30px; } transition: 0.3s ease !important;
.tm-modal-body { padding: 0; }
/* Phone landscape and bigger */
@media (min-width: @breakpoint-small) {
.tm-modal-header { padding: 25px 50px; }
.tm-modal-body { padding: 0 50px; }
} }
/* Desktop and bigger */
@media (min-width: @breakpoint-xlarge) {
.tm-modal-header { padding: 50px 120px; }
.tm-modal-body { padding: 0 120px; }
}
.tm-modal-close-full {
background: transparent;
padding: 30px;
}
/* Phone landscape and bigger */
@media (min-width: @breakpoint-small) {
.tm-modal-close-full { padding: @global-medium-margin; }
}
/* Utilities
========================================================================== */
/* /*
* Heading fragment * Notifications
*/ */
.uk-notification-message {
.tm-heading-fragment > a { border-radius: @paper-border-radius;
color: inherit; box-shadow: 0 14px 25px rgba(0, 0, 0, 0.16);
text-decoration: none;
} }
/* Phone landscape and bigger */ .uk-notification-message-danger {
@media (min-width: @breakpoint-small) { border: 1px solid @notification-message-danger-color
}
h2.tm-heading-fragment > a:before { .uk-notification-message-warning {
content: "#"; border: 1px solid @notification-message-warning-color
position: absolute; }
width: 25px;
margin-left: -25px; .uk-notification-message-success {
color: @global-muted-color; border: 1px solid @notification-message-success-color
}
/*
* Links
*/
.uk-link, .uk-button-link {
color: @global-muted-color;
}
.uk-link:hover, .uk-button-link:hover, a:hover {
color: @global-primary-background;
}
.hook-inverse() {
.uk-link:hover, .uk-button-link:hover, a:hover {
color: @inverse-primary-muted-color;
}
}
/*
* Buttons
*/
.uk-button {
border-radius: 2px;
}
.uk-button-danger {
background-color: transparent;
color: #f0506e;
border: 1px solid #f0506e;
}
.hook-inverse() {
.uk-button-primary{
background-color: rgba(180, 180, 180, 0.15);
color: @inverse-primary-muted-color;
border-color: @inverse-primary-muted-color;
} }
} .uk-button-primary:hover {
background-color: @inverse-primary-muted-color;
color: #fff;
/* Reset container for docs
========================================================================== */
.tm-main .uk-container {
padding-left: @container-padding-horizontal;
padding-right: @container-padding-horizontal;
}
/* Phone landscape and bigger */
@media (min-width: @breakpoint-small) {
.tm-main .uk-container {
padding-left: @container-padding-horizontal-s;
padding-right: @container-padding-horizontal-s;
} }
} }
/* Tablet landscape and bigger */ .uk-icon-button, .uk-icon-button:hover {
@media (min-width: @breakpoint-medium) { text-decoration: none
.tm-main .uk-container {
padding-left: @container-padding-horizontal-m;
padding-right: @container-padding-horizontal-m;
}
}
/* Pro
========================================================================== */
/* Desktop and bigger */
@media (min-width: @breakpoint-large) {
.tm-intro-text { margin-top: 30px; }
}
/* Large screen and bigger */
@media (min-width: @breakpoint-xlarge) {
.tm-intro-text { margin-top: 90px; }
.tm-intro-image { margin-right: -75px; }
} }

View file

@ -43,18 +43,20 @@
<div class="uk-accordion-content"> <div class="uk-accordion-content">
<form @submit.prevent="handleMetadataSubmit"> <form @submit.prevent="handleMetadataSubmit">
<div class="uk-margin-small uk-grid-small" uk-grid> <div class="uk-margin-remove uk-flex uk-flex-middle">
<div class="uk-margin-remove-top uk-width-2-5"><input v-model="newMetadata.key" class="uk-input uk-form-width-small uk-form-small" type="text" name="flavor" placeholder="Key"></div> <div class="uk-margin-remove-top uk-padding-remove uk-grid-small uk-width-expand" uk-grid>
<div class="uk-margin-remove-top uk-width-2-5"><input v-model="newMetadata.value" class="uk-input uk-form-width-small uk-form-small" type="text" name="flavor" placeholder="Value"></div> <div class="uk-margin-remove uk-width-1-2"><input v-model="newMetadata.key" class="uk-input uk-form-width-small uk-form-small" type="text" name="flavor" placeholder="Key"></div>
<div class="uk-margin-remove-top uk-width-1-5"><button class="uk-button uk-button-default uk-form-small uk-float-right" uk-icon="plus"></button></div> <div class="uk-margin-remove uk-width-1-2"><input v-model="newMetadata.value" class="uk-input uk-form-width-small uk-form-small" type="text" name="flavor" placeholder="Value"></div>
</div>
<a href="#" v-on:click="handleMetadataSubmit()" class="uk-icon uk-margin-left"><i class="material-icons">add_circle</i></a>
</div> </div>
</form> </form>
<div v-for="(value, key) in customMetadata" :key="key" class="uk-width-1-1 uk-margin-small uk-margin-remove-left uk-margin-remove-right" uk-grid> <div v-for="(value, key) in customMetadata" :key="key" class="uk-width-1-1 uk-margin-small uk-margin-remove-left uk-margin-remove-right uk-flex uk-flex-middle">
<div class="uk-margin-remove-top uk-padding-remove uk-width-expand"><b>{{ key }}: </b>{{ value }}</div> <div class="uk-margin-remove-top uk-padding-remove uk-width-expand"><b>{{ key }}: </b>{{ value }}</div>
<div class="uk-margin-remove-top uk-padding-remove uk-width-auto"> <a href="#" v-on:click="delMetadataKey(key)" class="uk-icon uk-width-auto"><i class="material-icons">delete</i></a>
<a href="#" v-on:click="delMetadataKey(key)" class="uk-icon-link" uk-icon="trash"></a>
</div>
</div> </div>
</div> </div>
@ -65,9 +67,16 @@
<div class="uk-accordion-content"> <div class="uk-accordion-content">
<form @submit.prevent="handleTagSubmit"> <form @submit.prevent="handleTagSubmit">
<div class="uk-margin-small uk-grid-small" uk-grid> <div class="uk-margin-small uk-flex uk-flex-middle">
<div class="uk-margin-remove-top uk-width-4-5"><input v-model="newTag" class="uk-input uk-form-width-small uk-form-small" type="text" name="flavor" placeholder="Tag"></div>
<div class="uk-margin-remove-top uk-width-1-5"><button class="uk-button uk-button-default uk-form-small uk-float-right" uk-icon="plus"></button></div> <div class="uk-margin-remove-top uk-width-expand">
<div class="uk-inline uk-width-1-1">
<span class="uk-form-icon"><i class="material-icons">label</i></span>
<input v-model="newTag" class="uk-input uk-form-small" type="text" name="flavor" placeholder="Tag">
</div>
</div>
<a href="#" v-on:click="handleTagSubmit()" class="uk-icon uk-margin-left"><i class="material-icons">add_circle</i></a>
</div> </div>
</form> </form>

View file

@ -2,8 +2,6 @@
<div class="host-input"> <div class="host-input">
<div class="uk-margin"> <div class="uk-margin">
<h3>Connect</h3>
<form @submit.prevent="handleSubmit"> <form @submit.prevent="handleSubmit">
<div class="uk-form-controls uk-margin"> <div class="uk-form-controls uk-margin">
<label><input class="uk-radio" type="radio" name="radio_local" v-model="computedLocalMode" v-bind:value="true"> Connect locally</label><br> <label><input class="uk-radio" type="radio" name="radio_local" v-model="computedLocalMode" v-bind:value="true"> Connect locally</label><br>
@ -12,7 +10,7 @@
<div v-if="!localMode"> <div v-if="!localMode">
<div class="uk-inline uk-width-1-1"> <div class="uk-inline uk-width-1-1">
<span class="uk-form-icon" uk-icon="icon: server"></span> <span class="uk-form-icon"><i class="material-icons">dns</i></span>
<input v-model="hostname" v-bind:class="IpFormClasses" class="uk-input uk-form-small" type="text" placeholder="Hostname or IP address"> <input v-model="hostname" v-bind:class="IpFormClasses" class="uk-input uk-form-small" type="text" placeholder="Hostname or IP address">
</div> </div>
</div> </div>
@ -23,10 +21,11 @@
<div class="uk-accordion-content"> <div class="uk-accordion-content">
<label class="uk-form-label" for="form-stacked-text">Port</label> <label class="uk-form-label" for="form-stacked-text">Port</label>
<div class="uk-form-controls"> <div class="uk-form-controls">
<input v-model="computedPort" class="uk-input uk-form-width-medium uk-form-small" id="form-stacked-text" type="number" value=5000> <input v-model="computedPort" class="uk-input uk-form-small" id="form-stacked-text" type="number" value=5000>
</div> </div>
</div> </div>
</li> </li>
<li> <li>
<a class="uk-accordion-title" href="#">Status</a> <a class="uk-accordion-title" href="#">Status</a>
<div class="uk-accordion-content"> <div class="uk-accordion-content">
@ -35,23 +34,23 @@
<p><b>Base URI:</b> {{ $store.getters.uri }}</p> <p><b>Base URI:</b> {{ $store.getters.uri }}</p>
<p v-if="$store.state.apiConfig.name"><b>Device name:</b> {{ $store.state.apiConfig.name }}</p> <p v-if="$store.state.apiConfig.name"><b>Device name:</b> {{ $store.state.apiConfig.name }}</p>
</div> </div>
<div v-else-if="$store.state.waiting"><div uk-spinner></div></div> <div v-else-if="$store.state.waiting"><div uk-spinner></div></div>
<div v-else-if="$store.state.error"><b>Error:</b> {{ $store.state.error }}</div> <div v-else-if="$store.state.error"><b>Error:</b> {{ $store.state.error }}</div>
<div v-else>No active connection</div> <div v-else>No active connection</div>
</div> </div>
</li> </li>
<li v-if="!localMode" class="uk-open"> <li v-if="!localMode" class="uk-open">
<a class="uk-accordion-title" href="#">Saved hosts</a> <a class="uk-accordion-title" href="#">Saved hosts</a>
<div class="uk-accordion-content"> <div class="uk-accordion-content">
<button v-if="$store.getters.ready" v-on:click="saveHost()" class="uk-button uk-button-default uk-form-small uk-margin-small uk-width-1-1">Save Current</button> <button v-if="$store.getters.ready" v-on:click="saveHost()" class="uk-button uk-button-default uk-form-small uk-margin-small uk-width-1-1">Save Current</button>
<div v-for="host in savedHosts" :key="host.name" class="uk-margin-small uk-margin-remove-left uk-margin-remove-right uk-flex uk-flex-middle">
<div v-for="host in savedHosts" :key="host.name" class="uk-margin-small uk-margin-remove-left uk-margin-remove-right uk-grid"> <a href="#" v-on:click="autofillHost(host)" class="uk-link uk-padding-remove uk-width-expand host-description"><b>{{ host.name }}</b> ({{ host.hostname }}:{{ host.port }})</a>
<a href="#" v-on:click="autofillHost(host)" class="uk-icon-link uk-padding-remove uk-width-expand"><b>{{ host.name }}</b> ({{ host.hostname }}:{{ host.port }})</a> <a href="#" v-on:click="delSavedHost(host)" class="uk-icon uk-width-auto host-delete"><i class="material-icons">delete</i></a>
<a href="#" v-on:click="delSavedHost(host)" class="uk-icon-link uk-width-auto" uk-icon="trash"></a>
</div> </div>
</div> </div>
</li> </li>
</ul> </ul>
<button class="uk-button uk-button-primary uk-form-small uk-float-right uk-margin uk-margin-remove-top uk-width-1-1">Connect</button> <button class="uk-button uk-button-primary uk-form-small uk-float-right uk-margin uk-margin-remove-top uk-width-1-1">Connect</button>
@ -161,7 +160,7 @@ export default {
this.$store.commit("changeSetting", ['trackWindow', state]); this.$store.commit("changeSetting", ['trackWindow', state]);
this.$store.commit("changeSetting", ['disableStream', state]); this.$store.commit("changeSetting", ['disableStream', state]);
this.$store.commit("changeSetting", ['autoGpuPreview', state]); this.$store.commit("changeSetting", ['autoGpuPreview', state]);
this.$root.$emit('globalTogglePreview', state) //this.$root.$emit('globalTogglePreview', state)
}, },
autofillHost: function (host) { autofillHost: function (host) {
@ -246,4 +245,14 @@ export default {
.host-input { .host-input {
text-align: left; text-align: left;
} }
.host-description {
text-overflow: ellipsis;
overflow: hidden;
}
.host-delete {
padding: 0 2px 0 10px;
}
</style> </style>

View file

@ -64,26 +64,23 @@
<li class="uk-open"> <li class="uk-open">
<a class="uk-accordion-title" href="#">Autofocus</a> <a class="uk-accordion-title" href="#">Autofocus</a>
<div class="uk-text-center uk-container" v-if="isAutofocusing"> <div class="uk-accordion-content">
<div class="center-spinner" uk-spinner></div>
</div>
<div class="uk-accordion-content" v-else>
<div class="uk-grid-small uk-child-width-1-3" uk-grid> <div v-if="isAutofocusing">
<progressBar/>
<div> </div>
<button v-on:click="runFastAutofocus(2000, 300);" class="uk-button uk-button-default uk-form-small uk-float-right uk-width-1-1">Fast</button>
</div>
<div>
<button v-on:click="runAutofocus([-60,-30,0,30,60]);" class="uk-button uk-button-default uk-form-small uk-float-right uk-width-1-1">Medium</button>
</div>
<div>
<button v-on:click="runAutofocus([-20,-10,0,10,20]);" class="uk-button uk-button-default uk-form-small uk-float-right uk-width-1-1">Fine</button>
</div>
<div class="uk-grid-small uk-child-width-1-3" v-bind:hidden="isAutofocusing" uk-grid>
<div>
<button v-on:click="runFastAutofocus(2000, 300);" class="uk-button uk-button-default uk-form-small uk-float-right uk-width-1-1">Fast</button>
</div> </div>
<div>
<button v-on:click="runAutofocus([-60,-30,0,30,60]);" class="uk-button uk-button-default uk-form-small uk-float-right uk-width-1-1">Medium</button>
</div>
<div>
<button v-on:click="runAutofocus([-20,-10,0,10,20]);" class="uk-button uk-button-default uk-form-small uk-float-right uk-width-1-1">Fine</button>
</div>
</div>
</div> </div>
</li> </li>
@ -94,6 +91,7 @@
<script> <script>
import axios from 'axios' import axios from 'axios'
import progressBar from "../genericComponents/progressBar"
// Key Codes // Key Codes
const keyCodes = { const keyCodes = {
@ -111,6 +109,10 @@ const keyCodes = {
export default { export default {
name: 'paneNavigate', name: 'paneNavigate',
components: {
progressBar
},
data: function () { data: function () {
return { return {
keysDown: {}, keysDown: {},

View file

@ -1,7 +1,6 @@
<template> <template>
<div id="paneSettings"> <div id="paneSettings">
<h3>Settings</h3>
<appSettings/> <appSettings/>
<ul uk-accordion="multiple: true"> <ul uk-accordion="multiple: true">
@ -26,10 +25,10 @@
</template> </template>
<script> <script>
import streamSettings from './paneSettingsComponents/streamSettings.vue' import streamSettings from './settingsComponents/streamSettings.vue'
import microscopeSettings from './paneSettingsComponents/microscopeSettings.vue' import microscopeSettings from './settingsComponents/microscopeSettings.vue'
import cameraSettings from './paneSettingsComponents/cameraSettings.vue' import cameraSettings from './settingsComponents/cameraSettings.vue'
import appSettings from './paneSettingsComponents/appSettings.vue' import appSettings from './settingsComponents/appSettings.vue'
// Export main app // Export main app
export default { export default {

View file

@ -24,10 +24,11 @@
<button type="submit" class="uk-button uk-button-primary uk-form-small uk-float-right uk-margin-small uk-width-1-1">Apply Settings</button> <button type="submit" class="uk-button uk-button-primary uk-form-small uk-float-right uk-margin-small uk-width-1-1">Apply Settings</button>
<div class="uk-text-center uk-container" v-if="isCalibrating"> <div v-if="isCalibrating">
<div class="center-spinner" uk-spinner></div> <progressBar/>
</div> </div>
<div v-else>
<div v-bind:hidden="isCalibrating">
<button type="button" v-on:click="recalibrateConfirm()" class="uk-button uk-button-default uk-form-small uk-float-right uk-margin-small uk-width-1-1">Auto-Calibrate</button> <button type="button" v-on:click="recalibrateConfirm()" class="uk-button uk-button-default uk-form-small uk-float-right uk-margin-small uk-width-1-1">Auto-Calibrate</button>
</div> </div>
@ -40,11 +41,16 @@
<script> <script>
import axios from 'axios' import axios from 'axios'
import progressBar from "../../genericComponents/progressBar"
// Export main app // Export main app
export default { export default {
name: 'microscopeSettings', name: 'microscopeSettings',
components: {
progressBar
},
data: function () { data: function () {
return { return {
shutterSpeed: this.$store.state.apiConfig.camera_settings.picamera_settings.shutter_speed, shutterSpeed: this.$store.state.apiConfig.camera_settings.picamera_settings.shutter_speed,

View file

@ -0,0 +1,142 @@
<template>
<div class="progress uk-margin-top uk-margin-horizontal-remove uk-padding-remove">
<div class="indeterminate"></div>
</div>
</template>
<script>
export default {
name: 'progressBar',
props: {},
methods: {
setThisTab(event, value) {
this.$emit('set-tab', event, this.id);
}
},
computed: {
tooltipOptions: function () {
var title = this.id.charAt(0).toUpperCase() + this.id.slice(1);
return `pos: right; title: ${title}; delay: 500`
},
classObject: function () {
return {
'tabicon-active': this.currentTab == this.id,
'uk-disabled': (this.requireConnection && !this.$store.getters.ready)
}
}
}
}
</script>
<style lang="less" scoped>
@import "../../assets/less/theme.less";
.progress {
position: relative;
height: 5px;
display: block;
width: 100%;
background-color: rgba(180, 180, 180, 0.15);
border-radius: 2px;
background-clip: padding-box;
margin: 0.5rem 0 1rem 0;
overflow: hidden;
}
.progress .determinate {
position: absolute;
background-color: inherit;
top: 0;
bottom: 0;
transition: width .3s linear;
}
.progress .indeterminate, .progress .determinate {
background-color: @global-primary-background;
}
.hook-inverse() {
.progress .indeterminate, .progress .determinate{
background-color: @inverse-primary-muted-color;
}
}
.progress .indeterminate:before {
content: '';
position: absolute;
background-color: inherit;
top: 0;
left: 0;
bottom: 0;
will-change: left, right;
-webkit-animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
}
.progress .indeterminate:after {
content: '';
position: absolute;
background-color: inherit;
top: 0;
left: 0;
bottom: 0;
will-change: left, right;
-webkit-animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;
animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;
-webkit-animation-delay: 1.15s;
animation-delay: 1.15s;
}
@-webkit-keyframes indeterminate {
0% {
left: -35%;
right: 100%; }
60% {
left: 100%;
right: -90%; }
100% {
left: 100%;
right: -90%; }
}
@keyframes indeterminate {
0% {
left: -35%;
right: 100%; }
60% {
left: 100%;
right: -90%; }
100% {
left: 100%;
right: -90%; }
}
@-webkit-keyframes indeterminate-short {
0% {
left: -200%;
right: 100%; }
60% {
left: 107%;
right: -8%; }
100% {
left: 107%;
right: -8%; }
}
@keyframes indeterminate-short {
0% {
left: -200%;
right: 100%; }
60% {
left: 107%;
right: -8%; }
100% {
left: 107%;
right: -8%; }
}
</style>

View file

@ -0,0 +1,46 @@
<template>
<div
v-if="!(this.requireConnection && !this.$store.getters.ready)"
v-bind:hidden="currentTab!=id"
class="uk-width-expand"
>
<div class="section-heading">{{ id }}</div>
<div class="section-content"><slot></slot></div>
</div>
</template>
<script>
export default {
name: 'tabContent',
props: {
id: String,
currentTab: String,
requireConnection: Boolean,
},
methods: {},
computed: {}
}
</script>
<style lang="less" scoped>
.section-heading {
display: block;
font-size: 12px;
text-transform: uppercase;
line-height: 20px;
cursor: default;
}
.section-content, .section-heading {
padding: 9px 10px;
}
</style>

View file

@ -0,0 +1,62 @@
<template>
<a
href="#"
class="uk-link"
:class="classObject"
:uk-tooltip="tooltipOptions"
@click="setThisTab">
<slot></slot>
</a>
</template>
<script>
export default {
name: 'tabIcon',
props: {
id: String,
currentTab: String,
requireConnection: Boolean
},
methods: {
setThisTab(event, value) {
this.$emit('set-tab', event, this.id);
}
},
computed: {
tooltipOptions: function () {
var title = this.id.charAt(0).toUpperCase() + this.id.slice(1);
return `pos: right; title: ${title}; delay: 500`
},
classObject: function () {
return {
'tabicon-active': this.currentTab == this.id,
'uk-disabled': (this.requireConnection && !this.$store.getters.ready)
}
}
}
}
</script>
<style lang="less" scoped>
// Custom UIkit CSS modifications
@import "../../assets/less/theme.less";
.tabicon-active {
color: @global-primary-background !important;
}
.hook-inverse() {
.tabicon-active {
color: @inverse-primary-muted-color !important;
}
}
</style>

View file

@ -1,59 +1,160 @@
<template> <template>
<div id="panelLeft" class="uk-flex uk-flex-column uk-margin-remove uk-padding-remove uk-height-1-1"> <div id="panel-left" class="uk-margin-remove uk-padding-remove uk-height-1-1" uk-grid>
<ul class="uk-flex-none uk-flex-center uk-margin-remove-top uk-margin-remove-bottom" uk-tab="swiping: false">
<li><a href="#" uk-switcher-item="connect" uk-icon="server" uk-tooltip="pos: bottom; title: Connect"></a></li> <!-- Vertical tab bar -->
<li v-bind:class="disableIfDisconnected"><a href="#" uk-switcher-item="navigate" uk-icon="location" uk-tooltip="pos: bottom; title: Navigate"></a></li> <div id="switcher-left" class="uk-flex uk-flex-column uk-padding-remove uk-width-auto uk-height-1-1">
<li v-bind:class="disableIfDisconnected"><a href="#" uk-switcher-item="capture" uk-icon="camera" uk-tooltip="pos: bottom; title: Capture"></a></li> <tabIcon id="connect" :requireConnection="false" :currentTab="currentTab" @set-tab="setTab">
<li v-bind:class="disableIfDisconnected"><a href="#" uk-switcher-item="plugins" uk-icon="git-fork" uk-tooltip="pos: bottom; title: Plugins"></a></li> <i class="material-icons">settings_ethernet</i>
<li><a href="#" uk-switcher-item="settings" uk-icon="settings" uk-tooltip="pos: bottom; title: Settings"></a></li> </tabIcon>
</ul> <tabIcon id="navigate" :requireConnection="true" :currentTab="currentTab" @set-tab="setTab">
<ul class="uk-switcher uk-padding-small uk-flex uk-flex-1 panel-content"> <i class="material-icons">gamepad</i>
<li class="uk-width-expand"><paneConnect/></li> </tabIcon>
<li class="uk-width-expand"><div v-if="$store.getters.ready"><paneNavigate/></div></li> <tabIcon id="capture" :requireConnection="true" :currentTab="currentTab" @set-tab="setTab">
<li class="uk-width-expand"><div v-if="$store.getters.ready"><paneCapture/></div></li> <i class="material-icons">camera_alt</i>
<li class="uk-width-expand"><div v-if="$store.getters.ready"><panePlugins/></div></li> </tabIcon>
<li class="uk-width-expand"><paneSettings/></li> <tabIcon id="settings" :requireConnection="false" :currentTab="currentTab" @set-tab="setTab">
</ul> <i class="material-icons">settings</i>
</tabIcon>
<hr>
<tabIcon v-for="plugin in $store.state.apiPlugins" @set-tab="setTab"
:key="plugin.id"
:id="plugin.id"
:requireConnection="plugin.requiresConnection"
:currentTab="currentTab">
<i class="material-icons">{{ plugin.icon || "extension" }}</i>
</tabIcon>
</div>
<!-- Corresponding vertical tab content -->
<div v-bind:hidden="!showControlBar" id="container-left" class="uk-padding-remove uk-height-1-1 uk-width-expand">
<div id="component-left" class="uk-padding-remove uk-flex uk-flex-1 panel-content">
<tabContent id="connect" :requireConnection="false" :currentTab="currentTab">
<paneConnect/>
</tabContent>
<tabContent id="navigate" :requireConnection="true" :currentTab="currentTab">
<paneNavigate/>
</tabContent>
<tabContent id="capture" :requireConnection="true" :currentTab="currentTab">
<paneCapture/>
</tabContent>
<tabContent id="settings" :requireConnection="false" :currentTab="currentTab">
<paneSettings/>
</tabContent>
<tabContent v-for="plugin in $store.state.apiPlugins"
:key="plugin.id"
:id="plugin.id"
:requireConnection="plugin.requiresConnection"
:currentTab="currentTab">
<div class="uk-flex uk-flex-column" v-for="form in plugin.forms" :key="`${form.route}/${form.name}`.replace(/\s+/g, '-').toLowerCase()" >
<JsonForm
:name="form.name"
:route="form.route"
:isTask="form.isTask"
:submitLabel="form.submitLabel"
:selfUpdate="form.selfUpdate"
:schema="form.schema"/>
<hr>
</div>
</tabContent>
</div>
</div>
</div> </div>
</template> </template>
<script> <script>
// Import components // Import axios for HTTP requests
import paneConnect from './paneConnect.vue' import axios from 'axios'
import paneNavigate from './paneNavigate.vue'
import paneCapture from './paneCapture.vue' // Import generic components
import panePlugins from './panePlugins.vue' import tabIcon from './genericComponents/tabIcon'
import paneSettings from './paneSettings.vue' import tabContent from './genericComponents/tabContent'
// Import pane components
import paneConnect from './controlComponents/paneConnect'
import paneNavigate from './controlComponents/paneNavigate'
import paneCapture from './controlComponents/paneCapture'
import paneSettings from './controlComponents/paneSettings'
// Import plugin components
import JsonForm from './pluginComponents/formComponents/JsonForm'
// Export main app // Export main app
export default { export default {
name: 'panelLeft', name: 'panelLeft',
components: { components: {
tabIcon,
tabContent,
paneConnect, paneConnect,
paneNavigate, paneNavigate,
paneCapture, paneCapture,
panePlugins, paneSettings,
paneSettings JsonForm
},
data: function () {
return {
currentTab: 'connect',
showControlBar: true
}
},
methods: {
setTab: function(event, tab) {
if (this.currentTab == tab) {
this.showControlBar = !this.showControlBar
this.currentTab = 'none'
}
else {
this.showControlBar = true
this.currentTab = tab
}
},
}, },
computed: { computed: {
disableIfDisconnected: function () { pluginApiUri: function () {
return { return this.$store.getters.uri + "/plugin"
'uk-disabled': !this.$store.getters.ready },
}
}
} }
} }
</script> </script>
<style lang="less"> <style scoped lang="less">
.uk-tab {
padding-left: 0; #component-left {
}
.panel-content {
width: 300px; width: 300px;
overflow: auto;
} }
#container-left {
overflow: hidden auto;
background-color: rgba(180, 180, 180, 0.025);
}
#container-left, #switcher-left {
border-width: 0 1px 0 0;
border-style: solid;
border-color: rgba(180, 180, 180, 0.25)
}
#switcher-left a{
padding: 10px 16px;
}
#switcher-left{
background-color: rgba(180, 180, 180, 0.1);
padding-top: 2px !important;
}
</style> </style>

View file

@ -1,30 +1,34 @@
<template> <template>
<div id="panelDisplay" class="uk-flex uk-flex-column uk-margin-remove uk-padding-remove uk-height-1-1"> <!-- Tabbed panel for gallery and live views -->
<div id="panel-right" class="uk-flex uk-flex-column uk-margin-remove uk-padding-remove uk-width-expand uk-height-1-1">
<ul class="uk-flex-none uk-flex-center uk-margin-remove-bottom uk-text-center" uk-tab="swiping: false"> <ul class="uk-flex-none uk-flex-center uk-margin-remove-bottom uk-text-center" uk-tab="swiping: false">
<li><a href="#" uk-switcher-item="preview" uk-icon="play-circle" uk-tooltip="pos: bottom; title: Live"></a></li> <li><a href="#" uk-switcher-item="preview">Live</a></li>
<li v-bind:class="{'uk-disabled': !this.$store.getters.ready}"><a href="#" uk-switcher-item="gallery" uk-icon="image" uk-tooltip="pos: bottom; title: Captures"></a></li> <li v-bind:class="{'uk-disabled': !this.$store.getters.ready}"><a href="#" uk-switcher-item="gallery">Gallery</a></li>
</ul> </ul>
<ul class="uk-switcher uk-flex uk-flex-1"> <ul class="uk-switcher uk-flex uk-flex-1">
<li class="uk-height-1-1 uk-width-1-1 clickableTab" id="streamDisplayTab"><streamDisplay/></li> <li class="uk-height-1-1 uk-width-1-1 clickableTab" id="streamDisplayTab"><streamDisplay/></li>
<li class="uk-height-1-1 uk-width-1-1 uk-overflow-auto" id="galleryDisplayTab"><galleryDisplay/></li> <li class="uk-height-1-1 uk-width-1-1 uk-overflow-auto" id="galleryDisplayTab"><galleryDisplay/></li>
</ul> </ul>
</div> </div>
</template> </template>
<script> <script>
// Import axios for HTTP requests
import axios from 'axios'
// Import basic UIkit
import UIkit from 'uikit'; import UIkit from 'uikit';
// Import components // Import components
import streamDisplay from './paneDisplayComponents/streamDisplay.vue' import streamDisplay from './viewComponents/streamDisplay.vue'
import galleryDisplay from './paneDisplayComponents/galleryDisplay.vue' import galleryDisplay from './viewComponents/galleryDisplay.vue'
// Export main app // Export main app
export default { export default {
name: 'panelDisplay', name: 'panelRight',
components: { components: {
streamDisplay, streamDisplay,
galleryDisplay galleryDisplay,
}, },
mounted() { mounted() {
@ -54,4 +58,9 @@ export default {
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
.uk-tab {
padding-left: 0;
}
</style> </style>

View file

@ -0,0 +1,229 @@
<template>
<div>
<div class="uk-flex">
<div class="uk-text-bold uk-text-uppercase uk-width-expand">{{ name }}</div>
<a href="#" v-if="selfUpdate" v-on:click="getFormData()" class="uk-icon uk-width-auto"><i class="material-icons">cached</i></a>
</div>
<form @submit.prevent="submitForm" class="uk-form-stacked">
<div v-for="(field, index) in schema" :key="index">
<div v-if="Array.isArray(field)" class="uk-grid-small uk-width-1-1 uk-child-width-expand" uk-grid>
<div v-for="(subfield, subindex) in field" :key="subindex">
<component
:is="subfield.fieldType"
v-model="formData[subfield.name]"
v-bind="subfield">
</component>
</div>
</div>
<component
:is="field.fieldType"
v-model="formData[field.name]"
v-bind="field">
</component>
</div>
<div v-if="taskRunning">
<progressBar/>
</div>
<button v-bind:hidden="taskRunning" class="uk-button uk-button-primary uk-form-small uk-float-right uk-margin-small uk-width-1-1">{{ submitLabel }}</button>
</form>
</div>
</template>
<script>
import axios from 'axios'
import numberInput from "./fieldComponents/numberInput";
import selectList from "./fieldComponents/selectList";
import textInput from "./fieldComponents/textInput";
import htmlBlock from "./fieldComponents/htmlBlock";
import radioList from "./fieldComponents/radioList";
import checkList from "./fieldComponents/checkList"
import progressBar from "../../genericComponents/progressBar"
export default {
name: 'JsonForm',
components: {
numberInput,
selectList,
textInput,
htmlBlock,
radioList,
checkList,
progressBar
},
props: {
'name': {
type: String,
required: false,
default: "Plugin"
},
'schema': {
type: Array,
required: true
},
'route': {
type: String,
required: true
},
'isTask': {
type: Boolean,
required: false,
default: false
},
'submitLabel': {
type: String,
required: false,
default: "Submit"
},
'selfUpdate': {
type: Boolean,
required: false,
default: false
},
},
data: function () {
return {
formData: {},
taskRunning: false
}
},
created() {
this.initialiseFormData()
if (this.selfUpdate) {
this.getFormData()
}
},
methods: {
initialiseFormData() {
/*
This function initialises the form data.
Limitations in Vue mean that newly created formData properties are not reactive.
GETting formData values from the server creates the properties, but the form components
don't get updated because of this limitation.
Here, we step through the form schema, and properly create reactive properties for each component.
*/
for (const field of this.schema) {
if (Array.isArray(field)) {
for (const subfield of field) {
console.log(subfield.name)
this.$set(this.formData, subfield.name, null)
}
}
else {
console.log(field.name)
this.$set(this.formData, field.name, null)
}
}
},
updateForm(fieldName, value) {
this.$set(this.formData, fieldName, value);
this.$emit('input', this.formData)
},
submitForm() {
if (this.isTask == true) {
this.newLongRequest(this.formData)
}
else {
this.newQuickRequest(this.formData)
}
},
getFormData: function() {
// Send a quick request
axios.get(this.submitApiUri)
.then(response => {
console.log(response.data)
Object.assign(this.formData, response.data)
})
.catch(error => {
this.modalError(error) // Let mixin handle error
})
},
newQuickRequest: function(params) {
console.log(this.submitApiUri)
console.log(params)
// Send a quick request
axios.post(this.submitApiUri, params)
.then(response => {
// Do something with the response
console.log(response)
// Update the form data if we're self-updating
if (this.selfUpdate) {
this.getFormData()
}
})
.catch(error => {
this.modalError(error) // Let mixin handle error
})
},
newLongRequest: function(params) {
this.taskRunning = true
axios.post(this.submitApiUri, params)
.then(response => {
console.log("Task ID: " + response.data.id)
// Start the store polling TaskId for success
return this.$store.dispatch('pollTask', [response.data.id, null, null])
})
.then(response => {
// Do something with the response
console.log(response)
})
.catch(error => {
this.modalError(error) // Let mixin handle error
})
.finally(() => {
console.log("Cleaning up after task.")
this.taskRunning = false
// Update the form data if we're self-updating
if (this.selfUpdate) {
this.getFormData()
}
})
},
},
computed: {
pluginApiUri: function () {
return this.$store.getters.uri + "/plugin"
},
submitApiUri: function () {
return this.pluginApiUri + this.route
},
}
}
</script>
<style scoped>
.flex-container {
display: flex;
}
.flex-container > div {
flex-basis: 100%
}
</style>

View file

@ -0,0 +1,52 @@
<template>
<div>
<label>{{label}}</label>
<div class="uk-form-controls">
<div v-for="option in options" :key="option">
<label>
<input class="uk-checkbox" type="checkbox" v-bind:value="option" v-bind:checked="(value && value.includes(option))" @change="updateValue($event.target)">
{{ option }}
</label>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'checkList',
props: [
'options',
'name',
'label',
'value'
],
methods: {
updateValue(target) {
var newSelected = this.value != null ? [...this.value] : [] // Clone value array
if (target.checked) {
if (!newSelected.includes(target.value)) {
newSelected.push(target.value)
}
}
else {
if (newSelected.includes(target.value)) {
var newSelected = newSelected.filter(function(value, index, arr){
return value != target.value;
})
}
}
this.$emit('input', newSelected)
}
}
}
</script>
<style scoped></style>

View file

@ -0,0 +1,19 @@
<template>
<div>
<p v-html="content"></p>
</div>
</template>
<script>
export default {
name: 'htmlBlock',
props: [
'label',
'name',
'content'
]
}
</script>
<style scoped></style>

View file

@ -0,0 +1,33 @@
<template>
<div>
<label class="uk-form-label">{{label}}</label>
<input
class="uk-input uk-form-small"
type="number"
:name="name"
:value="value"
@input="$emit('input', $event.target.value)"
:placeholder="placeholder"
>
</div>
</template>
<script>
export default {
name: 'numberInput',
props: [
'placeholder',
'label',
'name',
'value'
]
}
</script>
<style scoped></style>

View file

@ -0,0 +1,29 @@
<template>
<div>
<label>{{label}}</label>
<div class="uk-form-controls">
<div v-for="option in options" :key="option">
<label><input class="uk-radio" type="radio" :name="name" :value="option" :checked="value==option" @input="$emit('input', $event.target.value)"> {{ option }}</label>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'radioList',
props: [
'options',
'name',
'label',
'value'
]
}
</script>
<style scoped></style>

View file

@ -0,0 +1,33 @@
<template>
<div>
<label class="uk-form-label">{{label}}</label>
<select
class="uk-select uk-form-small"
:multiple="multi"
:value="value"
@input="$emit('input', $event.target.value)"
>
<option v-for="option in options" :key="option">
{{option}}
</option>
</select>
</div>
</template>
<script>
export default {
name: 'selectList',
props: [
'multi',
'options',
'name',
'label',
'value'
]
}
</script>
<style scoped></style>

View file

@ -0,0 +1,30 @@
<template>
<div>
<label class="uk-form-label">{{label}}</label>
<input
class="uk-input uk-form-small"
type="text"
v-bind:name="name"
v-bind:value="value"
v-bind:placeholder="placeholder"
@input="$emit('input',$event.target.value)"
>
</div>
</template>
<script>
export default {
name: 'textInput',
props: [
'placeholder',
'label',
'name',
'value'
]
}
</script>
<style scoped></style>

View file

@ -13,7 +13,9 @@
<div class="uk-width-1-1 uk-margin-small uk-margin-remove-left uk-margin-remove-right" uk-grid> <div class="uk-width-1-1 uk-margin-small uk-margin-remove-left uk-margin-remove-right" uk-grid>
<div class="uk-margin-remove-top uk-padding-remove uk-width-expand">{{ metadata.filename }}</div> <div class="uk-margin-remove-top uk-padding-remove uk-width-expand">{{ metadata.filename }}</div>
<div class="uk-margin-remove-top uk-padding-remove uk-width-auto"> <div class="uk-margin-remove-top uk-padding-remove uk-width-auto">
<a href="#" v-on:click="delCaptureConfirm()" class="uk-icon-link" uk-icon="trash"></a> <a href="#" v-on:click="delCaptureConfirm()" class="uk-icon">
<i class="material-icons">delete</i>
</a>
</div> </div>
</div> </div>
@ -59,7 +61,7 @@
<form class="uk-modal-dialog uk-modal-body uk-margin-auto-vertical" v-bind:class="{ 'uk-light uk-background-secondary': $store.state.globalSettings.darkMode }" @submit.prevent="handleTagSubmit"> <form class="uk-modal-dialog uk-modal-body uk-margin-auto-vertical" v-bind:class="{ 'uk-light uk-background-secondary': $store.state.globalSettings.darkMode }" @submit.prevent="handleTagSubmit">
<div class="uk-inline"> <div class="uk-inline">
<span class="uk-form-icon" uk-icon="icon: tag"></span> <span class="uk-form-icon"><i class="material-icons">label</i></span>
<input v-model="newtag" class="uk-input uk-form-width-medium uk-form-small" type="text" name="tagname" placeholder="tag"> <input v-model="newtag" class="uk-input uk-form-width-medium uk-form-small" type="text" name="tagname" placeholder="tag">
<button class="uk-button uk-button-default uk-margin-left uk-form-small uk-modal-close" type="button">Cancel</button> <button class="uk-button uk-button-default uk-margin-left uk-form-small uk-modal-close" type="button">Cancel</button>

View file

@ -4,8 +4,8 @@
<nav class="uk-navbar-container uk-navbar-transparent navbar" uk-navbar="mode: click"> <nav class="uk-navbar-container uk-navbar-transparent navbar" uk-navbar="mode: click">
<div class="uk-navbar-left uk-padding-remove-top uk-padding-remove-bottom"> <div class="uk-navbar-left uk-padding-remove-top uk-padding-remove-bottom">
<ul class="uk-navbar-nav"> <ul class="uk-navbar-nav">
<li v-bind:class="[sortDescending ? 'uk-active' : '']"><a v-on:click="sortDescending=true;" class="uk-icon-link" href="#" uk-icon="icon: arrow-down"></a></li> <li v-bind:class="[sortDescending ? 'uk-active' : '']"><a v-on:click="sortDescending=true;" class="uk-icon" href="#"><i class="material-icons">keyboard_arrow_down</i></a></li>
<li v-bind:class="[!sortDescending ? 'uk-active' : '']"><a v-on:click="sortDescending=false;" class="uk-icon-link" href="#" uk-icon="icon: arrow-up"></a></li> <li v-bind:class="[!sortDescending ? 'uk-active' : '']"><a v-on:click="sortDescending=false;" class="uk-icon" href="#"><i class="material-icons">keyboard_arrow_up</i></a></li>
<li> <li>
<a href="#">Filter</a> <a href="#">Filter</a>
<div class="uk-navbar-dropdown" v-bind:class="{ 'uk-light uk-background-secondary': $store.state.globalSettings.darkMode }"> <div class="uk-navbar-dropdown" v-bind:class="{ 'uk-light uk-background-secondary': $store.state.globalSettings.darkMode }">
@ -25,9 +25,11 @@
<div v-if="$store.getters.ready" class="uk-padding-remove-top" uk-lightbox="toggle: .lightbox-link"> <div v-if="$store.getters.ready" class="uk-padding-remove-top" uk-lightbox="toggle: .lightbox-link">
<div v-if="(galleryFolder)" class="uk-padding uk-padding-remove-right uk-padding-remove-bottom"> <div v-if="(galleryFolder)" class="uk-flex uk-flex-middle uk-padding uk-padding-remove-horizontal uk-padding-remove-bottom">
<a href="#" v-on:click="galleryFolder=''" class="uk-icon-button" uk-icon="arrow-left"></a> <a href="#" v-on:click="galleryFolder=''" class="uk-icon uk-margin-remove"><i class="material-icons">arrow_back</i></a>
<h3 class="uk-inline uk-float-right uk-margin-remove"><b>SCAN</b> {{ allScans[galleryFolder].metadata.filename }}</h3> <div class="uk-margin-left">
<h3 class="uk-margin-remove uk-margin-left"><b>SCAN</b> {{ allScans[galleryFolder].metadata.filename }}</h3>
</div>
</div> </div>
<div class="uk-grid-medium uk-grid-match uk-margin-top" uk-grid> <div class="uk-grid-medium uk-grid-match uk-margin-top" uk-grid>

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="streamDisplay uk-width-1-1 uk-height-1-1 scrollTarget" id="streamDisplay" ref="streamDisplay"> <div ref="streamDisplay" class="stream-display uk-width-1-1 uk-height-1-1 scrollTarget" id="stream-display">
<img class="uk-align-center uk-margin-remove-bottom" v-on:dblclick="clickmonitor" v-if="showStream" v-bind:src="streamImgUri" alt="Stream"> <img class="uk-align-center uk-margin-remove-bottom" v-on:dblclick="clickmonitor" v-if="showStream" v-bind:src="streamImgUri" alt="Stream">
@ -23,33 +23,37 @@ import axios from 'axios'
// Export main app // Export main app
export default { export default {
name: 'streamDisplay', name: 'stream-display',
data: function () { data: function () {
return { return {
displaySize: [0, 0], displaySize: [0, 0],
displayPosition: [0, 0], displayPosition: [0, 0],
GpuPreviewActive: false, GpuPreviewActive: false,
resizeTeimoutId: setTimeout(this.doneResizing, 500) resizeTimeoutId: setTimeout(this.doneResizing, 500)
} }
}, },
mounted() { mounted() {
// A global signal listener to change the GPU preview state
this.$root.$on('globalResizePreview', (state) => {
this.handleDoneResize()
})
// A global signal listener to change the GPU preview state // A global signal listener to change the GPU preview state
this.$root.$on('globalTogglePreview', (state) => { this.$root.$on('globalTogglePreview', (state) => {
console.log(`Toggling preview to ${state}`) console.log(`Toggling preview to ${state}`)
this.previewRequest(state) this.previewRequest(state)
}) })
// Mutation observer
this.sizeObserver = new ResizeObserver(entries => {
this.handleResize() // For any element attached to the observer, run handleResize() on change
entries.forEach(entry => {}) // Optional: Run something per entry
});
// Fetch streamDisplay by ref
const streamDisplayElement = this.$refs.streamDisplay.parentNode;
// Attach streamDisplay to the size observer
this.sizeObserver.observe(streamDisplayElement);
}, },
created: function () { created: function () {
// Add resize listener
window.addEventListener('resize', this.handleResize);
// Watch for host 'ready' // Watch for host 'ready'
this.$store.watch( this.$store.watch(
(state)=>{ (state)=>{
@ -57,29 +61,27 @@ export default {
}, },
(newValue, oldValue)=>{ (newValue, oldValue)=>{
// 'ready' changed, so do something // 'ready' changed, so do something
console.log(oldValue)
console.log(newValue)
this.previewRequest(this.$store.state.globalSettings.autoGpuPreview) this.previewRequest(this.$store.state.globalSettings.autoGpuPreview)
} }
) )
}, },
beforeDestroy: function () { beforeDestroy: function () {
// Remove resize listener // Disconnect the size observer
window.removeEventListener('resize', this.handleResize) this.sizeObserver.disconnect()
}, },
methods: { methods: {
clickmonitor: function(event) { clickmonitor: function(event) {
// Calculate steps from event coordinates and store config FOV // Calculate steps from event coordinates and store config FOV
var xCoordinate = event.offsetX; let xCoordinate = event.offsetX;
var yCoordinate = event.offsetY; let yCoordinate = event.offsetY;
var xRelative = (0.5*event.target.offsetWidth - xCoordinate)/event.target.offsetWidth; let xRelative = (0.5*event.target.offsetWidth - xCoordinate)/event.target.offsetWidth;
var yRelative = (0.5*event.target.offsetHeight - yCoordinate)/event.target.offsetHeight; let yRelative = (0.5*event.target.offsetHeight - yCoordinate)/event.target.offsetHeight;
var xSteps = xRelative * this.$store.state.apiConfig.fov[0]; let xSteps = xRelative * this.$store.state.apiConfig.fov[0];
var ySteps = yRelative * this.$store.state.apiConfig.fov[1]; let ySteps = yRelative * this.$store.state.apiConfig.fov[1];
// Emit a signal to move, acted on by panelNavigate.vue // Emit a signal to move, acted on by panelNavigate.vue
this.$root.$emit('globalMoveEvent', xSteps, ySteps, 0, false) this.$root.$emit('globalMoveEvent', xSteps, ySteps, 0, false)
@ -87,12 +89,13 @@ export default {
handleResize: function(event) { handleResize: function(event) {
// Only fires resize event after no resize in 500ms (prevents resize event spam) // Only fires resize event after no resize in 500ms (prevents resize event spam)
clearTimeout(this.resizeTeimoutId); clearTimeout(this.resizeTimeoutId);
this.resizeTeimoutId = setTimeout(this.handleDoneResize, 500) this.resizeTimeoutId = setTimeout(this.handleDoneResize, 250)
}, },
handleDoneResize: function() { handleDoneResize: function() {
// Recalculate size // Recalculate size
console.log("Recalculating frame size")
this.recalculateSize(); this.recalculateSize();
if (this.$store.state.globalSettings.autoGpuPreview == true && this.GpuPreviewActive == true) { if (this.$store.state.globalSettings.autoGpuPreview == true && this.GpuPreviewActive == true) {
// Reload preview // Reload preview
@ -101,23 +104,24 @@ export default {
}, },
recalculateSize: function () { recalculateSize: function () {
console.log("Recalculating window dimensions...") let element = this.$refs.streamDisplay.parentNode;
// Stacking parentNode is a hacky fix let bound = element.getBoundingClientRect()
// For some reason, when switching tabs, width was always half what it should be,
// until the size was recalculated at some later time. Probably something to do
// with tab transition. This parentNode stuff instead reads the size of the tab
// container, irrespective of WHICH tab is selected. It's nasty, but works.
let element = this.$refs.streamDisplay.parentNode.parentNode;
let size = [element.clientWidth, element.clientHeight]; let elementSize = [bound.width, bound.height]
let elem_pos = [element.getBoundingClientRect().left, element.getBoundingClientRect().top]; let elementPositionOnWindow = [bound.left, bound.top]
let wind_pos = [window.screenX, window.screenY]; let windowPositionOnDisplay = [window.screenX, window.screenY]
let navHeight = window.outerHeight - window.innerHeight; let windowChromeHeight = window.outerHeight - window.innerHeight
let position = [Math.max(0, wind_pos[0] + elem_pos[0]), Math.max(0, wind_pos[1] + elem_pos[1] + navHeight)]; let elementPositionOnDisplay = [
Math.max(0, windowPositionOnDisplay[0] + elementPositionOnWindow[0]),
Math.max(0, windowPositionOnDisplay[1] + elementPositionOnWindow[1] + windowChromeHeight)
]
this.displaySize = size; this.displaySize = elementSize
this.displayPosition = position; this.displayPosition = elementPositionOnDisplay
console.log(`Size: ${this.displaySize}`)
console.log(`Position: ${this.displayPosition}`)
}, },
previewRequest: function(state) { previewRequest: function(state) {
@ -147,7 +151,7 @@ export default {
} }
} }
else { else {
var letpayload = {} var payload = {}
} }
// Send preview request // Send preview request
@ -182,13 +186,13 @@ export default {
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
.streamDisplay img { .stream-display img {
height: 100%; height: 100%;
text-align: center; text-align: center;
object-fit: contain object-fit: contain
} }
.streamDisplay { .stream-display {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }

View file

@ -3,9 +3,9 @@ import App from './App.vue'
import store from './store' import store from './store'
import UIkit from 'uikit'; import UIkit from 'uikit';
// Import UIkit icon set
import Icons from 'uikit/dist/js/uikit-icons'; // Import MD icons
UIkit.use(Icons); import 'material-design-icons/iconfont/material-icons.css'
Vue.config.productionTip = false Vue.config.productionTip = false
@ -78,7 +78,7 @@ Vue.mixin({
console.log(errormsg) console.log(errormsg)
} }
this.$store.commit('setError', errormsg); this.$store.commit('setError', errormsg);
UIkit.notification({message: `<span uk-icon=\'icon: warning\'></span> ${errormsg}`, status: 'danger'}) UIkit.notification({message: `${errormsg}`, status: 'danger'})
}, },
getLocalStorageObj: function(keyName) { getLocalStorageObj: function(keyName) {

View file

@ -15,6 +15,7 @@ export default new Vuex.Store({
error: '', error: '',
apiConfig: {}, apiConfig: {},
apiState: {}, apiState: {},
apiPlugins: [],
globalSettings: { globalSettings: {
disableStream: false, disableStream: false,
autoGpuPreview: false, autoGpuPreview: false,
@ -38,6 +39,9 @@ export default new Vuex.Store({
commitState(state, stateData) { commitState(state, stateData) {
state.apiState = stateData; state.apiState = stateData;
}, },
commitPlugins(state, pluginData) {
state.apiPlugins = pluginData;
},
changeSetting(state, [key, value]) { changeSetting(state, [key, value]) {
state.globalSettings[key] = value; state.globalSettings[key] = value;
}, },
@ -47,6 +51,7 @@ export default new Vuex.Store({
state.error = null state.error = null
state.apiConfig = {} state.apiConfig = {}
state.apiState = {} state.apiState = {}
state.apiPlugins = []
}, },
setConnected(state) { setConnected(state) {
state.waiting = false state.waiting = false
@ -72,6 +77,9 @@ export default new Vuex.Store({
.then (() => { .then (() => {
context.dispatch('updateState') context.dispatch('updateState')
}) })
.then (() => {
context.dispatch('updatePlugins')
})
.then (() => { .then (() => {
resolve() resolve()
}) })
@ -116,6 +124,25 @@ export default new Vuex.Store({
return new Promise(sendRequest) return new Promise(sendRequest)
}, },
updatePlugins(context, uri=`${context.getters.uri}/plugin`) {
context.commit('changeWaiting', true)
var sendRequest = function(resolve, reject) {
axios.get(uri)
.then(response => {
console.log("PLUGINS")
console.log(response.data)
context.commit('commitPlugins', response.data)
context.commit('setConnected')
resolve()
})
.catch(error => {
reject(new Error(error))
})
}
return new Promise(sendRequest)
},
pollTask(context, [taskId, timeout, interval]) { pollTask(context, [taskId, timeout, interval]) {
var endTime = Number(new Date()) + (timeout*1000 || 30000); var endTime = Number(new Date()) + (timeout*1000 || 30000);
interval = interval*1000 || 500; interval = interval*1000 || 500;
@ -128,7 +155,7 @@ export default new Vuex.Store({
var result = response.data.status var result = response.data.status
// If the task ends with success // If the task ends with success
if(result == 'success') { if(result == 'success') {
resolve(response.data.return) resolve(response.data)
} }
// If task ends with an error // If task ends with an error
else if (result == 'error') { else if (result == 'error') {
@ -136,7 +163,7 @@ export default new Vuex.Store({
} }
// If the condition isn't met but the timeout hasn't elapsed, go again // If the condition isn't met but the timeout hasn't elapsed, go again
else if (Number(new Date()) < endTime) { else if (Number(new Date()) < endTime) {
setTimeout(checkCondition, interval, resolve, reject) setTimeout(checkCondition, interval, resolve, reject)
} }
// Didn't match and too much time, reject! // Didn't match and too much time, reject!
else { else {