useFormWizard Composable
Multi-step form wizard management with validation and step navigation
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!'));