remix-validated-form 3.2.0 → 3.3.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/.turbo/turbo-build.log +2 -2
- package/README.md +2 -2
- package/browser/ValidatedForm.js +16 -6
- package/browser/internal/MultiValueMap.d.ts +9 -0
- package/browser/internal/MultiValueMap.js +39 -1
- package/browser/internal/SingleTypeMultiValueMap.d.ts +1 -0
- package/browser/internal/SingleTypeMultiValueMap.js +1 -0
- package/browser/internal/flatten.js +6 -1
- package/browser/internal/getInputProps.d.ts +5 -10
- package/browser/internal/getInputProps.js +2 -1
- package/browser/internal/test.d.ts +0 -1
- package/browser/internal/test.js +15 -10
- package/browser/internal/useMultiValueMap.d.ts +1 -0
- package/browser/internal/useMultiValueMap.js +11 -0
- package/build/ValidatedForm.js +17 -7
- package/build/internal/MultiValueMap.d.ts +9 -0
- package/build/internal/MultiValueMap.js +44 -0
- package/build/internal/flatten.js +6 -1
- package/build/internal/getInputProps.d.ts +5 -10
- package/build/internal/getInputProps.js +2 -1
- package/build/internal/test.d.ts +0 -0
- package/build/internal/test.js +0 -0
- package/package.json +1 -1
- package/src/ValidatedForm.tsx +15 -9
- package/src/internal/{SingleTypeMultiValueMap.ts → MultiValueMap.ts} +2 -1
- package/src/internal/flatten.ts +9 -2
- package/src/internal/getInputProps.ts +13 -18
package/.turbo/turbo-build.log
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
[2K[1G[2m$ npm run build:browser && npm run build:main[22m
|
2
2
|
|
3
|
-
> remix-validated-form@3.
|
3
|
+
> remix-validated-form@3.3.0 build:browser
|
4
4
|
> tsc --module ESNext --outDir ./browser
|
5
5
|
|
6
6
|
|
7
|
-
> remix-validated-form@3.
|
7
|
+
> remix-validated-form@3.3.0 build:main
|
8
8
|
> tsc --module CommonJS --outDir ./build
|
9
9
|
|
package/README.md
CHANGED
@@ -63,7 +63,7 @@ type MyInputProps = {
|
|
63
63
|
label: string;
|
64
64
|
};
|
65
65
|
|
66
|
-
export const MyInput = ({ name, label }:
|
66
|
+
export const MyInput = ({ name, label }: MyInputProps) => {
|
67
67
|
const { error, getInputProps } = useField(name);
|
68
68
|
return (
|
69
69
|
<div>
|
@@ -257,4 +257,4 @@ See the [Remix](https://remix.run/docs/en/v1/api/remix#sessionflashkey-value) do
|
|
257
257
|
|
258
258
|
## Why is my cancel button triggering form submission?
|
259
259
|
Problem: the cancel button has an onClick handler to navigate away from the form route but instead it is submitting the form.
|
260
|
-
A button defaults to `type="submit"` in a form which will submit the form by default. If you want to prevent this you can add `type="reset"` or `type="button"` to the cancel button.
|
260
|
+
A button defaults to `type="submit"` in a form which will submit the form by default. If you want to prevent this you can add `type="reset"` or `type="button"` to the cancel button.
|
package/browser/ValidatedForm.js
CHANGED
@@ -3,7 +3,7 @@ import { Form as RemixForm, useActionData, useFormAction, useTransition, } from
|
|
3
3
|
import { useEffect, useMemo, useRef, useState, } from "react";
|
4
4
|
import invariant from "tiny-invariant";
|
5
5
|
import { FormContext } from "./internal/formContext";
|
6
|
-
import { useMultiValueMap
|
6
|
+
import { useMultiValueMap } from "./internal/MultiValueMap";
|
7
7
|
import { useSubmitComplete } from "./internal/submissionCallbacks";
|
8
8
|
import { omit, mergeRefs } from "./internal/util";
|
9
9
|
function useFieldErrorsFromBackend(fetcher, subaction) {
|
@@ -123,14 +123,24 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
|
|
123
123
|
validateField: (fieldName) => {
|
124
124
|
invariant(formRef.current, "Cannot find reference to form");
|
125
125
|
const { error } = validator.validateField(getDataFromForm(formRef.current), fieldName);
|
126
|
+
// By checking and returning `prev` here, we can avoid a re-render
|
127
|
+
// if the validation state is the same.
|
126
128
|
if (error) {
|
127
|
-
setFieldErrors((prev) =>
|
128
|
-
|
129
|
-
|
130
|
-
|
129
|
+
setFieldErrors((prev) => {
|
130
|
+
if (prev[fieldName] === error)
|
131
|
+
return prev;
|
132
|
+
return {
|
133
|
+
...prev,
|
134
|
+
[fieldName]: error,
|
135
|
+
};
|
136
|
+
});
|
131
137
|
}
|
132
138
|
else {
|
133
|
-
setFieldErrors((prev) =>
|
139
|
+
setFieldErrors((prev) => {
|
140
|
+
if (!(fieldName in prev))
|
141
|
+
return prev;
|
142
|
+
return omit(prev, fieldName);
|
143
|
+
});
|
134
144
|
}
|
135
145
|
},
|
136
146
|
registerReceiveFocus: (fieldName, handler) => {
|
@@ -0,0 +1,9 @@
|
|
1
|
+
export declare class MultiValueMap<Key, Value> {
|
2
|
+
private dict;
|
3
|
+
add: (key: Key, value: Value) => void;
|
4
|
+
remove: (key: Key, value: Value) => void;
|
5
|
+
getAll: (key: Key) => Value[];
|
6
|
+
entries: () => IterableIterator<[Key, Value[]]>;
|
7
|
+
has: (key: Key) => boolean;
|
8
|
+
}
|
9
|
+
export declare const useMultiValueMap: <Key, Value>() => () => MultiValueMap<Key, Value>;
|
@@ -1 +1,39 @@
|
|
1
|
-
|
1
|
+
import { useRef } from "react";
|
2
|
+
export class MultiValueMap {
|
3
|
+
constructor() {
|
4
|
+
this.dict = new Map();
|
5
|
+
this.add = (key, value) => {
|
6
|
+
if (this.dict.has(key)) {
|
7
|
+
this.dict.get(key).push(value);
|
8
|
+
}
|
9
|
+
else {
|
10
|
+
this.dict.set(key, [value]);
|
11
|
+
}
|
12
|
+
};
|
13
|
+
this.remove = (key, value) => {
|
14
|
+
if (!this.dict.has(key))
|
15
|
+
return;
|
16
|
+
const array = this.dict.get(key);
|
17
|
+
const index = array.indexOf(value);
|
18
|
+
if (index !== -1)
|
19
|
+
array.splice(index, 1);
|
20
|
+
if (array.length === 0)
|
21
|
+
this.dict.delete(key);
|
22
|
+
};
|
23
|
+
this.getAll = (key) => {
|
24
|
+
var _a;
|
25
|
+
return (_a = this.dict.get(key)) !== null && _a !== void 0 ? _a : [];
|
26
|
+
};
|
27
|
+
this.entries = () => this.dict.entries();
|
28
|
+
this.has = (key) => this.dict.has(key);
|
29
|
+
}
|
30
|
+
}
|
31
|
+
export const useMultiValueMap = () => {
|
32
|
+
const ref = useRef(null);
|
33
|
+
return () => {
|
34
|
+
if (ref.current)
|
35
|
+
return ref.current;
|
36
|
+
ref.current = new MultiValueMap();
|
37
|
+
return ref.current;
|
38
|
+
};
|
39
|
+
};
|
@@ -3,6 +3,7 @@ export declare class MultiValueMap<Key, Value> {
|
|
3
3
|
add: (key: Key, value: Value) => void;
|
4
4
|
remove: (key: Key, value: Value) => void;
|
5
5
|
getAll: (key: Key) => Value[];
|
6
|
+
entries: () => IterableIterator<[Key, Value[]]>;
|
6
7
|
has: (key: Key) => boolean;
|
7
8
|
}
|
8
9
|
export declare const useMultiValueMap: <Key, Value>() => () => MultiValueMap<Key, Value>;
|
@@ -6,7 +6,12 @@ import keys from "lodash/keys";
|
|
6
6
|
import mapKeys from "lodash/mapKeys";
|
7
7
|
import set from "lodash/set";
|
8
8
|
import transform from "lodash/transform";
|
9
|
-
|
9
|
+
import { MultiValueMap } from "./MultiValueMap";
|
10
|
+
export const objectFromPathEntries = (entries) => {
|
11
|
+
const map = new MultiValueMap();
|
12
|
+
entries.forEach(([key, value]) => map.add(key, value));
|
13
|
+
return [...map.entries()].reduce((acc, [key, value]) => set(acc, key, value.length === 1 ? value[0] : value), {});
|
14
|
+
};
|
10
15
|
/** Flatten an object so there are no nested objects or arrays */
|
11
16
|
export function flatten(obj, preserveEmpty = false) {
|
12
17
|
return transform(obj, function (result, value, key) {
|
@@ -14,15 +14,10 @@ export declare type CreateGetInputPropsOptions = {
|
|
14
14
|
validationBehavior?: Partial<ValidationBehaviorOptions>;
|
15
15
|
name: string;
|
16
16
|
};
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
declare type OmitHandledProps = {
|
18
|
+
name?: never;
|
19
|
+
defaultValue?: never;
|
20
20
|
};
|
21
|
-
export declare type
|
22
|
-
name: string;
|
23
|
-
onChange: (...args: any[]) => void;
|
24
|
-
onBlur: (...args: any[]) => void;
|
25
|
-
defaultValue?: any;
|
26
|
-
};
|
27
|
-
export declare type GetInputProps = <T extends {}>(props?: T & MinimalInputProps) => T & MinimalResult;
|
21
|
+
export declare type GetInputProps = <T extends Record<string, any>>(props?: T & OmitHandledProps) => T;
|
28
22
|
export declare const createGetInputProps: ({ clearError, validate, defaultValue, touched, setTouched, hasBeenSubmitted, validationBehavior, name, }: CreateGetInputPropsOptions) => GetInputProps;
|
23
|
+
export {};
|
@@ -14,7 +14,7 @@ export const createGetInputProps = ({ clearError, validate, defaultValue, touche
|
|
14
14
|
: touched
|
15
15
|
? validationBehaviors.whenTouched
|
16
16
|
: validationBehaviors.initial;
|
17
|
-
|
17
|
+
const result = {
|
18
18
|
...props,
|
19
19
|
onChange: (...args) => {
|
20
20
|
var _a;
|
@@ -34,5 +34,6 @@ export const createGetInputProps = ({ clearError, validate, defaultValue, touche
|
|
34
34
|
defaultValue,
|
35
35
|
name,
|
36
36
|
};
|
37
|
+
return result;
|
37
38
|
};
|
38
39
|
};
|
@@ -1 +0,0 @@
|
|
1
|
-
export {};
|
package/browser/internal/test.js
CHANGED
@@ -1,10 +1,15 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
}
|
1
|
+
"use strict";
|
2
|
+
z.preprocess((val) => {
|
3
|
+
// Somewhat awkward -- this gets processed per item in the form,
|
4
|
+
// but as a whole array in the backend
|
5
|
+
if (Array.isArray(val)) {
|
6
|
+
return val;
|
7
|
+
}
|
8
|
+
else {
|
9
|
+
return [val];
|
10
|
+
}
|
11
|
+
}, z.array(z.preprocess((val) => {
|
12
|
+
return typeof val !== "string" || val === ""
|
13
|
+
? undefined
|
14
|
+
: Number.parseInt(val);
|
15
|
+
}, z.number())));
|
@@ -0,0 +1 @@
|
|
1
|
+
export declare const useMultiValueMap: <Key, Value>() => () => any;
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import { MultiValueMap } from "multi-value-map";
|
2
|
+
import { useRef } from "react";
|
3
|
+
export const useMultiValueMap = () => {
|
4
|
+
const ref = useRef(null);
|
5
|
+
return () => {
|
6
|
+
if (ref.current)
|
7
|
+
return ref.current;
|
8
|
+
ref.current = new MultiValueMap();
|
9
|
+
return ref.current;
|
10
|
+
};
|
11
|
+
};
|
package/build/ValidatedForm.js
CHANGED
@@ -9,7 +9,7 @@ const react_1 = require("@remix-run/react");
|
|
9
9
|
const react_2 = require("react");
|
10
10
|
const tiny_invariant_1 = __importDefault(require("tiny-invariant"));
|
11
11
|
const formContext_1 = require("./internal/formContext");
|
12
|
-
const
|
12
|
+
const MultiValueMap_1 = require("./internal/MultiValueMap");
|
13
13
|
const submissionCallbacks_1 = require("./internal/submissionCallbacks");
|
14
14
|
const util_1 = require("./internal/util");
|
15
15
|
function useFieldErrorsFromBackend(fetcher, subaction) {
|
@@ -111,7 +111,7 @@ function ValidatedForm({ validator, onSubmit, children, fetcher, action, default
|
|
111
111
|
(_a = formRef.current) === null || _a === void 0 ? void 0 : _a.reset();
|
112
112
|
}
|
113
113
|
});
|
114
|
-
const customFocusHandlers = (0,
|
114
|
+
const customFocusHandlers = (0, MultiValueMap_1.useMultiValueMap)();
|
115
115
|
const contextValue = (0, react_2.useMemo)(() => ({
|
116
116
|
fieldErrors,
|
117
117
|
action,
|
@@ -129,14 +129,24 @@ function ValidatedForm({ validator, onSubmit, children, fetcher, action, default
|
|
129
129
|
validateField: (fieldName) => {
|
130
130
|
(0, tiny_invariant_1.default)(formRef.current, "Cannot find reference to form");
|
131
131
|
const { error } = validator.validateField(getDataFromForm(formRef.current), fieldName);
|
132
|
+
// By checking and returning `prev` here, we can avoid a re-render
|
133
|
+
// if the validation state is the same.
|
132
134
|
if (error) {
|
133
|
-
setFieldErrors((prev) =>
|
134
|
-
|
135
|
-
|
136
|
-
|
135
|
+
setFieldErrors((prev) => {
|
136
|
+
if (prev[fieldName] === error)
|
137
|
+
return prev;
|
138
|
+
return {
|
139
|
+
...prev,
|
140
|
+
[fieldName]: error,
|
141
|
+
};
|
142
|
+
});
|
137
143
|
}
|
138
144
|
else {
|
139
|
-
setFieldErrors((prev) =>
|
145
|
+
setFieldErrors((prev) => {
|
146
|
+
if (!(fieldName in prev))
|
147
|
+
return prev;
|
148
|
+
return (0, util_1.omit)(prev, fieldName);
|
149
|
+
});
|
140
150
|
}
|
141
151
|
},
|
142
152
|
registerReceiveFocus: (fieldName, handler) => {
|
@@ -0,0 +1,9 @@
|
|
1
|
+
export declare class MultiValueMap<Key, Value> {
|
2
|
+
private dict;
|
3
|
+
add: (key: Key, value: Value) => void;
|
4
|
+
remove: (key: Key, value: Value) => void;
|
5
|
+
getAll: (key: Key) => Value[];
|
6
|
+
entries: () => IterableIterator<[Key, Value[]]>;
|
7
|
+
has: (key: Key) => boolean;
|
8
|
+
}
|
9
|
+
export declare const useMultiValueMap: <Key, Value>() => () => MultiValueMap<Key, Value>;
|
@@ -0,0 +1,44 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.useMultiValueMap = exports.MultiValueMap = void 0;
|
4
|
+
const react_1 = require("react");
|
5
|
+
class MultiValueMap {
|
6
|
+
constructor() {
|
7
|
+
this.dict = new Map();
|
8
|
+
this.add = (key, value) => {
|
9
|
+
if (this.dict.has(key)) {
|
10
|
+
this.dict.get(key).push(value);
|
11
|
+
}
|
12
|
+
else {
|
13
|
+
this.dict.set(key, [value]);
|
14
|
+
}
|
15
|
+
};
|
16
|
+
this.remove = (key, value) => {
|
17
|
+
if (!this.dict.has(key))
|
18
|
+
return;
|
19
|
+
const array = this.dict.get(key);
|
20
|
+
const index = array.indexOf(value);
|
21
|
+
if (index !== -1)
|
22
|
+
array.splice(index, 1);
|
23
|
+
if (array.length === 0)
|
24
|
+
this.dict.delete(key);
|
25
|
+
};
|
26
|
+
this.getAll = (key) => {
|
27
|
+
var _a;
|
28
|
+
return (_a = this.dict.get(key)) !== null && _a !== void 0 ? _a : [];
|
29
|
+
};
|
30
|
+
this.entries = () => this.dict.entries();
|
31
|
+
this.has = (key) => this.dict.has(key);
|
32
|
+
}
|
33
|
+
}
|
34
|
+
exports.MultiValueMap = MultiValueMap;
|
35
|
+
const useMultiValueMap = () => {
|
36
|
+
const ref = (0, react_1.useRef)(null);
|
37
|
+
return () => {
|
38
|
+
if (ref.current)
|
39
|
+
return ref.current;
|
40
|
+
ref.current = new MultiValueMap();
|
41
|
+
return ref.current;
|
42
|
+
};
|
43
|
+
};
|
44
|
+
exports.useMultiValueMap = useMultiValueMap;
|
@@ -12,7 +12,12 @@ const keys_1 = __importDefault(require("lodash/keys"));
|
|
12
12
|
const mapKeys_1 = __importDefault(require("lodash/mapKeys"));
|
13
13
|
const set_1 = __importDefault(require("lodash/set"));
|
14
14
|
const transform_1 = __importDefault(require("lodash/transform"));
|
15
|
-
const
|
15
|
+
const MultiValueMap_1 = require("./MultiValueMap");
|
16
|
+
const objectFromPathEntries = (entries) => {
|
17
|
+
const map = new MultiValueMap_1.MultiValueMap();
|
18
|
+
entries.forEach(([key, value]) => map.add(key, value));
|
19
|
+
return [...map.entries()].reduce((acc, [key, value]) => (0, set_1.default)(acc, key, value.length === 1 ? value[0] : value), {});
|
20
|
+
};
|
16
21
|
exports.objectFromPathEntries = objectFromPathEntries;
|
17
22
|
/** Flatten an object so there are no nested objects or arrays */
|
18
23
|
function flatten(obj, preserveEmpty = false) {
|
@@ -14,15 +14,10 @@ export declare type CreateGetInputPropsOptions = {
|
|
14
14
|
validationBehavior?: Partial<ValidationBehaviorOptions>;
|
15
15
|
name: string;
|
16
16
|
};
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
declare type OmitHandledProps = {
|
18
|
+
name?: never;
|
19
|
+
defaultValue?: never;
|
20
20
|
};
|
21
|
-
export declare type
|
22
|
-
name: string;
|
23
|
-
onChange: (...args: any[]) => void;
|
24
|
-
onBlur: (...args: any[]) => void;
|
25
|
-
defaultValue?: any;
|
26
|
-
};
|
27
|
-
export declare type GetInputProps = <T extends {}>(props?: T & MinimalInputProps) => T & MinimalResult;
|
21
|
+
export declare type GetInputProps = <T extends Record<string, any>>(props?: T & OmitHandledProps) => T;
|
28
22
|
export declare const createGetInputProps: ({ clearError, validate, defaultValue, touched, setTouched, hasBeenSubmitted, validationBehavior, name, }: CreateGetInputPropsOptions) => GetInputProps;
|
23
|
+
export {};
|
@@ -17,7 +17,7 @@ const createGetInputProps = ({ clearError, validate, defaultValue, touched, setT
|
|
17
17
|
: touched
|
18
18
|
? validationBehaviors.whenTouched
|
19
19
|
: validationBehaviors.initial;
|
20
|
-
|
20
|
+
const result = {
|
21
21
|
...props,
|
22
22
|
onChange: (...args) => {
|
23
23
|
var _a;
|
@@ -37,6 +37,7 @@ const createGetInputProps = ({ clearError, validate, defaultValue, touched, setT
|
|
37
37
|
defaultValue,
|
38
38
|
name,
|
39
39
|
};
|
40
|
+
return result;
|
40
41
|
};
|
41
42
|
};
|
42
43
|
exports.createGetInputProps = createGetInputProps;
|
package/build/internal/test.d.ts
CHANGED
File without changes
|
package/build/internal/test.js
CHANGED
File without changes
|
package/package.json
CHANGED
package/src/ValidatedForm.tsx
CHANGED
@@ -14,10 +14,7 @@ import React, {
|
|
14
14
|
} from "react";
|
15
15
|
import invariant from "tiny-invariant";
|
16
16
|
import { FormContext, FormContextValue } from "./internal/formContext";
|
17
|
-
import {
|
18
|
-
MultiValueMap,
|
19
|
-
useMultiValueMap,
|
20
|
-
} from "./internal/SingleTypeMultiValueMap";
|
17
|
+
import { MultiValueMap, useMultiValueMap } from "./internal/MultiValueMap";
|
21
18
|
import { useSubmitComplete } from "./internal/submissionCallbacks";
|
22
19
|
import { omit, mergeRefs } from "./internal/util";
|
23
20
|
import {
|
@@ -229,13 +226,22 @@ export function ValidatedForm<DataType>({
|
|
229
226
|
getDataFromForm(formRef.current),
|
230
227
|
fieldName as any
|
231
228
|
);
|
229
|
+
|
230
|
+
// By checking and returning `prev` here, we can avoid a re-render
|
231
|
+
// if the validation state is the same.
|
232
232
|
if (error) {
|
233
|
-
setFieldErrors((prev) =>
|
234
|
-
|
235
|
-
|
236
|
-
|
233
|
+
setFieldErrors((prev) => {
|
234
|
+
if (prev[fieldName] === error) return prev;
|
235
|
+
return {
|
236
|
+
...prev,
|
237
|
+
[fieldName]: error,
|
238
|
+
};
|
239
|
+
});
|
237
240
|
} else {
|
238
|
-
setFieldErrors((prev) =>
|
241
|
+
setFieldErrors((prev) => {
|
242
|
+
if (!(fieldName in prev)) return prev;
|
243
|
+
return omit(prev, fieldName);
|
244
|
+
});
|
239
245
|
}
|
240
246
|
},
|
241
247
|
registerReceiveFocus: (fieldName, handler) => {
|
@@ -4,7 +4,6 @@ export class MultiValueMap<Key, Value> {
|
|
4
4
|
private dict: Map<Key, Value[]> = new Map();
|
5
5
|
|
6
6
|
add = (key: Key, value: Value) => {
|
7
|
-
this.dict.set(key, [...(this.dict.get(key) ?? []), value]);
|
8
7
|
if (this.dict.has(key)) {
|
9
8
|
this.dict.get(key)!.push(value);
|
10
9
|
} else {
|
@@ -24,6 +23,8 @@ export class MultiValueMap<Key, Value> {
|
|
24
23
|
return this.dict.get(key) ?? [];
|
25
24
|
};
|
26
25
|
|
26
|
+
entries = (): IterableIterator<[Key, Value[]]> => this.dict.entries();
|
27
|
+
|
27
28
|
has = (key: Key): boolean => this.dict.has(key);
|
28
29
|
}
|
29
30
|
|
package/src/internal/flatten.ts
CHANGED
@@ -7,9 +7,16 @@ import mapKeys from "lodash/mapKeys";
|
|
7
7
|
import set from "lodash/set";
|
8
8
|
import transform from "lodash/transform";
|
9
9
|
import { GenericObject } from "..";
|
10
|
+
import { MultiValueMap } from "./MultiValueMap";
|
10
11
|
|
11
|
-
export const objectFromPathEntries = (entries: [string, any][]) =>
|
12
|
-
|
12
|
+
export const objectFromPathEntries = (entries: [string, any][]) => {
|
13
|
+
const map = new MultiValueMap<string, any>();
|
14
|
+
entries.forEach(([key, value]) => map.add(key, value));
|
15
|
+
return [...map.entries()].reduce(
|
16
|
+
(acc, [key, value]) => set(acc, key, value.length === 1 ? value[0] : value),
|
17
|
+
{}
|
18
|
+
);
|
19
|
+
};
|
13
20
|
|
14
21
|
/** Flatten an object so there are no nested objects or arrays */
|
15
22
|
export function flatten(obj: GenericObject, preserveEmpty = false) {
|
@@ -17,21 +17,13 @@ export type CreateGetInputPropsOptions = {
|
|
17
17
|
name: string;
|
18
18
|
};
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
};
|
20
|
+
// Using Omit<T, HandledProps> breaks type inference sometimes for some reason.
|
21
|
+
// Doing T & OmitHandledProps gives us the same behavior without breaking type inference.
|
22
|
+
type OmitHandledProps = { name?: never; defaultValue?: never };
|
24
23
|
|
25
|
-
export type
|
26
|
-
|
27
|
-
|
28
|
-
onBlur: (...args: any[]) => void;
|
29
|
-
defaultValue?: any;
|
30
|
-
};
|
31
|
-
|
32
|
-
export type GetInputProps = <T extends {}>(
|
33
|
-
props?: T & MinimalInputProps
|
34
|
-
) => T & MinimalResult;
|
24
|
+
export type GetInputProps = <T extends Record<string, any>>(
|
25
|
+
props?: T & OmitHandledProps
|
26
|
+
) => T;
|
35
27
|
|
36
28
|
const defaultValidationBehavior: ValidationBehaviorOptions = {
|
37
29
|
initial: "onBlur",
|
@@ -54,20 +46,21 @@ export const createGetInputProps = ({
|
|
54
46
|
...validationBehavior,
|
55
47
|
};
|
56
48
|
|
57
|
-
return (props = {} as any) => {
|
49
|
+
return <T extends Record<string, any>>(props = {} as any) => {
|
58
50
|
const behavior = hasBeenSubmitted
|
59
51
|
? validationBehaviors.whenSubmitted
|
60
52
|
: touched
|
61
53
|
? validationBehaviors.whenTouched
|
62
54
|
: validationBehaviors.initial;
|
63
|
-
|
55
|
+
|
56
|
+
const result: T = {
|
64
57
|
...props,
|
65
|
-
onChange: (...args) => {
|
58
|
+
onChange: (...args: unknown[]) => {
|
66
59
|
if (behavior === "onChange") validate();
|
67
60
|
else clearError();
|
68
61
|
return props?.onChange?.(...args);
|
69
62
|
},
|
70
|
-
onBlur: (...args) => {
|
63
|
+
onBlur: (...args: unknown[]) => {
|
71
64
|
if (behavior === "onBlur") validate();
|
72
65
|
setTouched(true);
|
73
66
|
return props?.onBlur?.(...args);
|
@@ -75,5 +68,7 @@ export const createGetInputProps = ({
|
|
75
68
|
defaultValue,
|
76
69
|
name,
|
77
70
|
};
|
71
|
+
|
72
|
+
return result;
|
78
73
|
};
|
79
74
|
};
|