commit
This commit is contained in:
100
src/hooks/use-stepper.tsx
Normal file
100
src/hooks/use-stepper.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import type { AnyFormApi } from '@tanstack/react-form';
|
||||
import { useCallback, useState } from 'react';
|
||||
import type { ZodTypeAny } from 'zod';
|
||||
|
||||
/**
|
||||
* Options for handling cancel/back actions
|
||||
*/
|
||||
type HandleCancelOrBackOpts = {
|
||||
onBack?: VoidFunction;
|
||||
onCancel?: VoidFunction;
|
||||
};
|
||||
|
||||
/**
|
||||
* State of the current step
|
||||
*/
|
||||
type StepState = {
|
||||
value: number;
|
||||
count: number;
|
||||
goToNextStep: () => void;
|
||||
goToPrevStep: () => void;
|
||||
isCompleted: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook for managing multi-step form navigation and validation
|
||||
*
|
||||
* @param schemas - Array of Zod schemas for each step
|
||||
* @returns Object with stepper state and methods
|
||||
*/
|
||||
export function useFormStepper(schemas: ZodTypeAny[]) {
|
||||
const stepCount = schemas.length;
|
||||
const [currentStep, setCurrentStep] = useState(1); // Start from 1
|
||||
|
||||
const goToNextStep = useCallback(() => {
|
||||
setCurrentStep((prev) => Math.min(prev + 1, stepCount));
|
||||
}, [stepCount]);
|
||||
|
||||
const goToPrevStep = useCallback(() => {
|
||||
setCurrentStep((prev) => Math.max(prev - 1, 1));
|
||||
}, []);
|
||||
|
||||
const step: StepState = {
|
||||
value: currentStep,
|
||||
count: stepCount,
|
||||
goToNextStep,
|
||||
goToPrevStep,
|
||||
isCompleted: currentStep === stepCount
|
||||
};
|
||||
|
||||
const currentValidator = schemas[currentStep - 1]; // Convert to 0-based for array access
|
||||
const isFirstStep = currentStep === 1;
|
||||
|
||||
const triggerFormGroup = async (form: AnyFormApi) => {
|
||||
const result = currentValidator.safeParse(form.state.values);
|
||||
if (!result.success) {
|
||||
await form.handleSubmit({ step: String(currentStep) });
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const handleNextStepOrSubmit = async (form: AnyFormApi) => {
|
||||
const result = await triggerFormGroup(form);
|
||||
if (!result.success) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentStep < stepCount) {
|
||||
goToNextStep();
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentStep === stepCount) {
|
||||
form.handleSubmit();
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancelOrBack = (opts?: HandleCancelOrBackOpts) => {
|
||||
if (isFirstStep || step.isCompleted) {
|
||||
opts?.onCancel?.();
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentStep > 1) {
|
||||
opts?.onBack?.();
|
||||
goToPrevStep();
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
step, // Current step state
|
||||
currentStep, // Current step number (1-based)
|
||||
isFirstStep, // Whether current step is the first step
|
||||
currentValidator, // Zod schema for current step
|
||||
triggerFormGroup, // Validate current step fields
|
||||
handleNextStepOrSubmit, // Handle next/submit action
|
||||
handleCancelOrBack // Handle back/cancel action
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user