@rjsf/utils 6.0.0-beta.8 → 6.0.0
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/dist/{index.js → index.cjs} +563 -200
- package/dist/index.cjs.map +7 -0
- package/dist/utils.esm.js +562 -199
- package/dist/utils.esm.js.map +4 -4
- package/dist/utils.umd.js +533 -193
- package/lib/ErrorSchemaBuilder.d.ts +2 -2
- package/lib/constants.d.ts +3 -0
- package/lib/constants.js +3 -0
- package/lib/constants.js.map +1 -1
- package/lib/createSchemaUtils.js +25 -18
- package/lib/createSchemaUtils.js.map +1 -1
- package/lib/enums.d.ts +13 -3
- package/lib/enums.js +13 -3
- package/lib/enums.js.map +1 -1
- package/lib/findSchemaDefinition.d.ts +6 -0
- package/lib/findSchemaDefinition.js +44 -3
- package/lib/findSchemaDefinition.js.map +1 -1
- package/lib/getDateElementProps.d.ts +1 -2
- package/lib/getTestIds.js +2 -2
- package/lib/getTestIds.js.map +1 -1
- package/lib/getUiOptions.js +4 -0
- package/lib/getUiOptions.js.map +1 -1
- package/lib/getWidget.js +3 -3
- package/lib/getWidget.js.map +1 -1
- package/lib/guessType.d.ts +1 -1
- package/lib/idGenerators.d.ts +22 -15
- package/lib/idGenerators.js +17 -8
- package/lib/idGenerators.js.map +1 -1
- package/lib/index.d.ts +16 -6
- package/lib/index.js +13 -4
- package/lib/index.js.map +1 -1
- package/lib/isFormDataAvailable.d.ts +7 -0
- package/lib/isFormDataAvailable.js +13 -0
- package/lib/isFormDataAvailable.js.map +1 -0
- package/lib/isRootSchema.d.ts +13 -0
- package/lib/isRootSchema.js +25 -0
- package/lib/isRootSchema.js.map +1 -0
- package/lib/mergeDefaultsWithFormData.js +14 -2
- package/lib/mergeDefaultsWithFormData.js.map +1 -1
- package/lib/nameGenerators.d.ts +13 -0
- package/lib/nameGenerators.js +30 -0
- package/lib/nameGenerators.js.map +1 -0
- package/lib/schema/getDefaultFormState.d.ts +17 -3
- package/lib/schema/getDefaultFormState.js +66 -26
- package/lib/schema/getDefaultFormState.js.map +1 -1
- package/lib/schema/getDisplayLabel.js +2 -2
- package/lib/schema/getDisplayLabel.js.map +1 -1
- package/lib/schema/index.d.ts +1 -2
- package/lib/schema/index.js +1 -2
- package/lib/schema/index.js.map +1 -1
- package/lib/schema/retrieveSchema.d.ts +10 -5
- package/lib/schema/retrieveSchema.js +40 -17
- package/lib/schema/retrieveSchema.js.map +1 -1
- package/lib/shallowEquals.d.ts +8 -0
- package/lib/shallowEquals.js +36 -0
- package/lib/shallowEquals.js.map +1 -0
- package/lib/shouldRender.d.ts +8 -2
- package/lib/shouldRender.js +17 -2
- package/lib/shouldRender.js.map +1 -1
- package/lib/shouldRenderOptionalField.d.ts +18 -0
- package/lib/shouldRenderOptionalField.js +47 -0
- package/lib/shouldRenderOptionalField.js.map +1 -0
- package/lib/toFieldPathId.d.ts +14 -0
- package/lib/toFieldPathId.js +26 -0
- package/lib/toFieldPathId.js.map +1 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/types.d.ts +196 -105
- package/lib/useAltDateWidgetProps.d.ts +39 -0
- package/lib/useAltDateWidgetProps.js +71 -0
- package/lib/useAltDateWidgetProps.js.map +1 -0
- package/lib/useDeepCompareMemo.d.ts +8 -0
- package/lib/useDeepCompareMemo.js +17 -0
- package/lib/useDeepCompareMemo.js.map +1 -0
- package/lib/useFileWidgetProps.d.ts +29 -0
- package/lib/useFileWidgetProps.js +119 -0
- package/lib/useFileWidgetProps.js.map +1 -0
- package/lib/validationDataMerge.d.ts +2 -1
- package/lib/validationDataMerge.js +3 -2
- package/lib/validationDataMerge.js.map +1 -1
- package/package.json +13 -14
- package/src/ErrorSchemaBuilder.ts +2 -2
- package/src/constants.ts +3 -0
- package/src/createSchemaUtils.ts +25 -26
- package/src/enums.ts +13 -3
- package/src/findSchemaDefinition.ts +51 -3
- package/src/getDateElementProps.ts +1 -1
- package/src/getTestIds.ts +2 -2
- package/src/getUiOptions.ts +4 -0
- package/src/getWidget.tsx +3 -3
- package/src/idGenerators.ts +35 -25
- package/src/index.ts +36 -5
- package/src/isFormDataAvailable.ts +13 -0
- package/src/isRootSchema.ts +30 -0
- package/src/mergeDefaultsWithFormData.ts +16 -2
- package/src/nameGenerators.ts +43 -0
- package/src/schema/getDefaultFormState.ts +87 -31
- package/src/schema/getDisplayLabel.ts +2 -2
- package/src/schema/index.ts +0 -2
- package/src/schema/retrieveSchema.ts +43 -7
- package/src/shallowEquals.ts +41 -0
- package/src/shouldRender.ts +27 -2
- package/src/shouldRenderOptionalField.ts +56 -0
- package/src/toFieldPathId.ts +34 -0
- package/src/types.ts +229 -113
- package/src/useAltDateWidgetProps.tsx +163 -0
- package/src/useDeepCompareMemo.ts +17 -0
- package/src/useFileWidgetProps.ts +155 -0
- package/src/validationDataMerge.ts +7 -1
- package/dist/index.js.map +0 -7
- package/lib/schema/toIdSchema.d.ts +0 -14
- package/lib/schema/toIdSchema.js +0 -62
- package/lib/schema/toIdSchema.js.map +0 -1
- package/src/schema/toIdSchema.ts +0 -131
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { MouseEvent, useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import dateRangeOptions from './dateRangeOptions';
|
|
4
|
+
import getDateElementProps, { DateElementFormat, DateElementProp } from './getDateElementProps';
|
|
5
|
+
import { ariaDescribedByIds } from './idGenerators';
|
|
6
|
+
import parseDateString from './parseDateString';
|
|
7
|
+
import toDateString from './toDateString';
|
|
8
|
+
import { DateObject, FormContextType, RJSFSchema, StrictRJSFSchema, WidgetProps } from './types';
|
|
9
|
+
|
|
10
|
+
/** Function that checks to see if a `DateObject` is ready for the onChange callback to be triggered
|
|
11
|
+
*
|
|
12
|
+
* @param state - The current `DateObject`
|
|
13
|
+
* @returns - True if the `state` is ready to trigger an onChange
|
|
14
|
+
*/
|
|
15
|
+
function readyForChange(state: DateObject) {
|
|
16
|
+
return Object.values(state).every((value) => value !== -1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** The Props for the `DateElement` component */
|
|
20
|
+
export type DateElementProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = Pick<
|
|
21
|
+
WidgetProps<T, S, F>,
|
|
22
|
+
'value' | 'name' | 'disabled' | 'readonly' | 'autofocus' | 'registry' | 'onBlur' | 'onFocus' | 'className'
|
|
23
|
+
> & {
|
|
24
|
+
/** The root id of the field */
|
|
25
|
+
rootId: string;
|
|
26
|
+
/** The selector function for a specific prop within the `DateObject`, for a value */
|
|
27
|
+
select: (property: keyof DateObject, value: any) => void;
|
|
28
|
+
/** The type of the date element */
|
|
29
|
+
type: DateElementProp['type'];
|
|
30
|
+
/** The range for the date element */
|
|
31
|
+
range: DateElementProp['range'];
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/** The `DateElement` component renders one of the 6 date element selectors for an `AltDateWidget`, using the `select`
|
|
35
|
+
* widget from the registry.
|
|
36
|
+
*
|
|
37
|
+
* @param props - The `DateElementProps` for the date element
|
|
38
|
+
*/
|
|
39
|
+
export function DateElement<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
|
|
40
|
+
props: DateElementProps<T, S, F>,
|
|
41
|
+
) {
|
|
42
|
+
const {
|
|
43
|
+
className = 'form-control',
|
|
44
|
+
type,
|
|
45
|
+
range,
|
|
46
|
+
value,
|
|
47
|
+
select,
|
|
48
|
+
rootId,
|
|
49
|
+
name,
|
|
50
|
+
disabled,
|
|
51
|
+
readonly,
|
|
52
|
+
autofocus,
|
|
53
|
+
registry,
|
|
54
|
+
onBlur,
|
|
55
|
+
onFocus,
|
|
56
|
+
} = props;
|
|
57
|
+
const id = `${rootId}_${type}`;
|
|
58
|
+
const { SelectWidget } = registry.widgets;
|
|
59
|
+
const onChange = useCallback((value: any) => select(type as keyof DateObject, value), [select, type]);
|
|
60
|
+
return (
|
|
61
|
+
<SelectWidget
|
|
62
|
+
schema={{ type: 'integer' } as S}
|
|
63
|
+
id={id}
|
|
64
|
+
name={name}
|
|
65
|
+
className={className}
|
|
66
|
+
options={{ enumOptions: dateRangeOptions<S>(range[0], range[1]) }}
|
|
67
|
+
placeholder={type}
|
|
68
|
+
value={value}
|
|
69
|
+
disabled={disabled}
|
|
70
|
+
readonly={readonly}
|
|
71
|
+
autofocus={autofocus}
|
|
72
|
+
onChange={onChange}
|
|
73
|
+
onBlur={onBlur}
|
|
74
|
+
onFocus={onFocus}
|
|
75
|
+
registry={registry}
|
|
76
|
+
label=''
|
|
77
|
+
aria-describedby={ariaDescribedByIds(rootId)}
|
|
78
|
+
/>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** The result of a call to the `useAltDateWidgetProps()` hook */
|
|
83
|
+
export type UseAltDateWidgetResult = {
|
|
84
|
+
/** The list of `DateElementProp` data to render for the `AltDateWidget` */
|
|
85
|
+
elements: DateElementProp[];
|
|
86
|
+
/** The callback that handles the changing of DateElement components */
|
|
87
|
+
handleChange: (property: keyof DateObject, value?: string) => void;
|
|
88
|
+
/** The callback that will clear the `AltDateWidget` when a button is clicked */
|
|
89
|
+
handleClear: (event: MouseEvent) => void;
|
|
90
|
+
/** The callback that will set the `AltDateWidget` to NOW when a button is clicked */
|
|
91
|
+
handleSetNow: (event: MouseEvent) => void;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/** Hook which encapsulates the logic needed to render an `AltDateWidget` with optional `time` elements. It contains
|
|
95
|
+
* the `state` of the current date(/time) selections in the widget. It returns a `UseAltDateWidgetResult` object
|
|
96
|
+
* that contains the `elements: DateElementProp[]` and three callbacks needed to change one of the rendered `elements`,
|
|
97
|
+
* and to handle the clicking of the `clear` and `setNow` buttons.
|
|
98
|
+
*
|
|
99
|
+
* @param props - The `WidgetProps` for the `AltDateWidget`
|
|
100
|
+
*/
|
|
101
|
+
export default function useAltDateWidgetProps<
|
|
102
|
+
T = any,
|
|
103
|
+
S extends StrictRJSFSchema = RJSFSchema,
|
|
104
|
+
F extends FormContextType = any,
|
|
105
|
+
>(props: WidgetProps<T, S, F>): UseAltDateWidgetResult {
|
|
106
|
+
const { time = false, disabled = false, readonly = false, options, onChange, value } = props;
|
|
107
|
+
const [state, setState] = useState(parseDateString(value, time));
|
|
108
|
+
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
setState(parseDateString(value, time));
|
|
111
|
+
}, [time, value]);
|
|
112
|
+
|
|
113
|
+
const handleChange = useCallback(
|
|
114
|
+
(property: keyof DateObject, value?: string) => {
|
|
115
|
+
const nextState = {
|
|
116
|
+
...state,
|
|
117
|
+
[property]: typeof value === 'undefined' ? -1 : value,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
if (readyForChange(nextState)) {
|
|
121
|
+
onChange(toDateString(nextState, time));
|
|
122
|
+
} else {
|
|
123
|
+
setState(nextState);
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
[state, onChange, time],
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const handleClear = useCallback(
|
|
130
|
+
(event: MouseEvent) => {
|
|
131
|
+
event.preventDefault();
|
|
132
|
+
if (disabled || readonly) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
onChange(undefined);
|
|
136
|
+
},
|
|
137
|
+
[disabled, readonly, onChange],
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
const handleSetNow = useCallback(
|
|
141
|
+
(event: MouseEvent) => {
|
|
142
|
+
event.preventDefault();
|
|
143
|
+
if (disabled || readonly) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const nextState = parseDateString(new Date().toJSON(), time);
|
|
147
|
+
onChange(toDateString(nextState, time));
|
|
148
|
+
},
|
|
149
|
+
[disabled, readonly, time, onChange],
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
const elements = useMemo(
|
|
153
|
+
() =>
|
|
154
|
+
getDateElementProps(
|
|
155
|
+
state,
|
|
156
|
+
time,
|
|
157
|
+
options.yearsRange as [number, number] | undefined,
|
|
158
|
+
options.format as DateElementFormat | undefined,
|
|
159
|
+
),
|
|
160
|
+
[state, time, options.yearsRange, options.format],
|
|
161
|
+
);
|
|
162
|
+
return { elements, handleChange, handleClear, handleSetNow };
|
|
163
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useRef } from 'react';
|
|
2
|
+
import isEqual from 'lodash/isEqual';
|
|
3
|
+
|
|
4
|
+
/** Hook that stores and returns a `T` value. If `newValue` is the same as the stored one, then the stored one is
|
|
5
|
+
* returned to avoid having a component rerender due it being a different object. Otherwise, the `newValue` is stored
|
|
6
|
+
* and returned.
|
|
7
|
+
*
|
|
8
|
+
* @param newValue - The potential new `T` value
|
|
9
|
+
* @returns - The latest stored `T` value
|
|
10
|
+
*/
|
|
11
|
+
export default function useDeepCompareMemo<T = unknown>(newValue: T): T {
|
|
12
|
+
const valueRef = useRef<T>(newValue);
|
|
13
|
+
if (!isEqual(newValue, valueRef.current)) {
|
|
14
|
+
valueRef.current = newValue;
|
|
15
|
+
}
|
|
16
|
+
return valueRef.current;
|
|
17
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { useCallback, useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import dataURItoBlob from './dataURItoBlob';
|
|
4
|
+
|
|
5
|
+
/** The information about files used by a FileWidget */
|
|
6
|
+
export type FileInfoType = {
|
|
7
|
+
/** The url of the data containing the file */
|
|
8
|
+
dataURL?: string | null;
|
|
9
|
+
/** The name of the file */
|
|
10
|
+
name: string;
|
|
11
|
+
/** The size of the file */
|
|
12
|
+
size: number;
|
|
13
|
+
/** The type of the file */
|
|
14
|
+
type: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export interface UseFileWidgetPropsResult {
|
|
18
|
+
/** The list of FileInfoType contained within the FileWidget */
|
|
19
|
+
filesInfo: FileInfoType[];
|
|
20
|
+
/** The callback handler to pass to the onChange of the input */
|
|
21
|
+
handleChange: (files: FileList) => void;
|
|
22
|
+
/** The callback handler to pass in order to delete a file */
|
|
23
|
+
handleRemove: (index: number) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Updated the given `dataUrl` to add the `name` to it
|
|
27
|
+
*
|
|
28
|
+
* @param dataURL - The url description string
|
|
29
|
+
* @param name - The name of the file to add to the dataUrl
|
|
30
|
+
* @returns - The `dataUrl` updated to include the name
|
|
31
|
+
*/
|
|
32
|
+
function addNameToDataURL(dataURL: string, name: string) {
|
|
33
|
+
return dataURL.replace(';base64', `;name=${encodeURIComponent(name)};base64`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Returns a promise that will read the file from the browser and return it as the result of the promise.
|
|
37
|
+
*
|
|
38
|
+
* @param file - The `File` information to read
|
|
39
|
+
* @returns - A promise that resolves to the read file.
|
|
40
|
+
*/
|
|
41
|
+
function processFile(file: File): Promise<FileInfoType> {
|
|
42
|
+
const { name, size, type } = file;
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
const reader = new window.FileReader();
|
|
45
|
+
reader.onerror = reject;
|
|
46
|
+
reader.onload = (event) => {
|
|
47
|
+
if (typeof event.target?.result === 'string') {
|
|
48
|
+
resolve({
|
|
49
|
+
dataURL: addNameToDataURL(event.target.result, name),
|
|
50
|
+
name,
|
|
51
|
+
size,
|
|
52
|
+
type,
|
|
53
|
+
});
|
|
54
|
+
} else {
|
|
55
|
+
resolve({
|
|
56
|
+
dataURL: null,
|
|
57
|
+
name,
|
|
58
|
+
size,
|
|
59
|
+
type,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
reader.readAsDataURL(file);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Reads a list of files from the browser, returning the results of the promises of each individual file read.
|
|
68
|
+
*
|
|
69
|
+
* @param files - The list of files to read
|
|
70
|
+
* @returns - The list of read files
|
|
71
|
+
*/
|
|
72
|
+
function processFiles(files: FileList) {
|
|
73
|
+
return Promise.all(Array.from(files).map(processFile));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Extracts the file information from the data URLs
|
|
77
|
+
*
|
|
78
|
+
* @param dataURLs - The information about the files
|
|
79
|
+
* @returns - The list of `FileInfoType` objects extracted from the data urls
|
|
80
|
+
*/
|
|
81
|
+
function extractFileInfo(dataURLs: string[]): FileInfoType[] {
|
|
82
|
+
return dataURLs.reduce((acc, dataURL) => {
|
|
83
|
+
if (!dataURL) {
|
|
84
|
+
return acc;
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const { blob, name } = dataURItoBlob(dataURL);
|
|
88
|
+
return [
|
|
89
|
+
...acc,
|
|
90
|
+
{
|
|
91
|
+
dataURL,
|
|
92
|
+
name: name,
|
|
93
|
+
size: blob.size,
|
|
94
|
+
type: blob.type,
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
} catch {
|
|
98
|
+
// Invalid dataURI, so just ignore it.
|
|
99
|
+
return acc;
|
|
100
|
+
}
|
|
101
|
+
}, [] as FileInfoType[]);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Hook which encapsulates the logic needed to read and convert a `value` of `File` or `File[]` into the
|
|
105
|
+
* `filesInfo: FileInfoType[]` and the two callback implementations needed to change the list or to remove a
|
|
106
|
+
* `File` from the list. To be used by theme specific `FileWidget` implementations.
|
|
107
|
+
*
|
|
108
|
+
* @param value - The current value of the `FileWidget`
|
|
109
|
+
* @param onChange - The onChange handler for the `FileWidget`
|
|
110
|
+
* @param [multiple=false] - Flag indicating whether the control supports multiple selections
|
|
111
|
+
* @returns - The `UseFileWidgetPropsResult` to be used within a `FileWidget` implementation
|
|
112
|
+
*/
|
|
113
|
+
export default function useFileWidgetProps(
|
|
114
|
+
value: string | string[] | undefined | null,
|
|
115
|
+
onChange: (value?: string | null | (string | null)[]) => void,
|
|
116
|
+
multiple = false,
|
|
117
|
+
): UseFileWidgetPropsResult {
|
|
118
|
+
const values: (string | null)[] = useMemo(() => {
|
|
119
|
+
if (multiple && value) {
|
|
120
|
+
return Array.isArray(value) ? value : [value];
|
|
121
|
+
}
|
|
122
|
+
return [];
|
|
123
|
+
}, [value, multiple]);
|
|
124
|
+
const filesInfo = useMemo(
|
|
125
|
+
() => (Array.isArray(value) ? extractFileInfo(value) : extractFileInfo([value || ''])),
|
|
126
|
+
[value],
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const handleChange = useCallback(
|
|
130
|
+
(files: FileList) => {
|
|
131
|
+
processFiles(files).then((filesInfoEvent) => {
|
|
132
|
+
const newValue = filesInfoEvent.map((fileInfo) => fileInfo.dataURL || null);
|
|
133
|
+
if (multiple) {
|
|
134
|
+
onChange(values.concat(...newValue));
|
|
135
|
+
} else {
|
|
136
|
+
onChange(newValue[0]);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
},
|
|
140
|
+
[values, multiple, onChange],
|
|
141
|
+
);
|
|
142
|
+
const handleRemove = useCallback(
|
|
143
|
+
(index: number) => {
|
|
144
|
+
if (multiple) {
|
|
145
|
+
const newValue = values.filter((_, i: number) => i !== index);
|
|
146
|
+
onChange(newValue);
|
|
147
|
+
} else {
|
|
148
|
+
onChange(undefined);
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
[values, multiple, onChange],
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
return { filesInfo, handleChange, handleRemove };
|
|
155
|
+
}
|
|
@@ -11,11 +11,13 @@ import { ErrorSchema, ValidationData } from './types';
|
|
|
11
11
|
*
|
|
12
12
|
* @param validationData - The current `ValidationData` into which to merge the additional errors
|
|
13
13
|
* @param [additionalErrorSchema] - The optional additional set of errors in an `ErrorSchema`
|
|
14
|
+
* @param [preventDuplicates=false] - Optional flag, if true, will call `mergeObjects()` with `preventDuplicates`
|
|
14
15
|
* @returns - The `validationData` with the additional errors from `additionalErrorSchema` merged into it, if provided.
|
|
15
16
|
*/
|
|
16
17
|
export default function validationDataMerge<T = any>(
|
|
17
18
|
validationData: ValidationData<T>,
|
|
18
19
|
additionalErrorSchema?: ErrorSchema<T>,
|
|
20
|
+
preventDuplicates = false,
|
|
19
21
|
): ValidationData<T> {
|
|
20
22
|
if (!additionalErrorSchema) {
|
|
21
23
|
return validationData;
|
|
@@ -24,7 +26,11 @@ export default function validationDataMerge<T = any>(
|
|
|
24
26
|
let errors = toErrorList(additionalErrorSchema);
|
|
25
27
|
let errorSchema = additionalErrorSchema;
|
|
26
28
|
if (!isEmpty(oldErrorSchema)) {
|
|
27
|
-
errorSchema = mergeObjects(
|
|
29
|
+
errorSchema = mergeObjects(
|
|
30
|
+
oldErrorSchema,
|
|
31
|
+
additionalErrorSchema,
|
|
32
|
+
preventDuplicates ? 'preventDuplicates' : true,
|
|
33
|
+
) as ErrorSchema<T>;
|
|
28
34
|
errors = [...oldErrors].concat(errors);
|
|
29
35
|
}
|
|
30
36
|
return { errorSchema, errors };
|