@rjsf/utils 6.0.0-beta.12 → 6.0.0-beta.13
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 +95 -21
- package/dist/index.js.map +4 -4
- package/dist/utils.esm.js +95 -21
- package/dist/utils.esm.js.map +4 -4
- package/dist/utils.umd.js +93 -22
- package/lib/constants.d.ts +1 -0
- package/lib/constants.js +1 -0
- package/lib/constants.js.map +1 -1
- package/lib/createSchemaUtils.js +9 -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/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/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 +2 -0
- package/package.json +1 -1
- package/src/constants.ts +1 -0
- package/src/createSchemaUtils.ts +8 -1
- package/src/findSchemaDefinition.ts +51 -3
- package/src/index.ts +4 -0
- package/src/mergeDefaultsWithFormData.ts +1 -1
- package/src/schema/getDefaultFormState.ts +12 -2
- package/src/schema/retrieveSchema.ts +2 -2
- package/src/shallowEquals.ts +41 -0
- package/src/shouldRender.ts +27 -2
- package/src/types.ts +2 -0
|
@@ -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/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
|
{},
|
|
@@ -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
|
@@ -423,6 +423,8 @@ export interface Registry<T = any, S extends StrictRJSFSchema = RJSFSchema, F ex
|
|
|
423
423
|
translateString: (stringKey: TranslatableString, params?: string[]) => string;
|
|
424
424
|
/** The optional global UI Options that are available for all templates, fields and widgets to access */
|
|
425
425
|
globalUiOptions?: GlobalUISchemaOptions;
|
|
426
|
+
/** The component update strategy used by the Form and its fields for performance optimization */
|
|
427
|
+
experimental_componentUpdateStrategy?: 'customDeep' | 'shallow' | 'always';
|
|
426
428
|
}
|
|
427
429
|
|
|
428
430
|
/** The properties that are passed to a Field implementation */
|