import { Button } from '@components/common/ui/Button.js'; import { _ } from '@evershop/evershop/lib/locale/translate/_'; import React, { useEffect, useState } from 'react'; import { useForm, FormProvider, UseFormProps, FieldValues, SubmitHandler, UseFormReturn } from 'react-hook-form'; import { toast } from 'react-toastify'; interface FormProps extends Omit< React.FormHTMLAttributes, 'onSubmit' | 'onError' > { form?: UseFormReturn; action?: string; method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; formOptions?: UseFormProps; onSubmit?: SubmitHandler; onSuccess?: (response: any, data: T) => void; onError?: (error: string, data: T) => void; successMessage?: string; errorMessage?: string; submitBtn?: boolean; submitBtnText?: string; loading?: boolean; children: React.ReactNode; } export function Form({ form: externalForm, action, method = 'POST', formOptions, onSubmit, onSuccess, onError, successMessage = _('Saved successfully!'), errorMessage = _('Something went wrong! Please try again.'), submitBtn = true, submitBtnText = _('Save'), loading = false, children, className, noValidate = true, ...props }: FormProps) { const theForm = externalForm || useForm({ shouldUnregister: true, shouldFocusError: false, ...formOptions }); const { handleSubmit, formState: { isSubmitting } } = theForm; const defaultSubmit: SubmitHandler = async (data) => { if (!action) { return; } try { const response = await fetch(action, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await response.json(); if (result.error) { if (onError) { onError(result.error.message, data); } else { toast.error(result.error.message || errorMessage); } } else if (onSuccess) { onSuccess(result, data); } else { toast.success(successMessage); } } catch (error) { if (onError) { onError( errorMessage || (error instanceof Error ? error.message : ''), data ); } else { toast.error( errorMessage || (error instanceof Error ? error.message : '') ); } } }; const [canFocus, setCanFocus] = useState(true); const onValidationError = () => { setCanFocus(true); }; useEffect(() => { if (theForm.formState.errors && canFocus) { const elements = Array.from( document.querySelectorAll('[aria-invalid="true"]') ) as HTMLElement[]; elements.sort( (a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top ); if (elements.length > 0) { const errorElement = elements[0]; errorElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); errorElement.focus({ preventScroll: true }); setCanFocus(false); } } }, [theForm.formState, canFocus]); const handleFormSubmit = onSubmit || defaultSubmit; return (
{children}
{submitBtn && (
)}
); } export { useFormContext } from 'react-hook-form'; export { Controller } from 'react-hook-form'; export type { Control, FieldPath, FieldValues } from 'react-hook-form';