make Form less dependent
Massimo Melina committed
Mar 20, 2022 at 11:42 UTC
991e6654e709e0865f91e5e6ca1e501cb1453f25
1 file changed
+24
-12
admin/src/Form.ts
+24
-12
@@ -1,6 +1,5 @@
1
// This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
-
3
-import { createElement as h, FC, Fragment, isValidElement, ReactElement, ReactNode, useEffect, useState } from 'react'
2
+import { createElement as h, FC, Fragment, isValidElement, ReactElement, ReactNode, useEffect, useState, useRef, Ref } from 'react'
3
import {
4
Box, Button,
5
FormControl,
@@ -12,7 +11,6 @@ import {
11
Switch,
12
TextField
13
} from '@mui/material'
15
-import { Dict, useStateMounted } from './misc'
14
import { Save } from '@mui/icons-material'
15
import { LoadingButton } from '@mui/lab'
16
import _ from 'lodash'
@@ -20,7 +18,7 @@ import _ from 'lodash'
18
interface FieldDescriptor {
19
k:string
20
comp?: any
23
- label?: string | ReactElement
21
+ label?: ReactNode
22
validate?: (v: any, extra:any) => string | boolean
23
[extraProp:string]:any
24
}
@@ -28,9 +26,11 @@ interface FieldDescriptor {
26
// it seems necessary to cast (Multi)SelectField sometimes
27
export type Field<T> = FC<FieldProps<T>>
28
31
-interface FormProps {
29
+type Dict<T=any> = Record<string,T>
30
+
31
+export interface FormProps {
32
fields: (FieldDescriptor | ReactElement | null | undefined | false)[]
33
- defaults?: (f:FieldDescriptor) => Dict | any
33
+ defaults?: (f:FieldDescriptor) => any
34
values: Dict
35
set: (v: any, fieldK: string) => void
36
save: Partial<Parameters<typeof Button>[0]> | (()=>any)
@@ -38,11 +38,19 @@ interface FormProps {
38
addToBar?: ReactNode[]
39
barSx?: Dict
40
onError?: (err: any) => void
41
- [rest:string]: any
41
+ formRef?: Ref<HTMLFormElement>
42
}
43
export function Form({ fields, values, set, defaults, save, stickyBar, addToBar=[], barSx, formRef, onError, ...rest }: FormProps) {
44
- const [loading, setLoading] = useStateMounted(false)
45
- const [errors, setErrors] = useStateMounted<Dict>({})
44
+ const mounted = useRef(false)
45
+ useEffect(() => {
46
+ mounted.current = true
47
+ return () => {
48
+ mounted.current = false
49
+ }
50
+ }, [])
51
+
52
+ const [loading, setLoading] = useState(false)
53
+ const [errors, setErrors] = useState<Dict>({})
54
const saveBtn = typeof save === 'function' ? { onClick: save } : save
55
const { onClick } = saveBtn
56
saveBtn.onClick = onClick && async function (ev) {
@@ -51,6 +59,7 @@ export function Form({ fields, values, set, defaults, save, stickyBar, addToBar=
59
for (const f of fields) {
60
if (!f || isValidElement(f) || !f.k || !f.validate) continue
61
const res = await f.validate(values?.[f.k], { values, fields })
62
+ if (!mounted.current) return
63
if (res !== true)
64
return setErrors({ [f.k]: res || true })
65
}
@@ -58,10 +67,13 @@ export function Form({ fields, values, set, defaults, save, stickyBar, addToBar=
67
return await onClick(ev)
68
}
69
catch(e) { onError?.(e) }
61
- finally { setLoading(false) }
70
+ finally {
71
+ if (mounted.current)
72
+ setLoading(false)
73
+ }
74
}
75
64
- const [pendingSubmit, setPendingSubmit] = useStateMounted(false)
76
+ const [pendingSubmit, setPendingSubmit] = useState(false)
77
useEffect(() => {
78
if (!pendingSubmit) return
79
setTimeout(saveBtn.onClick!)
@@ -110,7 +122,7 @@ export function Form({ fields, values, set, defaults, save, stickyBar, addToBar=
122
field.helperText = field.helperText ? h(Fragment, {}, error, h('br'), field.helperText)
123
: error
124
if (field.label === undefined)
113
- field.label = _.capitalize(k.replaceAll('_', ' '))
125
+ field.label = _.capitalize(k.replace(/_/g, ' '))
126
_.defaults(field, defaults?.(field))
127
}
128
const { xs=12, sm, md, lg, xl, comp=StringField,