Skip to content

Визарды ​

useWizard(steps, options?) — composable для многошаговых форм, построенный поверх defineMachine. Машина wizard'а генерируется автоматически из массива шагов.

ts
function useWizard<TContext>(
  steps: WizardStep<TContext>[],
  options?: WizardOptions,
): WizardInstance<TContext>

Шаги визарда ​

Поля WizardStep:

id ​

string. Уникальный идентификатор шага (внутри становится именем состояния).

label ​

string?. Отображаемая метка.

component ​

Component?. Vue-компонент, рендерящийся для этого шага.

canProceed ​

(ctx) => boolean | Promise<boolean>. Условие для next() и goTo() вперёд; может быть асинхронным. Получает реальный, живой контекст wizard'а — то, что успели слить в него onEnter/onLeave.

onEnter ​

(ctx) => void | Partial<TContext>. Вызывается, когда wizard входит в этот шаг. Верните частичный объект, чтобы слить его в контекст — например, задать значение по умолчанию.

onLeave ​

(ctx) => void | Partial<TContext>. Вызывается, когда wizard покидает этот шаг. Верните частичный объект, чтобы слить его в контекст — обычное место для сохранения данных, собранных на шаге (например, полей формы), до того, как canProceed на следующем шаге их прочитает.

Опции визарда ​

Поля WizardOptions:

id ​

string? · по умолчанию: автогенерируется. Id машины, регистрируемый в MachineStore (когда установлен VueMachinePlugin) и отображаемый в DevTools. Задайте его явно, если нужен стабильный, предсказуемый id (например, чтобы найти wizard через useMachineStore().get(id)); иначе каждый useWizard() получает свой уникальный id автоматически.

initialStep ​

number · по умолчанию: 0. Индекс начального шага.

allowSkip ​

boolean · по умолчанию: false. Пропускать canProceed при goTo() вперёд.

circular ​

boolean · по умолчанию: false. next() возвращается от последнего шага к первому.

Возвращаемое значение ​

currentStep ​

Ref<WizardStep>. Объект текущего активного шага.

currentIndex ​

ComputedRef<number>. Индекс текущего шага (от нуля).

totalSteps ​

number. Общее число шагов.

progress ​

ComputedRef<number>. От 0 до 1 на основе текущего индекса.

isFirst ​

ComputedRef<boolean>. true на первом шаге.

isLast ​

ComputedRef<boolean>. true на последнем шаге.

history ​

Ref<string[]>. ID посещённых шагов.

context ​

Readonly<Ref<TContext>>. Накопленный контекст — то, что успели слить в него onEnter/onLeave.

next() ​

Promise<boolean>. Продвигает вперёд; сначала вызывает canProceed; возвращает false, если заблокировано.

prev() ​

void. Назад (без guard).

goTo(id) ​

Promise<boolean>. Переход к шагу по id; учитывает canProceed, если не allowSkip.

reset() ​

void. Возврат к начальному шагу.

Пример ​

vue
<script setup lang="ts">
import { useWizard } from '@macrulez/vue-state-machine'
import type { WizardStep } from '@macrulez/vue-state-machine'
import StepInfo from './StepInfo.vue'
import StepAddress from './StepAddress.vue'
import StepPayment from './StepPayment.vue'

type CheckoutCtx = {
  name: string
  email: string
  address: string
}

const steps: WizardStep<CheckoutCtx>[] = [
  {
    id: 'info',
    label: 'Your info',
    component: StepInfo,
    // Заполняется собственным onLeave шага StepAddress ниже, ещё до того как
    // этот код выполнится в canProceed *следующего* шага — см. "context" в
    // возвращаемом значении выше.
    canProceed: (ctx) => !!ctx.name && !!ctx.email,
  },
  {
    id: 'address',
    label: 'Delivery',
    component: StepAddress,
    canProceed: (ctx) => !!ctx.address,
    // Верните частичный объект, чтобы слить собранные данные формы в контекст —
    // например, прочитав их из ref, который StepAddress обновляет через v-model,
    // или из выпущенного им emit.
    onLeave: (): Partial<CheckoutCtx> => ({ address: addressFieldRef.value }),
  },
  {
    id: 'payment',
    label: 'Payment',
    component: StepPayment,
    onEnter: () => trackEvent('payment_step_entered'),
  },
]

const { currentStep, context, progress, isFirst, isLast, next, prev } = useWizard(steps)
</script>

<template>
  <div>
    <progress :value="progress" max="1" />

    <component :is="currentStep.component" :context="context" />

    <nav>
      <button :disabled="isFirst" @click="prev">Back</button>
      <button v-if="!isLast" @click="next">Next</button>
      <button v-else @click="submit">Place order</button>
    </nav>
  </div>
</template>

Правила для canProceed ​

  • Может возвращать boolean или Promise<boolean>
  • Если возвращает false, next() / goTo() вперёд возвращают false, и wizard остаётся на текущем шаге
  • Если выбрасывает исключение, результат тот же — возвращается false, ошибка логируется в console.error в dev-режиме
  • prev() и goTo() назад никогда не проверяют canProceed
  • allowSkip: true отключает canProceed только для goTo(); next() проверяет его всегда