VUE

useFormWizard Composable

Multi-step form wizard management with validation and step navigation

Vue3ComposablesFormsWizardValidation

Code

import { ref, computed, type Ref } from 'vue';

type StepValidator = () => boolean | Promise<boolean>;

interface WizardStep {
  id: string | number;
  label?: string;
  validator?: StepValidator;
  isDisabled?: boolean;
}

export function useFormWizard(initialSteps: WizardStep[], initialStep = 0) {
  const steps = ref<WizardStep[]>(initialSteps);
  const currentStepIndex = ref(Math.max(0, Math.min(initialStep, steps.value.length - 1)));
  const isSubmitting = ref(false);
  const stepErrors = ref<Record<string | number, string | null>>({});

  // Current step
  const currentStep = computed(() => {
    return steps.value[currentStepIndex.value];
  });

  // Navigation state
  const canGoNext = computed(() => {
    return currentStepIndex.value < steps.value.length - 1 && 
           !steps.value[currentStepIndex.value + 1]?.isDisabled;
  });

  const canGoPrev = computed(() => {
    return currentStepIndex.value > 0 && 
           !steps.value[currentStepIndex.value - 1]?.isDisabled;
  });

  const isFirstStep = computed(() => currentStepIndex.value === 0);
  const isLastStep = computed(() => currentStepIndex.value === steps.value.length - 1);

  // Validate current step
  const validateCurrentStep = __TOKEN_42__ (): Promise<boolean> => {
    const step = currentStep.value;
    __TOKEN_44__ (!step.validator) return true;

    try {
      const isValid = await step.validator();
      stepErrors.value[step.id] = isValid ? null : 'Step validation failed';
      return isValid;
    } __TOKEN_50__ (error) {
      stepErrors.value[step.id] = error instanceof Error ? error.message : 'Validation error';
      return false;
    }
  };

  // Navigate to step
  const goToStep = __TOKEN_54__ (index: number): Promise<boolean> => {
    __TOKEN_55__ (
      index < 0 || 
      index >= steps.value.length || 
      steps.value[index]?.isDisabled ||
      index === currentStepIndex.value
    ) {
      return false;
    }

    // Validate current step if moving forward
    __TOKEN_57__ (index > currentStepIndex.value) {
      const isValid = await validateCurrentStep();
      __TOKEN_60__ (!isValid) return false;
    }

    currentStepIndex.value = index;
    return true;
  };

  // Next step
  const goToNextStep = __TOKEN_64__ (): Promise<boolean> => {
    __TOKEN_65__ (!canGoNext.value) return false;
    return goToStep(currentStepIndex.value + 1);
  };

  // Previous step
  const goToPrevStep = __TOKEN_69__ (): Promise<boolean> => {
    __TOKEN_70__ (!canGoPrev.value) return false;
    return goToStep(currentStepIndex.value - 1);
  };

  // Reset wizard
  const reset = (newSteps?: WizardStep[]) => {
    __TOKEN_74__ (newSteps) steps.value = newSteps;
    currentStepIndex.value = 0;
    stepErrors.value = {};
    isSubmitting.value = false;
  };

  // Submit wizard (validate all steps)
  const submit = __TOKEN_76__ (onSubmit: () => void | Promise<void>): Promise<boolean> => {
    __TOKEN_77__ (isSubmitting.value) return false;

    isSubmitting.value = true;
    try {
      // Validate all steps
      __TOKEN_80__ (let i = 0; i < steps.value.length; i++) {
        currentStepIndex.value = i;
        const isValid = await validateCurrentStep();
        __TOKEN_84__ (!isValid) {
          currentStepIndex.value = i;
          return false;
        }
      }

      // Run submit handler
      await onSubmit();
      return true;
    } finally {
      isSubmitting.value = false;
    }
  };

  return {
    steps,
    currentStep,
    currentStepIndex,
    canGoNext,
    canGoPrev,
    isFirstStep,
    isLastStep,
    isSubmitting,
    stepErrors,
    validateCurrentStep,
    goToStep,
    goToNextStep,
    goToPrevStep,
    reset,
    submit
  };
}

// Usage example
// const steps = [
//   { id: 1, label: 'Personal Info', validator: validateStep1 },
//   { id: 2, label: 'Billing Info', validator: validateStep2 },
//   { id: 3, label: 'Review', validator: validateStep3 }
// ];
// const { currentStep, goToNextStep, submit } = useFormWizard(steps);
// submit(() => console.log('Form submitted!'));