Run lint:fix and accept a huge formatting change

This commit is contained in:
Julian Stirling 2025-10-27 18:00:00 +00:00
parent 3900c3e1ad
commit c085d2c0ac
68 changed files with 756 additions and 1047 deletions

View file

@ -1,10 +1,11 @@
<template>
<div
v-observe-visibility="visibilityChanged"
class="uk-margin-remove uk-padding-remove"
>
<div v-observe-visibility="visibilityChanged" class="uk-margin-remove uk-padding-remove">
<div v-if="taskStarted" ref="isPollingElement">
<action-progress-bar v-if="taskStarted && hideOnRun" :progress="progress" :task-status="taskStatus" />
<action-progress-bar
v-if="taskStarted && hideOnRun"
:progress="progress"
:task-status="taskStatus"
/>
<!-- hideOnRun selects if the button hides, don't show progress bar if button doesn't hide. -->
<button
v-if="canTerminate && taskRunning"
@ -22,7 +23,10 @@
:disabled="isDisabled"
:hidden="taskStarted && hideOnRun"
class="uk-button uk-width-1-1"
:class="[isDisabled ? 'uk-button-disabled' : '', buttonPrimary ? 'uk-button-primary' : 'uk-button-default']"
:class="[
isDisabled ? 'uk-button-disabled' : '',
buttonPrimary ? 'uk-button-primary' : 'uk-button-default',
]"
@click="bootstrapTask()"
>
{{ submitLabel }}
@ -35,18 +39,12 @@
class=""
uk-modal="bg-close: false; esc-close: false; stack: true;"
>
<div
id="status-modal"
class="uk-modal-dialog uk-modal-body uk-margin-auto-vertical"
>
<div id="status-modal" class="uk-modal-dialog uk-modal-body uk-margin-auto-vertical">
<h2>{{ submitLabel }}</h2>
<action-log-display :log="log" :task-status="taskStatus" />
<div id="progress-and-cancel-row">
<div class="stretchy">
<action-progress-bar
:progress="progress"
:task-status="taskStatus"
/>
<action-progress-bar :progress="progress" :task-status="taskStatus" />
</div>
<button
@ -84,67 +82,67 @@ export default {
props: {
action: {
type: String,
required: true
required: true,
},
thing: {
type: String,
required: true
required: true,
},
submitData: {
type: [Object, Array],
required: false,
default: () => ({})
default: () => ({}),
},
pollInterval: {
type: Number,
required: false,
default: 1
default: 1,
},
submitLabel: {
type: String,
required: false,
default: "Submit"
default: "Submit",
},
canTerminate: {
type: Boolean,
required: false,
default: true
default: true,
},
requiresConfirmation: {
type: Boolean,
required: false,
default: false
default: false,
},
confirmationMessage: {
type: String,
required: false,
default: "Start task?"
default: "Start task?",
},
buttonPrimary: {
type: Boolean,
required: false,
default: true
default: true,
},
submitOnEvent: {
type: String,
required: false,
default: null
default: null,
},
modalProgress: {
type: Boolean,
required: false,
default: false
default: false,
},
isDisabled: {
type: Boolean,
required: false,
default: false
default: false,
},
hideOnRun: {
type: Boolean,
required: false,
default: true
}
default: true,
},
},
data: function() {
@ -154,14 +152,14 @@ export default {
taskStarted: false,
taskRunning: false,
log: [],
taskStatus: ""
taskStatus: "",
};
},
computed: {
submitUrl() {
return this.thingActionUrl(this.thing, this.action);
}
},
},
watch: {
@ -179,7 +177,7 @@ export default {
},
taskStatus(newval) {
this.$emit("update:taskStatus", newval);
}
},
},
mounted() {
@ -195,13 +193,14 @@ export default {
// Otherwise, this is just normal property access
return this[n];
}
const TypedArray = Reflect.getPrototypeOf(Int8Array);
for (const C of [Array, String, TypedArray]) {
Object.defineProperty(C.prototype, "from_index",
{ value: from_index,
writable: true,
enumerable: false,
configurable: true });
const TypedArray = Reflect.getPrototypeOf(Int8Array);
for (const C of [Array, String, TypedArray]) {
Object.defineProperty(C.prototype, "from_index", {
value: from_index,
writable: true,
enumerable: false,
configurable: true,
});
}
// Check for already running tasks
if (this.taskStarted != true) {
@ -234,10 +233,7 @@ export default {
if (task.status == "pending" || task.status == "running") {
this.taskStarted = true;
this.$emit("taskStarted");
this.startPolling(
task.id,
task.links.find(t => t.rel == "self").href
);
this.startPolling(task.id, task.links.find(t => t.rel == "self").href);
}
}
});
@ -250,7 +246,7 @@ export default {
() => {
this.startTask();
},
() => {}
() => {},
);
} else {
this.startTask();
@ -271,10 +267,7 @@ export default {
UIkit.modal(this.$refs.statusModal).show();
}
// Start the store polling TaskId for success
response = await this.startPolling(
response.data.id,
response.data.href
);
response = await this.startPolling(response.data.id, response.data.href);
if (response.status == "completed") {
this.$emit("response", response);
this.$emit("completed", response.output);
@ -312,43 +305,46 @@ export default {
var checkCondition = (resolve, reject) => {
// If the condition is met, we're done!
axios
.get(this.taskUrl, { baseURL: this.$store.getters.baseUri })
.then(response => {
var result = response.data.status;
this.taskStatus = result;
if ((result == "running") | (result == "pending")) {
// If the task is still running, we update progress/log,
// and schedule another poll
this.progress = response.data.progress;
this.log = response.data.log;
// Check again after timeout
setTimeout(checkCondition, interval, resolve, reject);
axios.get(this.taskUrl, { baseURL: this.$store.getters.baseUri }).then(response => {
var result = response.data.status;
this.taskStatus = result;
if ((result == "running") | (result == "pending")) {
// If the task is still running, we update progress/log,
// and schedule another poll
this.progress = response.data.progress;
this.log = response.data.log;
// Check again after timeout
setTimeout(checkCondition, interval, resolve, reject);
}
// If task ends with an error
else if (result == "error") {
// Pass the error string back with reject
if (!this.progress) this.progress = 1;
// Test whether the log is empty or the most recent message is not from an error
// If so, return a default message
if (
(response.data.log.length == 0) |
(response.data.log.from_index(-1).levelname != "ERROR")
) {
var message = "Unexpected error, please check the logs";
}
// If task ends with an error
else if (result == "error") {
// Pass the error string back with reject
if (!this.progress) this.progress = 1;
// Test whether the log is empty or the most recent message is not from an error
// If so, return a default message
if (response.data.log.length == 0 | response.data.log.from_index(-1).levelname != "ERROR") {
var message = "Unexpected error, please check the logs";
}
// As LabThings Actions add the message from any raised exception to the log, the
// last message in the log is the message from the Exception.
//If the Exception was raised with no message, use a default.
else {
message = response.data.log.from_index(-1).message||"Unexpected error, please check the logs";
}
// Raise an Error with the chosen message
reject(new Error(message));
}
// If task ends without reporting an error
// (NB this includes cancellation)
// As LabThings Actions add the message from any raised exception to the log, the
// last message in the log is the message from the Exception.
//If the Exception was raised with no message, use a default.
else {
resolve(response.data);
message =
response.data.log.from_index(-1).message ||
"Unexpected error, please check the logs";
}
});
// Raise an Error with the chosen message
reject(new Error(message));
}
// If task ends without reporting an error
// (NB this includes cancellation)
else {
resolve(response.data);
}
});
};
return new Promise(checkCondition);
@ -362,8 +358,8 @@ export default {
terminateTask: function() {
axios.delete(this.taskUrl, { baseURL: this.$store.getters.baseUri });
this.$root.$emit("modalClosed");
}
}
},
},
};
</script>

View file

@ -1,31 +1,24 @@
<template>
<div
class="log-wrapper"
@mouseenter="onMouseEnter"
@mouseleave="onMouseLeave"
>
<div
ref="logContainer"
class="log-container uk-margin-left uk-margin-right uk-margin"
>
<div v-if="log">
<div v-for="(item, index) in log" :key="`log_entry_${index}`">
{{ item.message }}
<div class="log-wrapper" @mouseenter="onMouseEnter" @mouseleave="onMouseLeave">
<div ref="logContainer" class="log-container uk-margin-left uk-margin-right uk-margin">
<div v-if="log">
<div v-for="(item, index) in log" :key="`log_entry_${index}`">
{{ item.message }}
</div>
</div>
</div>
<div v-if="taskStatus == 'error'" class="uk-alert uk-alert-danger">
The task failed due to an error. There may be more information in the log.
</div>
<div v-if="taskStatus == 'cancelled'" class="uk-alert uk-alert-warning">
The task was cancelled.
</div>
<div v-if="taskStatus == 'completed'" class="uk-alert uk-alert-success">
The task completed successfully.
<div v-if="taskStatus == 'error'" class="uk-alert uk-alert-danger">
The task failed due to an error. There may be more information in the log.
</div>
<div v-if="taskStatus == 'cancelled'" class="uk-alert uk-alert-warning">
The task was cancelled.
</div>
<div v-if="taskStatus == 'completed'" class="uk-alert uk-alert-success">
The task completed successfully.
</div>
<!-- Paused banner outside scroll container, positioned relative to wrapper -->
<div v-if="userIsHovering" class="paused-banner">
Auto-scroll paused
<div v-if="userIsHovering" class="paused-banner">
Auto-scroll paused
</div>
</div>
</div>
@ -38,12 +31,12 @@ export default {
props: {
taskStatus: {
type: String,
required: true
required: true,
},
log: {
type: Array,
required: true
}
required: true,
},
},
data() {
@ -58,7 +51,7 @@ export default {
},
taskStatus: function() {
this.scrollToBottom();
}
},
},
methods: {
@ -70,15 +63,15 @@ export default {
},
scrollToBottom() {
/*Scroll to bottom of log unless the user is hovering over the log.*/
/*Scroll to bottom of log unless the user is hovering over the log.*/
this.$nextTick(() => {
if (this.userIsHovering) return;
const viewer = this.$refs.logContainer;
viewer.scrollTop = viewer.scrollHeight;
});
}
}
},
},
};
</script>

