@ask-ell/react 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/package.json +24 -0
- package/src/forms.ts +92 -0
- package/src/index.ts +3 -0
- package/src/keyboard-events.ts +19 -0
- package/src/select-events.ts +5 -0
- package/tsconfig.json +10 -0
- package/tsconfig.lib.json +14 -0
package/README.md
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ask-ell/react",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
"./package.json": "./package.json",
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"react": "^19.2.0",
|
|
19
|
+
"tslib": "^2.3.0"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/react": "^19.2.2"
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/forms.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Dispatch, FormEvent, SetStateAction, useState } from "react";
|
|
2
|
+
|
|
3
|
+
export enum SubmitMode {
|
|
4
|
+
ON_TRIGGER,
|
|
5
|
+
AUTOMATIC
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type ErrorSetter<FormData> = (key: keyof FormData) => (errorMessage: string) => void;
|
|
9
|
+
|
|
10
|
+
export type OnSubmitCallbackParams<FormData> = {
|
|
11
|
+
formData: FormData;
|
|
12
|
+
appendError: ErrorSetter<FormData>;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type OnSubmitCallback<FormData> = (params: OnSubmitCallbackParams<FormData>) => void;
|
|
16
|
+
|
|
17
|
+
export type UseFormParams<FormData> = {
|
|
18
|
+
defaultValue: FormData;
|
|
19
|
+
onSubmit: OnSubmitCallback<FormData>;
|
|
20
|
+
submitMode?: SubmitMode
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type FormDataFieldUpdater<FormData> = <Key extends keyof FormData>(key: Key) => (value: FormData[Key]) => void;
|
|
24
|
+
|
|
25
|
+
type SubmitTrigger = () => void;
|
|
26
|
+
|
|
27
|
+
export interface ErrorsMap<FormData> extends Map<keyof FormData, string[]> { }
|
|
28
|
+
|
|
29
|
+
export type FormController<FormData> = {
|
|
30
|
+
formData: FormData;
|
|
31
|
+
errors: ErrorsMap<FormData>;
|
|
32
|
+
setFormData: Dispatch<SetStateAction<FormData>>;
|
|
33
|
+
updateFormData: FormDataFieldUpdater<FormData>;
|
|
34
|
+
submit: SubmitTrigger;
|
|
35
|
+
reset: () => void;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export function useFormController<FormData>({ defaultValue, onSubmit, submitMode }: UseFormParams<FormData>): FormController<FormData> {
|
|
39
|
+
const [formData, setFormData] = useState<FormData>(defaultValue);
|
|
40
|
+
const [errors, setErrors] = useState<ErrorsMap<FormData>>(new Map());
|
|
41
|
+
|
|
42
|
+
const appendError: ErrorSetter<FormData> = (key: keyof FormData) => (message: string): void => {
|
|
43
|
+
const existingErrors: string[] = errors.get(key) ?? [];
|
|
44
|
+
existingErrors.push(message);
|
|
45
|
+
errors.set(key, existingErrors);
|
|
46
|
+
setErrors(new Map(errors));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const prepareOnSubmit = (callback: () => void) => {
|
|
50
|
+
errors.clear()
|
|
51
|
+
setErrors(new Map(errors));
|
|
52
|
+
callback();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const updateFormData: FormDataFieldUpdater<FormData> = <Key extends keyof FormData>(key: Key) => (value: FormData[Key]) => {
|
|
56
|
+
setTimeout((): void => {
|
|
57
|
+
const newFormData: FormData = {
|
|
58
|
+
...formData,
|
|
59
|
+
[key]: value
|
|
60
|
+
}
|
|
61
|
+
setFormData(() => newFormData);
|
|
62
|
+
if (submitMode === SubmitMode.AUTOMATIC) {
|
|
63
|
+
prepareOnSubmit(
|
|
64
|
+
(): void => onSubmit({ formData: newFormData, appendError })
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const submit: SubmitTrigger = (): void => prepareOnSubmit(
|
|
71
|
+
(): void => onSubmit({ formData, appendError })
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const reset = (): void => {
|
|
75
|
+
setFormData(defaultValue)
|
|
76
|
+
errors.clear();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
formData,
|
|
81
|
+
errors,
|
|
82
|
+
setFormData,
|
|
83
|
+
updateFormData,
|
|
84
|
+
submit,
|
|
85
|
+
reset
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const preventDefault = (callback: () => void) => (event: FormEvent): void => {
|
|
90
|
+
event.preventDefault();
|
|
91
|
+
callback();
|
|
92
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ChangeEvent, KeyboardEvent } from 'react';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export const handleEnterKeyDown =
|
|
5
|
+
(callback: () => void) => (event: KeyboardEvent<HTMLInputElement>) => {
|
|
6
|
+
if (event.key === 'Enter') {
|
|
7
|
+
callback();
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const handleKeyboardEvent =
|
|
12
|
+
(callback: (fieldValue: string) => void) => <TargetType>(event: KeyboardEvent<TargetType>) => {
|
|
13
|
+
callback((event.target as any).value as string);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const handleChangeEvent =
|
|
17
|
+
(callback: (fieldValue: string) => void) => <TargetType>(event: ChangeEvent<TargetType>) => {
|
|
18
|
+
callback((event.target as any).value as string);
|
|
19
|
+
};
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"baseUrl": ".",
|
|
5
|
+
"rootDir": "src",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo",
|
|
8
|
+
"emitDeclarationOnly": false,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"types": ["node"]
|
|
11
|
+
},
|
|
12
|
+
"include": ["src/**/*.ts"],
|
|
13
|
+
"references": []
|
|
14
|
+
}
|