From a3c0d43f67e2f01def1c0ccfa6d5d67bafff4927 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 20 Feb 2026 09:36:26 +0200 Subject: [PATCH] feat: add tour store for guided walkthrough state --- src/stores/tour.ts | 74 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 src/stores/tour.ts diff --git a/src/stores/tour.ts b/src/stores/tour.ts new file mode 100644 index 0000000..d357d4e --- /dev/null +++ b/src/stores/tour.ts @@ -0,0 +1,74 @@ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' + +export interface TourStep { + target: string + title: string + content: string + placement?: 'top' | 'bottom' | 'left' | 'right' +} + +export interface TourDefinition { + id: string + route: string + steps: TourStep[] +} + +export const useTourStore = defineStore('tour', () => { + const activeTourId = ref(null) + const currentStepIndex = ref(0) + const activeTour = ref(null) + + const isActive = computed(() => activeTourId.value !== null) + + const currentStep = computed(() => { + if (!activeTour.value) return null + return activeTour.value.steps[currentStepIndex.value] || null + }) + + const totalSteps = computed(() => activeTour.value?.steps.length || 0) + + const isFirstStep = computed(() => currentStepIndex.value === 0) + const isLastStep = computed(() => currentStepIndex.value >= totalSteps.value - 1) + + function start(tour: TourDefinition) { + activeTourId.value = tour.id + activeTour.value = tour + currentStepIndex.value = 0 + } + + function next() { + if (!isLastStep.value) { + currentStepIndex.value++ + } else { + stop() + } + } + + function back() { + if (!isFirstStep.value) { + currentStepIndex.value-- + } + } + + function stop() { + activeTourId.value = null + activeTour.value = null + currentStepIndex.value = 0 + } + + return { + activeTourId, + currentStepIndex, + activeTour, + isActive, + currentStep, + totalSteps, + isFirstStep, + isLastStep, + start, + next, + back, + stop, + } +})