View file

@ -13,12 +13,12 @@ export default {
progress: {
type: Number,
required: false,
default: null
default: null,
},
taskStatus: {
type: String,
required: true
}
required: true,
},
},
computed: {
@ -33,8 +33,8 @@ export default {
return true;
}
return false;
}
}
},
},
};
</script>
@ -81,8 +81,7 @@ export default {
left: 0;
bottom: 0;
will-change: left, right;
-webkit-animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395)
infinite;
-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;
}
@ -94,10 +93,8 @@ export default {
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: 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;
}

View file

@ -1,15 +1,18 @@
<template>
<a
class="uk-button"
:class="[isDisabled ? 'uk-button-disabled' : '', buttonPrimary ? 'uk-button-primary' : 'uk-button-default']"
:href="URL"
download
> {{ buttonLabel }}</a
>
<a
class="uk-button"
:class="[
isDisabled ? 'uk-button-disabled' : '',
buttonPrimary ? 'uk-button-primary' : 'uk-button-default',
]"
:href="URL"
download
>
{{ buttonLabel }}</a
>
</template>
<script>
export default {
name: "EndpointButton",
@ -17,7 +20,7 @@ export default {
buttonPrimary: {
type: Boolean,
required: false,
default: true
default: true,
},
URL: {
type: String,
@ -26,18 +29,18 @@ export default {
isDisabled: {
type: Boolean,
required: false,
default: false
default: false,
},
destinationName: {
type: String,
required: false,
default: null
default: null,
},
buttonLabel: {
type: String,
required: false,
default: "Download File"
}
default: "Download File",
},
},
};
</script>

View file

@ -51,8 +51,8 @@
<label v-if="dataType == 'number_object'" class="uk-form-label"
>{{ label }}
<div v-for="(val, key) in value" :key="key">
<label>{{internalLabels[key]}}</label>
<div class="input-and-buttons-container" >
<label>{{ internalLabels[key] }}</label>
<div class="input-and-buttons-container">
<input
v-model="internalValue[key]"
class="uk-form-small numeric-setting-line-input"
@ -89,24 +89,24 @@ export default {
name: "InputFromSchema",
components: {
syncPropertyButton
syncPropertyButton,
},
props: {
dataSchema: {
type: Object
type: Object,
},
value: {
type: null
type: null,
},
label: {
type: String,
default: ""
default: "",
},
animate: {
type: Boolean,
default: false
}
default: false,
},
},
data() {
@ -116,44 +116,21 @@ export default {
// (see resetInternalValue). If we do this here there is a chance we get errors
// as internalValue is still null when rendering starts.
internalValue: Array.isArray(this.value)
? [...this.value]
: typeof this.value === 'object'
? { ...this.value }
: this.value,
? [...this.value]
: typeof this.value === "object"
? { ...this.value }
: this.value,
// Is edited can't be computed as we mutate internalValue
isEdited: false,
animateUpdate: false,
};
},
mounted() {
if (this.value !== undefined) {
this.resetInternalValue();
}
},
watch: {
value() {
// Fire updateIsEdited on both value and internal value change,
// as change in value may not causse internalValue to change.
this.updateIsEdited();
this.resetInternalValue();
},
internalValue() {
this.updateIsEdited();
},
animate(updated) {
if (updated) {
this.animateUpdate = true;
}
}
},
computed: {
internalLabels: function() {
if (this.dataType == "number_object") {
let labels = {};
for (const key in this.internalValue){
labels[key] = this.dataSchema.properties[key].title
for (const key in this.internalValue) {
labels[key] = this.dataSchema.properties[key].title;
}
return labels;
}
@ -204,6 +181,29 @@ export default {
}
}
return "other";
},
},
watch: {
value() {
// Fire updateIsEdited on both value and internal value change,
// as change in value may not causse internalValue to change.
this.updateIsEdited();
this.resetInternalValue();
},
internalValue() {
this.updateIsEdited();
},
animate(updated) {
if (updated) {
this.animateUpdate = true;
}
},
},
mounted() {
if (this.value !== undefined) {
this.resetInternalValue();
}
},
@ -215,10 +215,10 @@ export default {
this.internalValue = JSON.parse(JSON.stringify(this.value));
},
requestUpdate: async function() {
this.$emit("requestUpdate")
this.$emit("requestUpdate");
},
sendValue: async function() {
this.$emit("sendValue", this.internalValue)
this.$emit("sendValue", this.internalValue);
},
checkboxUpdated: function() {
if (this.internalValue != this.$refs.checkbox.checked) {
@ -245,7 +245,7 @@ export default {
},
animationEnd: function() {
this.animateUpdate = false;
this.$emit('animationShown');
this.$emit("animationShown");
},
deepStringify: function(val) {
// Create a json string where all internal numbers are also JSON strings. This is
@ -256,16 +256,13 @@ export default {
if (Array.isArray(val)) {
return JSON.stringify(val.map(String));
}
if (val && typeof val === 'object') {
const normalized = Object.fromEntries(
Object.entries(val).map(([k, v]) => [k, String(v)])
);
if (val && typeof val === "object") {
const normalized = Object.fromEntries(Object.entries(val).map(([k, v]) => [k, String(v)]));
return JSON.stringify(normalized);
}
return JSON.stringify(String(val));
}
}
},
},
};
</script>
@ -288,8 +285,12 @@ export default {
background-color: #fff3cd;
}
@keyframes green-flash {
0% { background-color: #3fda63; }
100% { background-color: white; }
0% {
background-color: #3fda63;
}
100% {
background-color: white;
}
}
.flash {
animation: green-flash 0.7s ease;

View file

@ -18,58 +18,56 @@ export default {
name: "PropertyControl",
components: {
InputFromSchema
InputFromSchema,
},
props: {
label: {
type: String,
default: ""
default: "",
},
propertyName: {
type: String,
required: true
required: true,
},
thingName: {
type: String,
required: true
required: true,
},
readBack: {
type: Boolean,
required: false,
default: false
default: false,
},
readBackDelay: {
type: Number,
default: 1000,
required: false
}
required: false,
},
},
data() {
return {
value: undefined,
animate: false
animate: false,
};
},
computed: {
propertyDescription: function() {
try {
return this.thingDescription(this.thingName).properties[
this.propertyName
];
return this.thingDescription(this.thingName).properties[this.propertyName];
} catch (error) {
return undefined;
}
}
},
},
watch: {
propertyDescription: function() {
// Ensure we read the property once the URL is known
this.readProperty();
}
},
},
mounted: function() {
@ -83,21 +81,14 @@ export default {
methods: {
readProperty: async function() {
let data = await this.readThingProperty(
this.thingName,
this.propertyName
);
let data = await this.readThingProperty(this.thingName, this.propertyName);
this.value = data;
return data;
},
writeProperty: async function(requestedValue) {
try {
this.value=requestedValue;
await this.writeThingProperty(
this.thingName,
this.propertyName,
requestedValue
);
this.value = requestedValue;
await this.writeThingProperty(this.thingName, this.propertyName, requestedValue);
if (this.readBack) {
await new Promise(r => setTimeout(r, this.readBackDelay));
let newVal = await this.readProperty();
@ -106,7 +97,9 @@ export default {
} else {
this.animate = true;
await this.modalNotify(
`Set ${this.label} to ${formatValue(newVal)} (closest valid value to requested ${formatValue(requestedValue)}).`
`Set ${this.label} to ${formatValue(
newVal,
)} (closest valid value to requested ${formatValue(requestedValue)}).`,
);
}
} else {
@ -120,9 +113,9 @@ export default {
}
},
resetAnimate: function() {
this.animate = false;
}
}
this.animate = false;
},
},
};
</script>

View file

@ -22,15 +22,14 @@ export default {
name: "ServerSpecifiedActionButton",
components: {
ActionButton
ActionButton,
},
props: {
actionData: {
type: Object,
required: true,
}
},
},
methods: {
@ -38,8 +37,8 @@ export default {
if (this.actionData.notify_on_success) {
this.modalNotify(this.actionData.success_message);
}
}
}
},
},
};
</script>

View file

@ -17,15 +17,15 @@ export default {
name: "ServerSpecifiedPropertyControl",
components: {
PropertyControl
PropertyControl,
},
props: {
propertyData: {
type: Object,
required: true,
}
}
},
},
};
</script>

View file

@ -1,5 +1,5 @@
<template>
<a @click.prevent="$emit('click')" class="sync-button">
<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"
@ -11,7 +11,7 @@
<script>
export default {
name: "syncPropertyButton"
name: "SyncPropertyButton",
};
</script>
@ -24,14 +24,13 @@ export default {
cursor: pointer;
}
.material-symbols-outlined.sync-icon{
.material-symbols-outlined.sync-icon {
color: #888;
transition: transform 0.3s ease, color 0.3s ease;
}
.material-symbols-outlined.sync-icon:hover{
.material-symbols-outlined.sync-icon:hover {
transform: rotate(-90deg);
color: #c5247f;
}
</style>