-
Notifications
You must be signed in to change notification settings - Fork 136
/
Copy pathindex.ts
60 lines (50 loc) · 1.52 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import { getParseFn, Parser } from "./getParseFn";
export type FormError<T> = {
data: T;
error: unknown;
};
export type CreateFormOptions<T> = {
schema?: Parser;
onSubmit: (data: T) => Promise<void> | void;
onError: (errors: FormError<T>) => void | Promise<void>;
castNumbers?: boolean;
};
type FormEvent = Event & {
submitter: HTMLElement;
} & {
currentTarget: HTMLFormElement;
target: Element;
};
// Taken from https://youmightnotneed.com/lodash#set
const set = <T extends object>(obj: T, path: string | string[], value: string | number) => {
// Regex explained: https://regexr.com/58j0k
const pathArray = Array.isArray(path) ? path : path.match(/([^[.\]])+/g)!;
pathArray.reduce((acc, key, i) => {
if (acc[key] === undefined) acc[key] = {};
if (i === pathArray.length - 1) acc[key] = value;
return acc[key];
}, obj as any);
};
export const createForm = <T>(options: CreateFormOptions<T>) => {
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
let data: Partial<T> = {};
for (const [name, value] of formData.entries()) {
const v = !options.castNumbers || isNaN(value as any) ? value.toString() : Number(value);
set(data, name, v);
}
try {
if (options.schema) {
const parser = getParseFn(options.schema);
await parser(data);
}
options.onSubmit(data as T);
} catch (e) {
options.onError({ data: data as T, error: e });
}
};
return {
handleSubmit
};
};