@rjsf/utils 6.0.0-beta.12 → 6.0.0-beta.14
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 +100 -23
- package/dist/index.js.map +4 -4
- package/dist/utils.esm.js +100 -23
- package/dist/utils.esm.js.map +4 -4
- package/dist/utils.umd.js +98 -24
- package/lib/constants.d.ts +1 -0
- package/lib/constants.js +1 -0
- package/lib/constants.js.map +1 -1
- package/lib/createSchemaUtils.js +11 -1
- package/lib/createSchemaUtils.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/getUiOptions.js +4 -0
- package/lib/getUiOptions.js.map +1 -1
- package/lib/index.d.ts +3 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/mergeDefaultsWithFormData.js +2 -2
- package/lib/mergeDefaultsWithFormData.js.map +1 -1
- package/lib/schema/getDefaultFormState.js +10 -2
- 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/retrieveSchema.js +2 -2
- 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/tsconfig.tsbuildinfo +1 -1
- package/lib/types.d.ts +20 -3
- package/package.json +5 -5
- package/src/constants.ts +1 -0
- package/src/createSchemaUtils.ts +11 -1
- package/src/findSchemaDefinition.ts +51 -3
- package/src/getUiOptions.ts +4 -0
- package/src/index.ts +4 -0
- package/src/mergeDefaultsWithFormData.ts +1 -1
- package/src/schema/getDefaultFormState.ts +12 -2
- package/src/schema/getDisplayLabel.ts +2 -2
- package/src/schema/retrieveSchema.ts +2 -2
- package/src/shallowEquals.ts +41 -0
- package/src/shouldRender.ts +27 -2
- package/src/types.ts +23 -3
package/lib/types.d.ts
CHANGED
|
@@ -174,6 +174,8 @@ export type RJSFValidationError = {
|
|
|
174
174
|
schemaPath?: string;
|
|
175
175
|
/** Full error name, for example ".name is a required property" */
|
|
176
176
|
stack: string;
|
|
177
|
+
/** The title property for the failing field*/
|
|
178
|
+
title?: string;
|
|
177
179
|
};
|
|
178
180
|
/** The type that describes an error in a field */
|
|
179
181
|
export type FieldError = string;
|
|
@@ -358,6 +360,8 @@ export interface Registry<T = any, S extends StrictRJSFSchema = RJSFSchema, F ex
|
|
|
358
360
|
translateString: (stringKey: TranslatableString, params?: string[]) => string;
|
|
359
361
|
/** The optional global UI Options that are available for all templates, fields and widgets to access */
|
|
360
362
|
globalUiOptions?: GlobalUISchemaOptions;
|
|
363
|
+
/** The component update strategy used by the Form and its fields for performance optimization */
|
|
364
|
+
experimental_componentUpdateStrategy?: 'customDeep' | 'shallow' | 'always';
|
|
361
365
|
}
|
|
362
366
|
/** The properties that are passed to a Field implementation */
|
|
363
367
|
export interface FieldProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> extends GenericObjectType, RJSFBaseProps<T, S, F>, Pick<HTMLAttributes<HTMLElement>, Exclude<keyof HTMLAttributes<HTMLElement>, 'onBlur' | 'onFocus' | 'onChange'>> {
|
|
@@ -367,8 +371,10 @@ export interface FieldProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F
|
|
|
367
371
|
formData?: T;
|
|
368
372
|
/** The tree of errors for this field and its children */
|
|
369
373
|
errorSchema?: ErrorSchema<T>;
|
|
370
|
-
/** The field change event handler; called with the updated
|
|
371
|
-
|
|
374
|
+
/** The field change event handler; called with the updated field value, the optional change path for the value
|
|
375
|
+
* (defaults to an empty array), an optional ErrorSchema and the optional id of the field being changed
|
|
376
|
+
*/
|
|
377
|
+
onChange: (newValue: T | undefined, path?: (number | string)[], es?: ErrorSchema<T>, id?: string) => void;
|
|
372
378
|
/** The input blur event handler; call it with the field id and value */
|
|
373
379
|
onBlur: (id: string, value: any) => void;
|
|
374
380
|
/** The input focus event handler; call it with the field id and value */
|
|
@@ -637,7 +643,7 @@ export interface MultiSchemaFieldTemplateProps<T = any, S extends StrictRJSFSche
|
|
|
637
643
|
optionSchemaField: ReactNode;
|
|
638
644
|
}
|
|
639
645
|
/** The properties that are passed to a Widget implementation */
|
|
640
|
-
export interface WidgetProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> extends GenericObjectType, RJSFBaseProps<T, S, F>, Pick<HTMLAttributes<HTMLElement>, Exclude<keyof HTMLAttributes<HTMLElement>, 'onBlur' | 'onFocus'>> {
|
|
646
|
+
export interface WidgetProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> extends GenericObjectType, RJSFBaseProps<T, S, F>, Pick<HTMLAttributes<HTMLElement>, Exclude<keyof HTMLAttributes<HTMLElement>, 'onBlur' | 'onFocus' | 'onChange'>> {
|
|
641
647
|
/** The generated id for this widget, used to provide unique `name`s and `id`s for the HTML field elements rendered by
|
|
642
648
|
* widgets
|
|
643
649
|
*/
|
|
@@ -801,6 +807,12 @@ export type UIOptionsType<T = any, S extends StrictRJSFSchema = RJSFSchema, F ex
|
|
|
801
807
|
/** Anything else will be one of these types */
|
|
802
808
|
[key: string]: boolean | number | string | object | any[] | null | undefined;
|
|
803
809
|
};
|
|
810
|
+
/**
|
|
811
|
+
* A utility type that extracts the element type from an array type.
|
|
812
|
+
* If the type is not an array, it returns the type itself as a safe fallback.
|
|
813
|
+
* Handles both standard arrays and readonly arrays.
|
|
814
|
+
*/
|
|
815
|
+
export type ArrayElement<A> = A extends readonly (infer E)[] ? E : A;
|
|
804
816
|
/** Type describing the well-known properties of the `UiSchema` while also supporting all user defined properties,
|
|
805
817
|
* starting with `ui:`.
|
|
806
818
|
*/
|
|
@@ -821,6 +833,11 @@ export type UiSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends
|
|
|
821
833
|
'ui:fieldReplacesAnyOrOneOf'?: boolean;
|
|
822
834
|
/** An object that contains all the potential UI options in a single object */
|
|
823
835
|
'ui:options'?: UIOptionsType<T, S, F>;
|
|
836
|
+
/** The uiSchema for items in an array. Can be an object for a uniform uiSchema across all items (current behavior),
|
|
837
|
+
* or a function that returns a dynamic uiSchema based on the item's data and index.
|
|
838
|
+
* When using a function, it receives the item data, index, and optionally the form context as parameters.
|
|
839
|
+
*/
|
|
840
|
+
items?: UiSchema<ArrayElement<T>, S, F> | ((itemData: ArrayElement<T>, index: number, formContext?: F) => UiSchema<ArrayElement<T>, S, F>);
|
|
824
841
|
};
|
|
825
842
|
/** A `CustomValidator` function takes in a `formData`, `errors` and `uiSchema` objects and returns the given `errors`
|
|
826
843
|
* object back, while potentially adding additional messages to the `errors`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rjsf/utils",
|
|
3
|
-
"version": "6.0.0-beta.
|
|
3
|
+
"version": "6.0.0-beta.14",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"module": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
@@ -71,14 +71,14 @@
|
|
|
71
71
|
"lodash": "^4.17.21",
|
|
72
72
|
"lodash-es": "^4.17.21",
|
|
73
73
|
"nanoid": "^5.1.5",
|
|
74
|
-
"react-is": "^18.
|
|
74
|
+
"react-is": "^18.3.1"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
77
|
"@types/json-schema": "^7.0.15",
|
|
78
78
|
"@types/json-schema-merge-allof": "^0.6.5",
|
|
79
|
-
"@types/react-is": "^18.
|
|
80
|
-
"deep-freeze-es6": "^
|
|
81
|
-
"eslint": "^8.
|
|
79
|
+
"@types/react-is": "^18.3.1",
|
|
80
|
+
"deep-freeze-es6": "^4.0.1",
|
|
81
|
+
"eslint": "^8.57.1"
|
|
82
82
|
},
|
|
83
83
|
"publishConfig": {
|
|
84
84
|
"access": "public"
|
package/src/constants.ts
CHANGED
|
@@ -46,4 +46,5 @@ export const UI_GLOBAL_OPTIONS_KEY = 'ui:globalOptions';
|
|
|
46
46
|
|
|
47
47
|
/** The JSON Schema version strings
|
|
48
48
|
*/
|
|
49
|
+
export const JSON_SCHEMA_DRAFT_2019_09 = 'https://json-schema.org/draft/2019-09/schema';
|
|
49
50
|
export const JSON_SCHEMA_DRAFT_2020_12 = 'https://json-schema.org/draft/2020-12/schema';
|
package/src/createSchemaUtils.ts
CHANGED
|
@@ -29,6 +29,9 @@ import {
|
|
|
29
29
|
toIdSchema,
|
|
30
30
|
toPathSchema,
|
|
31
31
|
} from './schema';
|
|
32
|
+
import { makeAllReferencesAbsolute } from './findSchemaDefinition';
|
|
33
|
+
import { ID_KEY, JSON_SCHEMA_DRAFT_2020_12, SCHEMA_KEY } from './constants';
|
|
34
|
+
import get from 'lodash/get';
|
|
32
35
|
|
|
33
36
|
/** The `SchemaUtils` class provides a wrapper around the publicly exported APIs in the `utils/schema` directory such
|
|
34
37
|
* that one does not have to explicitly pass the `validator`, `rootSchema`, `experimental_defaultFormStateBehavior` or
|
|
@@ -57,7 +60,11 @@ class SchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
|
|
|
57
60
|
experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior,
|
|
58
61
|
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>,
|
|
59
62
|
) {
|
|
60
|
-
|
|
63
|
+
if (rootSchema && rootSchema[SCHEMA_KEY] === JSON_SCHEMA_DRAFT_2020_12) {
|
|
64
|
+
this.rootSchema = makeAllReferencesAbsolute(rootSchema, get(rootSchema, ID_KEY, '#'));
|
|
65
|
+
} else {
|
|
66
|
+
this.rootSchema = rootSchema;
|
|
67
|
+
}
|
|
61
68
|
this.validator = validator;
|
|
62
69
|
this.experimental_defaultFormStateBehavior = experimental_defaultFormStateBehavior;
|
|
63
70
|
this.experimental_customMergeAllOf = experimental_customMergeAllOf;
|
|
@@ -95,9 +102,12 @@ class SchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
|
|
|
95
102
|
experimental_defaultFormStateBehavior = {},
|
|
96
103
|
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>,
|
|
97
104
|
): boolean {
|
|
105
|
+
// If either validator or rootSchema are falsy, return false to prevent the creation
|
|
106
|
+
// of a new SchemaUtilsType with incomplete properties.
|
|
98
107
|
if (!validator || !rootSchema) {
|
|
99
108
|
return false;
|
|
100
109
|
}
|
|
110
|
+
|
|
101
111
|
return (
|
|
102
112
|
this.validator !== validator ||
|
|
103
113
|
!deepEquals(this.rootSchema, rootSchema) ||
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import jsonpointer from 'jsonpointer';
|
|
2
2
|
import omit from 'lodash/omit';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
ALL_OF_KEY,
|
|
6
|
+
ID_KEY,
|
|
7
|
+
JSON_SCHEMA_DRAFT_2019_09,
|
|
8
|
+
JSON_SCHEMA_DRAFT_2020_12,
|
|
9
|
+
REF_KEY,
|
|
10
|
+
SCHEMA_KEY,
|
|
11
|
+
} from './constants';
|
|
5
12
|
import { GenericObjectType, RJSFSchema, StrictRJSFSchema } from './types';
|
|
6
13
|
import isObject from 'lodash/isObject';
|
|
7
14
|
import isEmpty from 'lodash/isEmpty';
|
|
@@ -20,7 +27,16 @@ function findEmbeddedSchemaRecursive<S extends StrictRJSFSchema = RJSFSchema>(sc
|
|
|
20
27
|
return schema;
|
|
21
28
|
}
|
|
22
29
|
for (const subSchema of Object.values(schema)) {
|
|
23
|
-
if (
|
|
30
|
+
if (Array.isArray(subSchema)) {
|
|
31
|
+
for (const item of subSchema) {
|
|
32
|
+
if (isObject(item)) {
|
|
33
|
+
const result = findEmbeddedSchemaRecursive<S>(item as S, ref);
|
|
34
|
+
if (result !== undefined) {
|
|
35
|
+
return result as S;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
} else if (isObject(subSchema)) {
|
|
24
40
|
const result = findEmbeddedSchemaRecursive<S>(subSchema as S, ref);
|
|
25
41
|
if (result !== undefined) {
|
|
26
42
|
return result as S;
|
|
@@ -30,6 +46,31 @@ function findEmbeddedSchemaRecursive<S extends StrictRJSFSchema = RJSFSchema>(sc
|
|
|
30
46
|
return undefined;
|
|
31
47
|
}
|
|
32
48
|
|
|
49
|
+
/** Parses a JSONSchema and makes all references absolute with respect to
|
|
50
|
+
* the `baseURI` argument
|
|
51
|
+
* @param schema - The schema to be processed
|
|
52
|
+
* @param baseURI - The base URI to be used for resolving relative references
|
|
53
|
+
*/
|
|
54
|
+
export function makeAllReferencesAbsolute<S extends StrictRJSFSchema = RJSFSchema>(schema: S, baseURI: string): S {
|
|
55
|
+
const currentURI = get(schema, ID_KEY, baseURI);
|
|
56
|
+
// Make all other references absolute
|
|
57
|
+
if (REF_KEY in schema) {
|
|
58
|
+
schema = { ...schema, [REF_KEY]: UriResolver.resolve(currentURI, schema[REF_KEY]!) };
|
|
59
|
+
}
|
|
60
|
+
// Look for references in nested subschemas
|
|
61
|
+
for (const [key, subSchema] of Object.entries(schema)) {
|
|
62
|
+
if (Array.isArray(subSchema)) {
|
|
63
|
+
schema = {
|
|
64
|
+
...schema,
|
|
65
|
+
[key]: subSchema.map((item) => (isObject(item) ? makeAllReferencesAbsolute(item as S, currentURI) : item)),
|
|
66
|
+
};
|
|
67
|
+
} else if (isObject(subSchema)) {
|
|
68
|
+
schema = { ...schema, [key]: makeAllReferencesAbsolute(subSchema as S, currentURI) };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return schema;
|
|
72
|
+
}
|
|
73
|
+
|
|
33
74
|
/** Splits out the value at the `key` in `object` from the `object`, returning an array that contains in the first
|
|
34
75
|
* location, the `object` minus the `key: value` and in the second location the `value`.
|
|
35
76
|
*
|
|
@@ -103,7 +144,14 @@ export function findSchemaDefinitionRecursive<S extends StrictRJSFSchema = RJSFS
|
|
|
103
144
|
const [remaining, theRef] = splitKeyElementFromObject(REF_KEY, current);
|
|
104
145
|
const subSchema = findSchemaDefinitionRecursive<S>(theRef, rootSchema, [...recurseList, ref], baseURI);
|
|
105
146
|
if (Object.keys(remaining).length > 0) {
|
|
106
|
-
|
|
147
|
+
if (
|
|
148
|
+
rootSchema[SCHEMA_KEY] === JSON_SCHEMA_DRAFT_2019_09 ||
|
|
149
|
+
rootSchema[SCHEMA_KEY] === JSON_SCHEMA_DRAFT_2020_12
|
|
150
|
+
) {
|
|
151
|
+
return { [ALL_OF_KEY]: [remaining, subSchema] } as S;
|
|
152
|
+
} else {
|
|
153
|
+
return { ...remaining, ...subSchema };
|
|
154
|
+
}
|
|
107
155
|
}
|
|
108
156
|
return subSchema;
|
|
109
157
|
}
|
package/src/getUiOptions.ts
CHANGED
|
@@ -13,6 +13,10 @@ export default function getUiOptions<T = any, S extends StrictRJSFSchema = RJSFS
|
|
|
13
13
|
uiSchema: UiSchema<T, S, F> = {},
|
|
14
14
|
globalOptions: GlobalUISchemaOptions = {},
|
|
15
15
|
): UIOptionsType<T, S, F> {
|
|
16
|
+
// Handle null or undefined uiSchema
|
|
17
|
+
if (!uiSchema) {
|
|
18
|
+
return { ...globalOptions };
|
|
19
|
+
}
|
|
16
20
|
return Object.keys(uiSchema)
|
|
17
21
|
.filter((key) => key.indexOf('ui:') === 0)
|
|
18
22
|
.reduce(
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import createSchemaUtils from './createSchemaUtils';
|
|
|
6
6
|
import dataURItoBlob from './dataURItoBlob';
|
|
7
7
|
import dateRangeOptions from './dateRangeOptions';
|
|
8
8
|
import deepEquals from './deepEquals';
|
|
9
|
+
import shallowEquals from './shallowEquals';
|
|
9
10
|
import englishStringTranslator from './englishStringTranslator';
|
|
10
11
|
import enumOptionsDeselectValue from './enumOptionsDeselectValue';
|
|
11
12
|
import enumOptionsIndexForValue from './enumOptionsIndexForValue';
|
|
@@ -130,6 +131,7 @@ export {
|
|
|
130
131
|
rangeSpec,
|
|
131
132
|
replaceStringParameters,
|
|
132
133
|
schemaRequiresTrueValue,
|
|
134
|
+
shallowEquals,
|
|
133
135
|
shouldRender,
|
|
134
136
|
sortedJSONStringify,
|
|
135
137
|
titleId,
|
|
@@ -142,3 +144,5 @@ export {
|
|
|
142
144
|
validationDataMerge,
|
|
143
145
|
withIdRefPrefix,
|
|
144
146
|
};
|
|
147
|
+
|
|
148
|
+
export type { ComponentUpdateStrategy } from './shouldRender';
|
|
@@ -115,10 +115,18 @@ function maybeAddDefaultToObject<T = any>(
|
|
|
115
115
|
isConst = false,
|
|
116
116
|
) {
|
|
117
117
|
const { emptyObjectFields = 'populateAllDefaults' } = experimental_defaultFormStateBehavior;
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
|
|
119
|
+
if (includeUndefinedValues === true || isConst) {
|
|
120
|
+
// If includeUndefinedValues is explicitly true
|
|
120
121
|
// Or if the schema has a const property defined, then we should always return the computedDefault since it's coming from the const.
|
|
121
122
|
obj[key] = computedDefault;
|
|
123
|
+
} else if (includeUndefinedValues === 'excludeObjectChildren') {
|
|
124
|
+
// Fix for Issue #4709: When in 'excludeObjectChildren' mode, don't set primitive fields to empty objects
|
|
125
|
+
// Only add the computed default if it's not an empty object placeholder for a primitive field
|
|
126
|
+
if (!isObject(computedDefault) || !isEmpty(computedDefault)) {
|
|
127
|
+
obj[key] = computedDefault;
|
|
128
|
+
}
|
|
129
|
+
// If computedDefault is an empty object {}, don't add it - let the field stay undefined
|
|
122
130
|
} else if (emptyObjectFields !== 'skipDefaults') {
|
|
123
131
|
// If isParentRequired is undefined, then we are at the root level of the schema so defer to the requiredness of
|
|
124
132
|
// the field key itself in the `requiredField` list
|
|
@@ -473,6 +481,7 @@ export function getObjectDefaults<T = any, S extends StrictRJSFSchema = RJSFSche
|
|
|
473
481
|
required: retrievedSchema.required?.includes(key),
|
|
474
482
|
shouldMergeDefaultsIntoFormData,
|
|
475
483
|
});
|
|
484
|
+
|
|
476
485
|
maybeAddDefaultToObject<T>(
|
|
477
486
|
acc,
|
|
478
487
|
key,
|
|
@@ -483,6 +492,7 @@ export function getObjectDefaults<T = any, S extends StrictRJSFSchema = RJSFSche
|
|
|
483
492
|
experimental_defaultFormStateBehavior,
|
|
484
493
|
hasConst,
|
|
485
494
|
);
|
|
495
|
+
|
|
486
496
|
return acc;
|
|
487
497
|
},
|
|
488
498
|
{},
|
|
@@ -52,10 +52,10 @@ export default function getDisplayLabel<
|
|
|
52
52
|
if (schemaType === 'object') {
|
|
53
53
|
displayLabel = false;
|
|
54
54
|
}
|
|
55
|
-
if (schemaType === 'boolean' && !uiSchema[UI_WIDGET_KEY]) {
|
|
55
|
+
if (schemaType === 'boolean' && uiSchema && !uiSchema[UI_WIDGET_KEY]) {
|
|
56
56
|
displayLabel = false;
|
|
57
57
|
}
|
|
58
|
-
if (uiSchema[UI_FIELD_KEY]) {
|
|
58
|
+
if (uiSchema && uiSchema[UI_FIELD_KEY]) {
|
|
59
59
|
displayLabel = false;
|
|
60
60
|
}
|
|
61
61
|
return displayLabel;
|
|
@@ -430,7 +430,7 @@ export function stubExistingAdditionalProperties<
|
|
|
430
430
|
if (!isEmpty(matchingProperties)) {
|
|
431
431
|
schema.properties[key] = retrieveSchema<T, S, F>(
|
|
432
432
|
validator,
|
|
433
|
-
{
|
|
433
|
+
{ [ALL_OF_KEY]: Object.values(matchingProperties) } as S,
|
|
434
434
|
rootSchema,
|
|
435
435
|
get(formData, [key]) as T,
|
|
436
436
|
experimental_customMergeAllOf,
|
|
@@ -445,7 +445,7 @@ export function stubExistingAdditionalProperties<
|
|
|
445
445
|
if (REF_KEY in schema.additionalProperties!) {
|
|
446
446
|
additionalProperties = retrieveSchema<T, S, F>(
|
|
447
447
|
validator,
|
|
448
|
-
{
|
|
448
|
+
{ [REF_KEY]: get(schema.additionalProperties, [REF_KEY]) } as S,
|
|
449
449
|
rootSchema,
|
|
450
450
|
formData as T,
|
|
451
451
|
experimental_customMergeAllOf,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** Implements a shallow equals comparison that uses Object.is() for comparing values.
|
|
2
|
+
* This function compares objects by checking if all keys and their values are equal using Object.is().
|
|
3
|
+
*
|
|
4
|
+
* @param a - The first element to compare
|
|
5
|
+
* @param b - The second element to compare
|
|
6
|
+
* @returns - True if the `a` and `b` are shallow equal, false otherwise
|
|
7
|
+
*/
|
|
8
|
+
export default function shallowEquals(a: any, b: any): boolean {
|
|
9
|
+
// If they're the same reference, they're equal
|
|
10
|
+
if (Object.is(a, b)) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// If either is null or undefined, they're not equal (since we know they're not the same reference)
|
|
15
|
+
if (a == null || b == null) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// If they're not objects, they're not equal (since Object.is already checked)
|
|
20
|
+
if (typeof a !== 'object' || typeof b !== 'object') {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const keysA = Object.keys(a);
|
|
25
|
+
const keysB = Object.keys(b);
|
|
26
|
+
|
|
27
|
+
// Different number of keys means not equal
|
|
28
|
+
if (keysA.length !== keysB.length) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Check if all keys and values are equal
|
|
33
|
+
for (let i = 0; i < keysA.length; i++) {
|
|
34
|
+
const key = keysA[i];
|
|
35
|
+
if (!Object.prototype.hasOwnProperty.call(b, key) || !Object.is(a[key], b[key])) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return true;
|
|
41
|
+
}
|
package/src/shouldRender.ts
CHANGED
|
@@ -1,16 +1,41 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
3
|
import deepEquals from './deepEquals';
|
|
4
|
+
import shallowEquals from './shallowEquals';
|
|
5
|
+
|
|
6
|
+
/** The supported component update strategies */
|
|
7
|
+
export type ComponentUpdateStrategy = 'customDeep' | 'shallow' | 'always';
|
|
4
8
|
|
|
5
9
|
/** Determines whether the given `component` should be rerendered by comparing its current set of props and state
|
|
6
|
-
* against the next set.
|
|
10
|
+
* against the next set. The comparison strategy can be controlled via the `updateStrategy` parameter.
|
|
7
11
|
*
|
|
8
12
|
* @param component - A React component being checked
|
|
9
13
|
* @param nextProps - The next set of props against which to check
|
|
10
14
|
* @param nextState - The next set of state against which to check
|
|
15
|
+
* @param updateStrategy - The strategy to use for comparison:
|
|
16
|
+
* - 'customDeep': Uses RJSF's custom deep equality checks (default)
|
|
17
|
+
* - 'shallow': Uses shallow comparison of props and state
|
|
18
|
+
* - 'always': Always returns true (React's default behavior)
|
|
11
19
|
* @returns - True if the component should be re-rendered, false otherwise
|
|
12
20
|
*/
|
|
13
|
-
export default function shouldRender(
|
|
21
|
+
export default function shouldRender(
|
|
22
|
+
component: React.Component,
|
|
23
|
+
nextProps: any,
|
|
24
|
+
nextState: any,
|
|
25
|
+
updateStrategy: ComponentUpdateStrategy = 'customDeep',
|
|
26
|
+
) {
|
|
27
|
+
if (updateStrategy === 'always') {
|
|
28
|
+
// Use React's default behavior: always update if state or props change (no shouldComponentUpdate optimization)
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (updateStrategy === 'shallow') {
|
|
33
|
+
// Use shallow comparison for props and state
|
|
34
|
+
const { props, state } = component;
|
|
35
|
+
return !shallowEquals(props, nextProps) || !shallowEquals(state, nextState);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Use custom deep equality checks (default 'customDeep' strategy)
|
|
14
39
|
const { props, state } = component;
|
|
15
40
|
return !deepEquals(props, nextProps) || !deepEquals(state, nextState);
|
|
16
41
|
}
|
package/src/types.ts
CHANGED
|
@@ -212,6 +212,8 @@ export type RJSFValidationError = {
|
|
|
212
212
|
schemaPath?: string;
|
|
213
213
|
/** Full error name, for example ".name is a required property" */
|
|
214
214
|
stack: string;
|
|
215
|
+
/** The title property for the failing field*/
|
|
216
|
+
title?: string;
|
|
215
217
|
};
|
|
216
218
|
|
|
217
219
|
/** The type that describes an error in a field */
|
|
@@ -423,6 +425,8 @@ export interface Registry<T = any, S extends StrictRJSFSchema = RJSFSchema, F ex
|
|
|
423
425
|
translateString: (stringKey: TranslatableString, params?: string[]) => string;
|
|
424
426
|
/** The optional global UI Options that are available for all templates, fields and widgets to access */
|
|
425
427
|
globalUiOptions?: GlobalUISchemaOptions;
|
|
428
|
+
/** The component update strategy used by the Form and its fields for performance optimization */
|
|
429
|
+
experimental_componentUpdateStrategy?: 'customDeep' | 'shallow' | 'always';
|
|
426
430
|
}
|
|
427
431
|
|
|
428
432
|
/** The properties that are passed to a Field implementation */
|
|
@@ -436,8 +440,10 @@ export interface FieldProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F
|
|
|
436
440
|
formData?: T;
|
|
437
441
|
/** The tree of errors for this field and its children */
|
|
438
442
|
errorSchema?: ErrorSchema<T>;
|
|
439
|
-
/** The field change event handler; called with the updated
|
|
440
|
-
|
|
443
|
+
/** The field change event handler; called with the updated field value, the optional change path for the value
|
|
444
|
+
* (defaults to an empty array), an optional ErrorSchema and the optional id of the field being changed
|
|
445
|
+
*/
|
|
446
|
+
onChange: (newValue: T | undefined, path?: (number | string)[], es?: ErrorSchema<T>, id?: string) => void;
|
|
441
447
|
/** The input blur event handler; call it with the field id and value */
|
|
442
448
|
onBlur: (id: string, value: any) => void;
|
|
443
449
|
/** The input focus event handler; call it with the field id and value */
|
|
@@ -795,7 +801,7 @@ export interface MultiSchemaFieldTemplateProps<
|
|
|
795
801
|
export interface WidgetProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>
|
|
796
802
|
extends GenericObjectType,
|
|
797
803
|
RJSFBaseProps<T, S, F>,
|
|
798
|
-
Pick<HTMLAttributes<HTMLElement>, Exclude<keyof HTMLAttributes<HTMLElement>, 'onBlur' | 'onFocus'>> {
|
|
804
|
+
Pick<HTMLAttributes<HTMLElement>, Exclude<keyof HTMLAttributes<HTMLElement>, 'onBlur' | 'onFocus' | 'onChange'>> {
|
|
799
805
|
/** The generated id for this widget, used to provide unique `name`s and `id`s for the HTML field elements rendered by
|
|
800
806
|
* widgets
|
|
801
807
|
*/
|
|
@@ -1003,6 +1009,13 @@ export type UIOptionsType<
|
|
|
1003
1009
|
[key: string]: boolean | number | string | object | any[] | null | undefined;
|
|
1004
1010
|
};
|
|
1005
1011
|
|
|
1012
|
+
/**
|
|
1013
|
+
* A utility type that extracts the element type from an array type.
|
|
1014
|
+
* If the type is not an array, it returns the type itself as a safe fallback.
|
|
1015
|
+
* Handles both standard arrays and readonly arrays.
|
|
1016
|
+
*/
|
|
1017
|
+
export type ArrayElement<A> = A extends readonly (infer E)[] ? E : A;
|
|
1018
|
+
|
|
1006
1019
|
/** Type describing the well-known properties of the `UiSchema` while also supporting all user defined properties,
|
|
1007
1020
|
* starting with `ui:`.
|
|
1008
1021
|
*/
|
|
@@ -1028,6 +1041,13 @@ export type UiSchema<
|
|
|
1028
1041
|
'ui:fieldReplacesAnyOrOneOf'?: boolean;
|
|
1029
1042
|
/** An object that contains all the potential UI options in a single object */
|
|
1030
1043
|
'ui:options'?: UIOptionsType<T, S, F>;
|
|
1044
|
+
/** The uiSchema for items in an array. Can be an object for a uniform uiSchema across all items (current behavior),
|
|
1045
|
+
* or a function that returns a dynamic uiSchema based on the item's data and index.
|
|
1046
|
+
* When using a function, it receives the item data, index, and optionally the form context as parameters.
|
|
1047
|
+
*/
|
|
1048
|
+
items?:
|
|
1049
|
+
| UiSchema<ArrayElement<T>, S, F>
|
|
1050
|
+
| ((itemData: ArrayElement<T>, index: number, formContext?: F) => UiSchema<ArrayElement<T>, S, F>);
|
|
1031
1051
|
};
|
|
1032
1052
|
|
|
1033
1053
|
/** A `CustomValidator` function takes in a `formData`, `errors` and `uiSchema` objects and returns the given `errors`
|