openflexure-microscope-server/webapp/src/components/modalComponents/calibrationWizardComponents/calibrationWizardTask.vue
2026-06-30 20:10:14 +01:00

142 lines
3.5 KiB
Vue

<!-- The base component for a wizard task.
A task should be used for higher level groupings of individual steps in the wizard,
such as calibrating a Thing. Tasks can be divided into multiple steps.
## How to use this component:
*To make a task with lots of steps:*
Create a task component for a `Thing`, that wraps just this component and passes in
a list of steps. See `cameraCalibrationTask.vue` and `cameraStageMappingTask.vue`
as examples.
*To make a simple task with just one step:*
See `singleStepTask.vue`. This is what is used to display the welcome task.
## How not to use this component:
Do not import this directly into CalibrationWizard as the list of tasks will then
also need props to be passed with the list of step components and step props, and it
gets very confusing.
-->
<template>
<div>
<h3 v-if="title">{{ title }}</h3>
<component
v-bind="currentStep.props"
:is="currentStep.component"
v-if="currentStep"
:key="stepIndex"
@awaiting-user="handleAwaitingUser"
@prevent-navigation="handlePreventNavigation"
/>
<p class="uk-text-right">
<button
v-if="showBackButton"
class="uk-button uk-button-default"
:class="{ 'uk-button-disabled': preventNavigation, 'disabled-cursor': preventNavigation }"
type="button"
@click="previousStep"
>
Back
</button>
<button
class="uk-button uk-button-primary uk-margin-left"
:class="{ 'uk-button-disabled': preventNavigation, 'disabled-cursor': preventNavigation }"
type="button"
@click="nextStep"
>
{{ nextButtonText }}
</button>
</p>
</div>
</template>
<script>
export default {
name: "CalibrationWizardTask",
props: {
title: {
type: String,
default: null,
},
first: Boolean,
final: Boolean,
startOnLast: {
type: Boolean,
default: false,
},
steps: {
type: Array,
required: true,
},
},
emits: ["back", "next"],
data() {
return {
stepIndex: this.startOnLast ? this.steps.length - 1 : 0,
stepAwaitingUser: false,
preventNavigation: false,
};
},
computed: {
currentStep() {
return this.steps[this.stepIndex] || null;
},
showBackButton() {
return !this.first || this.stepIndex > 0;
},
nextButtonText() {
const isLastStep = this.final && this.stepIndex === this.steps.length - 1;
if (this.stepAwaitingUser) {
return isLastStep ? "Skip and Finish" : "Skip";
}
return isLastStep ? "Finish" : "Next";
},
},
methods: {
/*
* Move to the previous step in this task, or the previous task if first step.
*/
previousStep: function () {
if (this.preventNavigation) return;
this.stepAwaitingUser = false;
if (this.stepIndex > 0) {
this.stepIndex = this.stepIndex - 1;
} else {
this.$emit("back");
}
},
/*
* Move to the next step in this task, or the next task if final step.
*/
nextStep: function () {
if (this.preventNavigation) return;
this.stepAwaitingUser = false;
if (this.stepIndex < this.steps.length - 1) {
this.stepIndex = this.stepIndex + 1;
return true;
} else {
this.$emit("next");
}
},
handleAwaitingUser(isAwaiting) {
this.stepAwaitingUser = isAwaiting;
},
handlePreventNavigation(blocked) {
this.preventNavigation = blocked;
},
},
};
</script>
<style lang="less" scoped>
.disabled-cursor {
cursor: not-allowed !important ;
}
</style>