refactor: scroll to first error when submitting a form
This commit is contained in:
parent
505f60a8a1
commit
d34bd53275
5 changed files with 35 additions and 4 deletions
10
app/client/hooks/use-scroll-to-form-error.ts
Normal file
10
app/client/hooks/use-scroll-to-form-error.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { useCallback } from "react";
|
||||
|
||||
export function useScrollToFormError() {
|
||||
return useCallback(() => {
|
||||
setTimeout(() => {
|
||||
const firstError = document.querySelector("[data-slot='form-message']");
|
||||
firstError?.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
}, 50);
|
||||
}, []);
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import { arktypeResolver } from "@hookform/resolvers/arktype";
|
||||
import { useCallback, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useScrollToFormError } from "~/client/hooks/use-scroll-to-form-error";
|
||||
import { Form } from "~/client/components/ui/form";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/client/components/ui/card";
|
||||
import type { BackupSchedule, Volume } from "~/client/lib/types";
|
||||
|
|
@ -30,6 +31,8 @@ export const CreateScheduleForm = ({ initialValues, formId, onSubmit, volume }:
|
|||
defaultValues: backupScheduleToFormValues(initialValues),
|
||||
});
|
||||
|
||||
const scrollToFirstError = useScrollToFormError();
|
||||
|
||||
const handleSubmit = useCallback(
|
||||
(data: InternalFormValues) => {
|
||||
const {
|
||||
|
|
@ -100,7 +103,7 @@ export const CreateScheduleForm = ({ initialValues, formId, onSubmit, volume }:
|
|||
return (
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(handleSubmit)}
|
||||
onSubmit={form.handleSubmit(handleSubmit, scrollToFirstError)}
|
||||
className="grid gap-4 xl:grid-cols-[minmax(0,2.3fr)_minmax(320px,1fr)]"
|
||||
id={formId}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {
|
|||
import { Input } from "~/client/components/ui/input";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/client/components/ui/select";
|
||||
import { notificationConfigSchemaBase } from "~/schemas/notifications";
|
||||
import { useScrollToFormError } from "~/client/hooks/use-scroll-to-form-error";
|
||||
import {
|
||||
CustomForm,
|
||||
DiscordForm,
|
||||
|
|
@ -116,11 +117,16 @@ export const CreateNotificationForm = ({ onSubmit, mode = "create", initialValue
|
|||
});
|
||||
|
||||
const { watch } = form;
|
||||
const scrollToFirstError = useScrollToFormError();
|
||||
const watchedType = watch("type");
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form id={formId} onSubmit={form.handleSubmit(onSubmit)} className={cn("space-y-4", className)}>
|
||||
<form
|
||||
id={formId}
|
||||
onSubmit={form.handleSubmit(onSubmit, scrollToFirstError)}
|
||||
className={cn("space-y-4", className)}
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import { SecretInput } from "../../../components/ui/secret-input";
|
|||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../../../components/ui/select";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "../../../components/ui/tooltip";
|
||||
import { useSystemInfo } from "~/client/hooks/use-system-info";
|
||||
import { useScrollToFormError } from "~/client/hooks/use-scroll-to-form-error";
|
||||
import { COMPRESSION_MODES, repositoryConfigSchemaBase } from "~/schemas/restic";
|
||||
import { Checkbox } from "../../../components/ui/checkbox";
|
||||
import {
|
||||
|
|
@ -96,10 +97,15 @@ export const CreateRepositoryForm = ({
|
|||
const [passwordMode, setPasswordMode] = useState<"default" | "custom">("default");
|
||||
|
||||
const { capabilities } = useSystemInfo();
|
||||
const scrollToFirstError = useScrollToFormError();
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form id={formId} onSubmit={form.handleSubmit(onSubmit)} className={cn("space-y-4", className)}>
|
||||
<form
|
||||
id={formId}
|
||||
onSubmit={form.handleSubmit(onSubmit, scrollToFirstError)}
|
||||
className={cn("space-y-4", className)}
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import { volumeConfigSchemaBase } from "~/schemas/volumes";
|
|||
import { testConnectionMutation } from "../../../api-client/@tanstack/react-query.gen";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "../../../components/ui/tooltip";
|
||||
import { useSystemInfo } from "~/client/hooks/use-system-info";
|
||||
import { useScrollToFormError } from "~/client/hooks/use-scroll-to-form-error";
|
||||
import { DirectoryForm, NFSForm, SMBForm, WebDAVForm, RcloneForm, SFTPForm } from "./volume-forms";
|
||||
|
||||
export const formSchema = type({
|
||||
|
|
@ -65,6 +66,7 @@ export const CreateVolumeForm = ({ onSubmit, mode = "create", initialValues, for
|
|||
const { getValues, watch } = form;
|
||||
|
||||
const { capabilities } = useSystemInfo();
|
||||
const scrollToFirstError = useScrollToFormError();
|
||||
const watchedBackend = watch("backend");
|
||||
|
||||
const [testMessage, setTestMessage] = useState<{ success: boolean; message: string } | null>(null);
|
||||
|
|
@ -102,7 +104,11 @@ export const CreateVolumeForm = ({ onSubmit, mode = "create", initialValues, for
|
|||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form id={formId} onSubmit={form.handleSubmit(onSubmit)} className={cn("space-y-4", className)}>
|
||||
<form
|
||||
id={formId}
|
||||
onSubmit={form.handleSubmit(onSubmit, scrollToFirstError)}
|
||||
className={cn("space-y-4", className)}
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
|
|
|
|||
Loading…
Reference in a new issue