Merge branch 'reset-to-default' into 'v3'
Reset to default option for properties Closes #574 See merge request openflexure/openflexure-microscope-server!640
This commit is contained in:
commit
cae5ffa29b
7 changed files with 317 additions and 66 deletions
|
|
@ -15,6 +15,8 @@
|
|||
@darkened-primary-color: darken(@global-primary-background, 15%);
|
||||
@inverse-global-color: fade(@global-inverse-color, 80%);
|
||||
|
||||
@very-light-primary-color: lighten(@global-primary-background, 40%);
|
||||
|
||||
@global-border: #d5d5d5;
|
||||
|
||||
// UIkit
|
||||
|
|
@ -265,6 +267,32 @@
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Text
|
||||
*/
|
||||
|
||||
// A text label for an input control that is highlighted
|
||||
.ofm-highlighted-label {
|
||||
color: @darkened-primary-color;
|
||||
// 500 is slightly more bold than normal text (400) and less than semi-bold (600)
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
// Reducing the ui-kit margins from 20px to 5px
|
||||
.ofm-close-pars p{
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
// Use CSS "next-sibling combinator" to get p-tags following another tag at the same level.
|
||||
.ofm-close-pars * + p{
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.hook-inverse() {
|
||||
.ofm-highlighted-label{
|
||||
color: @very-light-primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Links
|
||||
|
|
@ -277,11 +305,29 @@
|
|||
}
|
||||
|
||||
.uk-alert-success{
|
||||
color: rgba(28, 131, 45);
|
||||
color: rgb(28, 131, 45);
|
||||
background-color: rgba(130, 221, 145, 0.671);
|
||||
}
|
||||
}
|
||||
|
||||
// For links that shouldn't look styled like links (such as in menus)
|
||||
.ofm-unstyled-link {
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
color: @global-color;
|
||||
}
|
||||
|
||||
.ofm-unstyled-link.disabled{
|
||||
cursor: default;
|
||||
color: @global-muted-color;
|
||||
}
|
||||
|
||||
.hook-inverse() {
|
||||
.ofm-unstyled-link{
|
||||
color: @inverse-global-color;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Buttons
|
||||
*/
|
||||
|
|
|
|||
74
webapp/src/components/genericComponents/iconButton.vue
Normal file
74
webapp/src/components/genericComponents/iconButton.vue
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<template>
|
||||
<a class="sync-button" :class="{ disabled: disabled }" @click.prevent="$emit('click')">
|
||||
<span class="material-symbols-outlined sync-icon">
|
||||
{{ icon }}
|
||||
</span>
|
||||
<span v-if="showLabel" class="icon-label ofm-unstyled-link" :class="{ disabled: disabled }">
|
||||
{{ label }}
|
||||
</span>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "IconButton",
|
||||
|
||||
props: {
|
||||
icon: {
|
||||
type: String,
|
||||
default: "sync_alt",
|
||||
},
|
||||
showLabel: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
|
||||
emits: ["click"],
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sync-button {
|
||||
display: inline-flex;
|
||||
flex-grow: 0;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.sync-button.disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.sync-button .material-symbols-outlined.sync-icon {
|
||||
color: #888;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.sync-button.disabled .material-symbols-outlined.sync-icon {
|
||||
color: #aaa;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.sync-button:not(.disabled):hover .material-symbols-outlined.sync-icon {
|
||||
color: #c5247f;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.icon-label {
|
||||
padding-left: 10px;
|
||||
padding-top: 3px;
|
||||
}
|
||||
</style>
|
||||
73
webapp/src/components/genericComponents/miniMenu.vue
Normal file
73
webapp/src/components/genericComponents/miniMenu.vue
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<template>
|
||||
<div ref="mini-menu" class="mini-menu">
|
||||
<a class="icon" @click.prevent="isOpen = !isOpen">
|
||||
<span class="material-symbols-outlined"> more_vert </span>
|
||||
</a>
|
||||
<div v-if="isOpen" class="dropdown-menu ofm-close-pars ofm-opaque-element">
|
||||
<slot :close="close"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MiniMenu",
|
||||
|
||||
props: {},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isOpen: false,
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
window.addEventListener("click", this.handleClickOutside);
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
window.removeEventListener("click", this.handleClickOutside);
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleClickOutside(event) {
|
||||
if (!this.$refs["mini-menu"].contains(event.target)) {
|
||||
this.isOpen = false;
|
||||
}
|
||||
},
|
||||
close() {
|
||||
this.isOpen = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import url("../../assets/less/variables.less");
|
||||
|
||||
.mini-menu {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
position: absolute;
|
||||
top: 25px;
|
||||
right: 0;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
max-height: 250px;
|
||||
width: 180px;
|
||||
overflow-y: auto;
|
||||
z-index: 1000;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
user-select: none;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.icon:hover {
|
||||
color: #c5247f;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
<template>
|
||||
<div>
|
||||
<label v-if="useDropdown" class="uk-form-label">
|
||||
{{ label }}
|
||||
<label v-if="useDropdown">
|
||||
<span class="uk-form-label" :class="{ 'ofm-highlighted-label': isChangedFomDefault }">
|
||||
{{ label }}
|
||||
</span>
|
||||
<div class="input-and-buttons-container">
|
||||
<select
|
||||
v-model="internalValue"
|
||||
|
|
@ -12,11 +14,17 @@
|
|||
{{ display }}
|
||||
</option>
|
||||
</select>
|
||||
<sync-property-button @click="requestUpdate" />
|
||||
<property-control-menu
|
||||
:can-reset="isChangedFomDefault"
|
||||
@reset-default="resetDefault"
|
||||
@request-update="requestUpdate"
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
<label v-if="!useDropdown && dataType == 'number'" class="uk-form-label"
|
||||
>{{ label }}
|
||||
<label v-if="!useDropdown && dataType == 'number'">
|
||||
<span class="uk-form-label" :class="{ 'ofm-highlighted-label': isChangedFomDefault }">
|
||||
{{ label }}
|
||||
</span>
|
||||
<div class="input-and-buttons-container">
|
||||
<div class="uk-inline number-wrapper">
|
||||
<input
|
||||
|
|
@ -42,11 +50,15 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<sync-property-button @click="requestUpdate" />
|
||||
<property-control-menu
|
||||
:can-reset="isChangedFomDefault"
|
||||
@reset-default="resetDefault"
|
||||
@request-update="requestUpdate"
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
<div v-if="!useDropdown && dataType == 'boolean'" class="input-and-buttons-container">
|
||||
<label class="uk-form-label property-input">
|
||||
<label class="property-input">
|
||||
<input
|
||||
ref="checkbox"
|
||||
v-model="internalValue"
|
||||
|
|
@ -55,12 +67,20 @@
|
|||
type="checkbox"
|
||||
@change="sendValue"
|
||||
/>
|
||||
{{ label }}
|
||||
<span class="uk-form-label" :class="{ 'ofm-highlighted-label': isChangedFomDefault }">
|
||||
{{ label }}
|
||||
</span>
|
||||
</label>
|
||||
<sync-property-button @click="requestUpdate" />
|
||||
<property-control-menu
|
||||
:can-reset="isChangedFomDefault"
|
||||
@reset-default="resetDefault"
|
||||
@request-update="requestUpdate"
|
||||
/>
|
||||
</div>
|
||||
<label v-if="!useDropdown && dataType == 'number_array'" class="uk-form-label"
|
||||
>{{ label }}
|
||||
<label v-if="!useDropdown && dataType == 'number_array'">
|
||||
<span class="uk-form-label" :class="{ 'ofm-highlighted-label': isChangedFomDefault }">
|
||||
{{ label }}
|
||||
</span>
|
||||
<div class="input-and-buttons-container">
|
||||
<input
|
||||
v-for="i in valueLength"
|
||||
|
|
@ -75,11 +95,17 @@
|
|||
@keydown="keyDown"
|
||||
@animationend="animationEnd"
|
||||
/>
|
||||
<sync-property-button @click="requestUpdate" />
|
||||
<property-control-menu
|
||||
:can-reset="isChangedFomDefault"
|
||||
@reset-default="resetDefault"
|
||||
@request-update="requestUpdate"
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
<label v-if="!useDropdown && dataType == 'number_object'" class="uk-form-label"
|
||||
>{{ label }}
|
||||
<label v-if="!useDropdown && dataType == 'number_object'">
|
||||
<span class="uk-form-label" :class="{ 'ofm-highlighted-label': isChangedFomDefault }">
|
||||
{{ label }}
|
||||
</span>
|
||||
<div v-for="(val, key) in modelValue" :key="key">
|
||||
<label>{{ internalLabels[key] }}</label>
|
||||
<div class="input-and-buttons-container">
|
||||
|
|
@ -94,12 +120,18 @@
|
|||
@keydown="keyDown"
|
||||
@animationend="animationEnd"
|
||||
/>
|
||||
<sync-property-button @click="requestUpdate" />
|
||||
<property-control-menu
|
||||
:can-reset="isChangedFomDefault"
|
||||
@reset-default="resetDefault"
|
||||
@request-update="requestUpdate"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label v-if="!useDropdown && dataType == 'string'" class="uk-form-label"
|
||||
>{{ label }}
|
||||
<label v-if="!useDropdown && dataType == 'string'">
|
||||
<span class="uk-form-label" :class="{ 'ofm-highlighted-label': isChangedFomDefault }">
|
||||
{{ label }}
|
||||
</span>
|
||||
<div class="input-and-buttons-container">
|
||||
<input
|
||||
v-model="internalValue"
|
||||
|
|
@ -111,11 +143,17 @@
|
|||
@keydown="keyDown"
|
||||
@animationend="animationEnd"
|
||||
/>
|
||||
<sync-property-button @click="requestUpdate" />
|
||||
<property-control-menu
|
||||
:can-reset="isChangedFomDefault"
|
||||
@reset-default="resetDefault"
|
||||
@request-update="requestUpdate"
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
<label v-if="!useDropdown && dataType == 'other'" class="uk-form-label"
|
||||
>{{ label }}
|
||||
<label v-if="!useDropdown && dataType == 'other'">
|
||||
<span class="uk-form-label" :class="{ 'ofm-highlighted-label': isChangedFomDefault }">
|
||||
{{ label }}
|
||||
</span>
|
||||
<div class="input-and-buttons-container">
|
||||
<input
|
||||
v-model="internalValue"
|
||||
|
|
@ -129,13 +167,13 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import syncPropertyButton from "./syncPropertyButton.vue";
|
||||
import PropertyControlMenu from "./propertyControlMenu.vue";
|
||||
|
||||
export default {
|
||||
name: "InputFromSchema",
|
||||
|
||||
components: {
|
||||
syncPropertyButton,
|
||||
PropertyControlMenu,
|
||||
},
|
||||
props: {
|
||||
dataSchema: {
|
||||
|
|
@ -267,6 +305,13 @@ export default {
|
|||
if (this.dataType !== "number") return undefined;
|
||||
return this.dataSchema.minimum;
|
||||
},
|
||||
/**
|
||||
* Is the value changed from default (If no default is set this returns false)
|
||||
*/
|
||||
isChangedFomDefault() {
|
||||
if (this.dataSchema.default === undefined) return false;
|
||||
return this.dataSchema.default !== this.modelValue;
|
||||
},
|
||||
/**
|
||||
* The step size for numerical spinners.
|
||||
*
|
||||
|
|
@ -321,6 +366,11 @@ export default {
|
|||
this.internalValue = this.modelValue;
|
||||
this.$emit("requestUpdate");
|
||||
},
|
||||
resetDefault: async function () {
|
||||
if (this.isChangedFomDefault) {
|
||||
this.$emit("sendValue", this.dataSchema.default);
|
||||
}
|
||||
},
|
||||
sendValue: async function () {
|
||||
this.$emit("sendValue", this.internalValue);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
<template>
|
||||
<mini-menu v-slot="{ close }">
|
||||
<p>
|
||||
<icon-button
|
||||
icon="refresh"
|
||||
:show-label="true"
|
||||
label="Reset to default"
|
||||
:disabled="!canReset"
|
||||
@click="
|
||||
$emit('reset-default');
|
||||
close();
|
||||
"
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<icon-button
|
||||
icon="sync_alt"
|
||||
:show-label="true"
|
||||
label="Re-sync value"
|
||||
@click="
|
||||
$emit('request-update');
|
||||
close();
|
||||
"
|
||||
/>
|
||||
</p>
|
||||
</mini-menu>
|
||||
</template>
|
||||
<script>
|
||||
import iconButton from "@/components/genericComponents/iconButton.vue";
|
||||
import MiniMenu from "@/components/genericComponents/miniMenu.vue";
|
||||
|
||||
export default {
|
||||
name: "PropertyControlMenu",
|
||||
|
||||
components: {
|
||||
iconButton,
|
||||
MiniMenu,
|
||||
},
|
||||
|
||||
props: {
|
||||
canReset: {
|
||||
type: Boolean,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
emits: ["request-update", "reset-default"],
|
||||
};
|
||||
</script>
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
<template>
|
||||
<a class="sync-button" @click.prevent="$emit('click')">
|
||||
<span
|
||||
class="material-symbols-outlined sync-icon"
|
||||
title="Reread value from microscope. Required if microscope is updated externally"
|
||||
>
|
||||
sync
|
||||
</span>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "SyncPropertyButton",
|
||||
|
||||
emits: ["click"],
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sync-button {
|
||||
flex-grow: 0;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.material-symbols-outlined.sync-icon {
|
||||
color: #888;
|
||||
transition:
|
||||
transform 0.3s ease,
|
||||
color 0.3s ease;
|
||||
}
|
||||
|
||||
.material-symbols-outlined.sync-icon:hover {
|
||||
transform: rotate(-90deg);
|
||||
color: #c5247f;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -22,7 +22,7 @@ and zero position buttons. It also includes the d-pad.
|
|||
type="number"
|
||||
@keyup.enter="startMoveTask"
|
||||
/>
|
||||
<sync-property-button @click="updatePosition" />
|
||||
<icon-button icon="sync_alt" @click="updatePosition" />
|
||||
</div>
|
||||
<p>
|
||||
<action-button
|
||||
|
|
@ -59,7 +59,7 @@ and zero position buttons. It also includes the d-pad.
|
|||
|
||||
<script>
|
||||
import ActionButton from "../../labThingsComponents/actionButton.vue";
|
||||
import syncPropertyButton from "../../labThingsComponents/syncPropertyButton.vue";
|
||||
import iconButton from "@/components/genericComponents/iconButton.vue";
|
||||
import stageControlButtons from "./stageControlButtons.vue";
|
||||
import { eventBus } from "../../../eventBus.js";
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ export default {
|
|||
|
||||
components: {
|
||||
ActionButton,
|
||||
syncPropertyButton,
|
||||
iconButton,
|
||||
stageControlButtons,
|
||||
},
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue