Skip to content

Commit 478863c

Browse files
committed
feat: generalize form submit button and error display
1 parent 9967505 commit 478863c

File tree

5 files changed

+95
-40
lines changed

5 files changed

+95
-40
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Callout } from '@blueprintjs/core'
2+
3+
import { FieldError, FieldErrors, FieldValues } from 'react-hook-form'
4+
5+
interface FormErrorProps<T extends FieldValues> {
6+
errors: FieldErrors<T>
7+
}
8+
9+
export const FormError = <T extends FieldValues>({
10+
errors,
11+
}: FormErrorProps<T>) => {
12+
const errorsArray = Object.values(errors) as FieldError[]
13+
14+
if (errorsArray.length === 0) {
15+
return null
16+
}
17+
18+
return (
19+
<Callout intent="danger" className="mt-2" title="发生错误…">
20+
<ol className="list-decimal list-inside">
21+
{errorsArray.map((error, i) => (
22+
<li key={i}>{error?.message || '未知错误'}</li>
23+
))}
24+
</ol>
25+
</Callout>
26+
)
27+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { Button, IconName } from '@blueprintjs/core'
2+
3+
import { useEffect, useRef, useState } from 'react'
4+
import { Control, FieldValues, useFormState } from 'react-hook-form'
5+
6+
interface FormSubmitButtonProps<T extends FieldValues> {
7+
className?: string
8+
control: Control<T>
9+
icon?: IconName
10+
children?: React.ReactNode
11+
}
12+
13+
export const FormSubmitButton = <T extends FieldValues>({
14+
className,
15+
control,
16+
icon,
17+
children,
18+
}: FormSubmitButtonProps<T>) => {
19+
const { isSubmitSuccessful } = useFormState({ control })
20+
21+
const [deferredSuccessful, setDeferredSuccessful] = useState(false)
22+
const resetTimer = useRef(-1)
23+
24+
useEffect(() => {
25+
if (isSubmitSuccessful) {
26+
setDeferredSuccessful(true)
27+
28+
resetTimer.current = window.setTimeout(() => {
29+
setDeferredSuccessful(false)
30+
}, 1000)
31+
} else {
32+
setDeferredSuccessful(false)
33+
}
34+
35+
return () => {
36+
clearTimeout(resetTimer.current)
37+
}
38+
}, [isSubmitSuccessful])
39+
40+
return (
41+
<Button
42+
className={className}
43+
intent={deferredSuccessful ? 'success' : 'primary'}
44+
type="submit"
45+
icon={deferredSuccessful ? 'tick' : icon}
46+
>
47+
{children}
48+
</Button>
49+
)
50+
}

src/components/editor/action/EditorActionAdd.tsx

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import {
1414
import { CardTitle } from 'components/CardTitle'
1515
import { FormField, FormField2 } from 'components/FormField'
1616
import { EditorResetButton } from 'components/editor/EditorResetButton'
17+
import { FormError } from 'components/editor/FormError'
18+
import { FormSubmitButton } from 'components/editor/FormSubmitButton'
1719
import { EditorActionDocColor } from 'components/editor/action/EditorActionDocColor'
1820
import {
1921
EditorActionExecPredicateCooling,
@@ -182,8 +184,6 @@ export const EditorActionAdd = ({
182184
}
183185
})
184186

185-
const globalError = (errors as FieldErrors<{ global: void }>).global?.message
186-
187187
return (
188188
<form onSubmit={onSubmit}>
189189
<Card className="mb-2 pb-8 pt-4 overflow-auto">
@@ -395,9 +395,9 @@ export const EditorActionAdd = ({
395395
</EditorActionModule>
396396

397397
<div className="mt-4 flex">
398-
<Button intent="primary" type="submit" icon={isNew ? 'add' : 'edit'}>
398+
<FormSubmitButton control={control} icon={isNew ? 'add' : 'edit'}>
399399
{isNew ? '添加' : '保存'}
400-
</Button>
400+
</FormSubmitButton>
401401

402402
{!isNew && (
403403
<Button icon="cross" className="ml-2" onClick={onCancel}>
@@ -406,11 +406,7 @@ export const EditorActionAdd = ({
406406
)}
407407
</div>
408408

409-
{globalError && (
410-
<Callout intent="danger" className="mt-2">
411-
{globalError}
412-
</Callout>
413-
)}
409+
<FormError errors={errors} />
414410
</Card>
415411
</form>
416412
)

src/components/editor/operator/EditorPerformerGroup.tsx

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
import { Button, Callout, InputGroup } from '@blueprintjs/core'
22

33
import { useEffect } from 'react'
4-
import {
5-
FieldErrors,
6-
SubmitHandler,
7-
UseFormSetError,
8-
useForm,
9-
} from 'react-hook-form'
4+
import { SubmitHandler, UseFormSetError, useForm } from 'react-hook-form'
105

116
import { CardTitle } from 'components/CardTitle'
127
import { FormField } from 'components/FormField'
138
import { EditorResetButton } from 'components/editor/EditorResetButton'
9+
import { FormError } from 'components/editor/FormError'
10+
import { FormSubmitButton } from 'components/editor/FormSubmitButton'
1411
import type { CopilotDocV1 } from 'models/copilot.schema'
1512

1613
import { FactItem } from '../../FactItem'
@@ -57,8 +54,6 @@ export const EditorPerformerGroup = ({
5754
}
5855
}
5956

60-
const globalError = (errors as FieldErrors<{ global: void }>).global?.message
61-
6257
return (
6358
<form onSubmit={handleSubmit(onSubmit)}>
6459
<div className="flex items-center mb-4">
@@ -96,9 +91,9 @@ export const EditorPerformerGroup = ({
9691
/>
9792

9893
<div className="flex">
99-
<Button intent="primary" type="submit" icon={isNew ? 'add' : 'edit'}>
94+
<FormSubmitButton control={control} icon={isNew ? 'add' : 'edit'}>
10095
{isNew ? '添加' : '保存'}
101-
</Button>
96+
</FormSubmitButton>
10297

10398
{!isNew && (
10499
<Button icon="cross" className="ml-2" onClick={onCancel}>
@@ -107,11 +102,7 @@ export const EditorPerformerGroup = ({
107102
)}
108103
</div>
109104

110-
{globalError && (
111-
<Callout intent="danger" className="mt-2">
112-
{globalError}
113-
</Callout>
114-
)}
105+
<FormError errors={errors} />
115106
</form>
116107
)
117108
}

src/components/editor/operator/EditorPerformerOperator.tsx

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
1-
import { Button, Callout } from '@blueprintjs/core'
1+
import { Button } from '@blueprintjs/core'
22

33
import { useEffect } from 'react'
4-
import {
5-
FieldErrors,
6-
SubmitHandler,
7-
UseFormSetError,
8-
useForm,
9-
} from 'react-hook-form'
4+
import { SubmitHandler, UseFormSetError, useForm } from 'react-hook-form'
105

116
import { CardTitle } from 'components/CardTitle'
127
import { EditorResetButton } from 'components/editor/EditorResetButton'
8+
import { FormError } from 'components/editor/FormError'
9+
import { FormSubmitButton } from 'components/editor/FormSubmitButton'
1310
import { CopilotDocV1 } from 'models/copilot.schema'
1411

1512
import { FormField2 } from '../../FormField'
@@ -94,8 +91,6 @@ export const EditorPerformerOperator = ({
9491
}
9592
}
9693

97-
const globalError = (errors as FieldErrors<{ global: void }>).global?.message
98-
9994
return (
10095
<form onSubmit={handleSubmit(onSubmit)}>
10196
<div className="flex items-center mb-4">
@@ -162,9 +157,9 @@ export const EditorPerformerOperator = ({
162157
</div>
163158

164159
<div className="flex">
165-
<Button intent="primary" type="submit" icon={isNew ? 'add' : 'edit'}>
160+
<FormSubmitButton control={control} icon={isNew ? 'add' : 'edit'}>
166161
{isNew ? '添加' : '保存'}
167-
</Button>
162+
</FormSubmitButton>
168163

169164
{!isNew && (
170165
<Button icon="cross" className="ml-2" onClick={onCancel}>
@@ -173,11 +168,7 @@ export const EditorPerformerOperator = ({
173168
)}
174169
</div>
175170

176-
{globalError && (
177-
<Callout intent="danger" className="mt-2">
178-
{globalError}
179-
</Callout>
180-
)}
171+
<FormError errors={errors} />
181172
</form>
182173
)
183174
}

0 commit comments

Comments
 (0)