Merge branch 'dropdown-gui' into 'v3'

Optional options in UI property control become dropdown

See merge request openflexure/openflexure-microscope-server!477
This commit is contained in:
Joe Knapper 2026-02-18 16:43:45 +00:00
commit a6b95f0948
4 changed files with 55 additions and 7 deletions

View file

@ -1,6 +1,6 @@
"""Functionality for communicating the required user interface for a thing."""
from typing import Any
from typing import Any, Optional
from pydantic import BaseModel
@ -90,6 +90,13 @@ class PropertyControl(BaseModel):
read_back_delay: int = 1000
"""The delay in ms before reading back the property."""
options: Optional[dict[str, str | int | float | bool]] = None
"""A mapping of UI display name to value, used for creating a dropdown.
These options aren't validated here in any way. Any invalid values will be rejected
when selected.
"""
def property_control_for(
thing: lt.Thing, property_name: str, **kwargs: Any

View file

@ -1,6 +1,21 @@
<template>
<div>
<label v-if="dataType == 'number'" class="uk-form-label"
<label v-if="useDropdown" class="uk-form-label">
{{ label }}
<div class="input-and-buttons-container">
<select
v-model="internalValue"
class="uk-form-small numeric-setting-line-input dropdown"
@change="sendValue"
>
<option v-for="(value, display) in options" :key="value" :value="value">
{{ display }}
</option>
</select>
<sync-property-button @click="requestUpdate" />
</div>
</label>
<label v-if="!useDropdown && dataType == 'number'" class="uk-form-label"
>{{ label }}
<div class="input-and-buttons-container">
<input
@ -18,7 +33,7 @@
<sync-property-button @click="requestUpdate" />
</div>
</label>
<div v-if="dataType == 'boolean'" class="input-and-buttons-container">
<div v-if="!useDropdown && dataType == 'boolean'" class="input-and-buttons-container">
<label class="uk-form-label numeric-setting-line-input">
<input
ref="checkbox"
@ -32,7 +47,7 @@
</label>
<sync-property-button @click="requestUpdate" />
</div>
<label v-if="dataType == 'number_array'" class="uk-form-label"
<label v-if="!useDropdown && dataType == 'number_array'" class="uk-form-label"
>{{ label }}
<div class="input-and-buttons-container">
<input
@ -51,7 +66,7 @@
<sync-property-button @click="requestUpdate" />
</div>
</label>
<label v-if="dataType == 'number_object'" class="uk-form-label"
<label v-if="!useDropdown && dataType == 'number_object'" class="uk-form-label"
>{{ label }}
<div v-for="(val, key) in modelValue" :key="key">
<label>{{ internalLabels[key] }}</label>
@ -71,7 +86,7 @@
</div>
</div>
</label>
<label v-if="dataType == 'string'" class="uk-form-label"
<label v-if="!useDropdown && dataType == 'string'" class="uk-form-label"
>{{ label }}
<div class="input-and-buttons-container">
<input
@ -87,7 +102,7 @@
<sync-property-button @click="requestUpdate" />
</div>
</label>
<label v-if="dataType == 'other'" class="uk-form-label"
<label v-if="!useDropdown && dataType == 'other'" class="uk-form-label"
>{{ label }}
<div class="input-and-buttons-container">
<input
@ -128,6 +143,12 @@ export default {
type: Boolean,
default: null,
},
options: {
type: Object,
// Default is Null as None is passed from the server.
default: null,
required: false,
},
},
emits: ["requestUpdate", "sendValue", "animationShown"],
@ -176,6 +197,14 @@ export default {
form.op?.includes('writeproperty')
);
},
useDropdown: function () {
if (this.options === null) return false;
if (["number", "string", "boolean"].includes(this.dataType)) {
return true;
}
console.warn(`Cannot support dropdown for datatype of ${this.dataType}`);
return false;
},
dataType: function () {
let prop = this.dataSchema;
if (prop == undefined) {
@ -328,6 +357,11 @@ export default {
margin-right: 5px;
width: 6em;
}
.dropdown {
background-color: #fff;
border: 1px solid #ccc;
opacity: 1;
}
.edited {
background-color: #fff3cd;
}

View file

@ -4,6 +4,7 @@
:data-schema="propertyDescription"
:label="label"
:animate="animate"
:options="options"
@request-update="readProperty"
@send-value="writeProperty"
@animation-shown="resetAnimate"
@ -44,6 +45,11 @@ export default {
default: 1000,
required: false,
},
options: {
type: Object,
default: null,
required: false,
},
},
data() {

View file

@ -6,6 +6,7 @@
:label="propertyData.label"
:read-back="propertyData.read_back"
:read-back-delay="propertyData.read_back_delay"
:options="propertyData.options"
/>
</template>