Created
August 22, 2024 19:22
-
-
Save allipiopereira/96c6a9f032d4c3567ace0ae90c71b698 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use client"; | |
import { defineStepper } from "@stepperize/react"; | |
import { zodResolver } from "@hookform/resolvers/zod"; | |
import { useForm, FormProvider, useFormContext, SubmitHandler } from "react-hook-form"; | |
import * as z from "zod"; | |
import React, { createContext, useContext, useState, ReactNode } from "react"; | |
import { | |
Form, | |
FormControl, | |
FormDescription, | |
FormField, | |
FormItem, | |
FormLabel, | |
FormMessage, | |
} from "@/components/ui/form"; | |
import { Input } from "@/components/ui/input"; | |
export const Stepper = defineStepper( | |
{ id: "general-info", title: "Informações Gerais", description: "Preencha as informações básicas do aluno" }, | |
{ id: "address", title: "Endereço", description: "Preencha o endereço do aluno" }, | |
{ id: "review", title: "Revisão", description: "Revise todas as informações do aluno" }, | |
); | |
const generalInfoFormSchema = z.object({ | |
username: z.string().min(2, { | |
message: "Username must be at least 2 characters.", | |
}), | |
}); | |
const addressFormSchema = z.object({ | |
address: z.string().min(5, { | |
message: "Address must be at least 5 characters.", | |
}), | |
}); | |
type GeneralInfoFormData = z.infer<typeof generalInfoFormSchema>; | |
type AddressFormData = z.infer<typeof addressFormSchema>; | |
type FormData = GeneralInfoFormData & AddressFormData; | |
interface FormDataContextType { | |
formData: FormData; | |
setFormData: React.Dispatch<React.SetStateAction<FormData>>; | |
} | |
const FormDataContext = createContext<FormDataContextType | null>(null); | |
export function GeneralInfoForm() { | |
const stepper = Stepper.useStepper(); | |
const context = useContext(FormDataContext); | |
if (!context) throw new Error("FormDataContext not found"); | |
const { formData, setFormData } = context; | |
const formMethods = useForm<GeneralInfoFormData>({ | |
resolver: zodResolver(generalInfoFormSchema), | |
defaultValues: { | |
username: formData.username, | |
}, | |
}); | |
const onSubmit: SubmitHandler<GeneralInfoFormData> = (data) => { | |
setFormData((prevData) => ({ ...prevData, ...data })); | |
stepper.next(); | |
}; | |
return ( | |
<FormProvider {...formMethods}> | |
<Form {...formMethods}> | |
<form id={`form-${stepper.current.id}`} onSubmit={formMethods.handleSubmit(onSubmit)} className="space-y-6"> | |
<FormField | |
control={formMethods.control} | |
name="username" | |
render={({ field }) => ( | |
<FormItem> | |
<FormLabel>Username</FormLabel> | |
<FormControl> | |
<Input placeholder="shadcn" {...field} /> | |
</FormControl> | |
<FormDescription> | |
This is your public display name. | |
</FormDescription> | |
<FormMessage /> | |
</FormItem> | |
)} | |
/> | |
</form> | |
</Form> | |
</FormProvider> | |
); | |
} | |
export function AddressForm() { | |
const stepper = Stepper.useStepper(); | |
const context = useContext(FormDataContext); | |
if (!context) throw new Error("FormDataContext not found"); | |
const { formData, setFormData } = context; | |
const formMethods = useForm<AddressFormData>({ | |
resolver: zodResolver(addressFormSchema), | |
defaultValues: { | |
address: formData.address, | |
}, | |
}); | |
const onSubmit: SubmitHandler<AddressFormData> = (data) => { | |
setFormData((prevData) => ({ ...prevData, ...data })); | |
stepper.next(); | |
}; | |
return ( | |
<FormProvider {...formMethods}> | |
<Form {...formMethods}> | |
<form id={`form-${stepper.current.id}`} onSubmit={formMethods.handleSubmit(onSubmit)} className="space-y-6"> | |
<FormField | |
control={formMethods.control} | |
name="address" | |
render={({ field }) => ( | |
<FormItem> | |
<FormLabel>Address</FormLabel> | |
<FormControl> | |
<Input placeholder="123 Main St" {...field} /> | |
</FormControl> | |
<FormDescription> | |
This is your address. | |
</FormDescription> | |
<FormMessage /> | |
</FormItem> | |
)} | |
/> | |
</form> | |
</Form> | |
</FormProvider> | |
); | |
} | |
export function ReviewForm() { | |
const stepper = Stepper.useStepper(); | |
const context = useContext(FormDataContext); | |
if (!context) throw new Error("FormDataContext not found"); | |
const { formData } = context; | |
async function createUser() { | |
console.log("Creating user with data:", formData); | |
} | |
return ( | |
<div> | |
<h1>Review</h1> | |
<div className="flex">Username: {formData.username}</div> | |
<div className="flex">Address: {formData.address}</div> | |
<button onClick={createUser}>Create User</button> | |
</div> | |
); | |
} | |
export const MySteps = () => { | |
const stepper = Stepper.useStepper(); | |
return ( | |
<> | |
{stepper.when("general-info", (step) => ( | |
<GeneralInfoForm /> | |
))} | |
{stepper.when("address", (step) => ( | |
<AddressForm /> | |
))} | |
{stepper.when("review", (step) => ( | |
<ReviewForm /> | |
))} | |
</> | |
); | |
}; | |
export const MyActions = () => { | |
const stepper = Stepper.useStepper(); | |
return ( | |
<div className="flex items-center gap-2"> | |
<button onClick={stepper.prev} disabled={stepper.isFirst}> | |
Previous | |
</button> | |
<button type="submit" form={`form-${stepper.current.id}`} disabled={stepper.isLast}> | |
{stepper.when( | |
"review", | |
() => "Finish", | |
() => "Next" | |
)} | |
</button> | |
</div> | |
); | |
}; | |
export const MyScopedStepper = () => { | |
const [formData, setFormData] = useState<FormData>({ | |
username: "", | |
address: "", | |
}); | |
return ( | |
<FormDataContext.Provider value={{ formData, setFormData }}> | |
<Stepper.Scoped> | |
<MySteps /> | |
<MyActions /> | |
</Stepper.Scoped> | |
</FormDataContext.Provider> | |
); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment