@rjsf/core 6.6.2 → 6.7.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/LICENSE.md +201 -0
- package/dist/core.umd.js +312 -252
- package/dist/index.cjs +947 -878
- package/dist/index.cjs.map +4 -4
- package/dist/index.esm.js +741 -667
- package/dist/index.esm.js.map +4 -4
- package/lib/components/Form.d.ts +2 -2
- package/lib/components/Form.d.ts.map +1 -1
- package/lib/components/Form.js +30 -15
- package/lib/components/RichDescription.d.ts +1 -1
- package/lib/components/RichDescription.d.ts.map +1 -1
- package/lib/components/RichHelp.d.ts +1 -1
- package/lib/components/RichHelp.d.ts.map +1 -1
- package/lib/components/SchemaExamples.d.ts +1 -1
- package/lib/components/SchemaExamples.d.ts.map +1 -1
- package/lib/components/fields/ArrayField.d.ts +1 -1
- package/lib/components/fields/ArrayField.d.ts.map +1 -1
- package/lib/components/fields/BooleanField.d.ts +1 -1
- package/lib/components/fields/BooleanField.d.ts.map +1 -1
- package/lib/components/fields/CyclicSchemaField.d.ts +8 -0
- package/lib/components/fields/CyclicSchemaField.d.ts.map +1 -0
- package/lib/components/fields/CyclicSchemaField.js +21 -0
- package/lib/components/fields/FallbackField.d.ts +1 -1
- package/lib/components/fields/FallbackField.d.ts.map +1 -1
- package/lib/components/fields/LayoutGridField.d.ts +1 -1
- package/lib/components/fields/LayoutGridField.d.ts.map +1 -1
- package/lib/components/fields/LayoutHeaderField.d.ts +1 -1
- package/lib/components/fields/LayoutHeaderField.d.ts.map +1 -1
- package/lib/components/fields/LayoutMultiSchemaField.d.ts +1 -1
- package/lib/components/fields/LayoutMultiSchemaField.d.ts.map +1 -1
- package/lib/components/fields/MultiSchemaField.d.ts +1 -46
- package/lib/components/fields/MultiSchemaField.d.ts.map +1 -1
- package/lib/components/fields/MultiSchemaField.js +111 -135
- package/lib/components/fields/NumberField.d.ts +1 -1
- package/lib/components/fields/NumberField.d.ts.map +1 -1
- package/lib/components/fields/NumberField.js +26 -15
- package/lib/components/fields/ObjectField.d.ts +1 -1
- package/lib/components/fields/ObjectField.d.ts.map +1 -1
- package/lib/components/fields/ObjectField.js +20 -5
- package/lib/components/fields/OptionalDataControlsField.d.ts +1 -1
- package/lib/components/fields/OptionalDataControlsField.d.ts.map +1 -1
- package/lib/components/fields/SchemaField.d.ts +13 -7
- package/lib/components/fields/SchemaField.d.ts.map +1 -1
- package/lib/components/fields/SchemaField.js +37 -24
- package/lib/components/fields/StringField.d.ts +1 -1
- package/lib/components/fields/StringField.d.ts.map +1 -1
- package/lib/components/fields/index.d.ts.map +1 -1
- package/lib/components/fields/index.js +2 -0
- package/lib/components/templates/ArrayFieldDescriptionTemplate.d.ts +1 -1
- package/lib/components/templates/ArrayFieldDescriptionTemplate.d.ts.map +1 -1
- package/lib/components/templates/ArrayFieldItemButtonsTemplate.d.ts +1 -1
- package/lib/components/templates/ArrayFieldItemButtonsTemplate.d.ts.map +1 -1
- package/lib/components/templates/ArrayFieldItemTemplate.d.ts +1 -1
- package/lib/components/templates/ArrayFieldItemTemplate.d.ts.map +1 -1
- package/lib/components/templates/ArrayFieldTemplate.d.ts +1 -1
- package/lib/components/templates/ArrayFieldTemplate.d.ts.map +1 -1
- package/lib/components/templates/ArrayFieldTitleTemplate.d.ts +1 -1
- package/lib/components/templates/ArrayFieldTitleTemplate.d.ts.map +1 -1
- package/lib/components/templates/BaseInputTemplate.d.ts +1 -1
- package/lib/components/templates/BaseInputTemplate.d.ts.map +1 -1
- package/lib/components/templates/BaseInputTemplate.js +2 -2
- package/lib/components/templates/ButtonTemplates/AddButton.d.ts +1 -1
- package/lib/components/templates/ButtonTemplates/AddButton.d.ts.map +1 -1
- package/lib/components/templates/ButtonTemplates/IconButton.d.ts +6 -6
- package/lib/components/templates/ButtonTemplates/IconButton.d.ts.map +1 -1
- package/lib/components/templates/ButtonTemplates/SubmitButton.d.ts +1 -1
- package/lib/components/templates/ButtonTemplates/SubmitButton.d.ts.map +1 -1
- package/lib/components/templates/CyclicSchemaExpandTemplate.d.ts +7 -0
- package/lib/components/templates/CyclicSchemaExpandTemplate.d.ts.map +1 -0
- package/lib/components/templates/CyclicSchemaExpandTemplate.js +12 -0
- package/lib/components/templates/DescriptionField.d.ts +1 -1
- package/lib/components/templates/DescriptionField.d.ts.map +1 -1
- package/lib/components/templates/ErrorList.d.ts +1 -1
- package/lib/components/templates/ErrorList.d.ts.map +1 -1
- package/lib/components/templates/FallbackFieldTemplate.d.ts +1 -1
- package/lib/components/templates/FallbackFieldTemplate.d.ts.map +1 -1
- package/lib/components/templates/FieldErrorTemplate.d.ts +1 -1
- package/lib/components/templates/FieldErrorTemplate.d.ts.map +1 -1
- package/lib/components/templates/FieldHelpTemplate.d.ts +1 -1
- package/lib/components/templates/FieldHelpTemplate.d.ts.map +1 -1
- package/lib/components/templates/FieldTemplate/FieldTemplate.d.ts +1 -1
- package/lib/components/templates/FieldTemplate/FieldTemplate.d.ts.map +1 -1
- package/lib/components/templates/FieldTemplate/Label.d.ts +1 -1
- package/lib/components/templates/FieldTemplate/Label.d.ts.map +1 -1
- package/lib/components/templates/GridTemplate.d.ts +1 -1
- package/lib/components/templates/GridTemplate.d.ts.map +1 -1
- package/lib/components/templates/MultiSchemaFieldTemplate.d.ts +1 -1
- package/lib/components/templates/MultiSchemaFieldTemplate.d.ts.map +1 -1
- package/lib/components/templates/ObjectFieldTemplate.d.ts +1 -1
- package/lib/components/templates/ObjectFieldTemplate.d.ts.map +1 -1
- package/lib/components/templates/OptionalDataControlsTemplate.d.ts +1 -1
- package/lib/components/templates/OptionalDataControlsTemplate.d.ts.map +1 -1
- package/lib/components/templates/TitleField.d.ts +1 -1
- package/lib/components/templates/TitleField.d.ts.map +1 -1
- package/lib/components/templates/UnsupportedField.d.ts +1 -1
- package/lib/components/templates/UnsupportedField.d.ts.map +1 -1
- package/lib/components/templates/UnsupportedField.js +1 -1
- package/lib/components/templates/WrapIfAdditionalTemplate.d.ts +1 -1
- package/lib/components/templates/WrapIfAdditionalTemplate.d.ts.map +1 -1
- package/lib/components/templates/index.d.ts.map +1 -1
- package/lib/components/templates/index.js +2 -0
- package/lib/components/widgets/AltDateTimeWidget.d.ts +1 -1
- package/lib/components/widgets/AltDateTimeWidget.d.ts.map +1 -1
- package/lib/components/widgets/AltDateWidget.d.ts +1 -1
- package/lib/components/widgets/AltDateWidget.d.ts.map +1 -1
- package/lib/components/widgets/CheckboxWidget.d.ts +1 -1
- package/lib/components/widgets/CheckboxWidget.d.ts.map +1 -1
- package/lib/components/widgets/CheckboxesWidget.d.ts +1 -1
- package/lib/components/widgets/CheckboxesWidget.d.ts.map +1 -1
- package/lib/components/widgets/CheckboxesWidget.js +2 -2
- package/lib/components/widgets/ColorWidget.d.ts +1 -1
- package/lib/components/widgets/ColorWidget.d.ts.map +1 -1
- package/lib/components/widgets/DateTimeWidget.d.ts +1 -1
- package/lib/components/widgets/DateTimeWidget.d.ts.map +1 -1
- package/lib/components/widgets/DateWidget.d.ts +1 -1
- package/lib/components/widgets/DateWidget.d.ts.map +1 -1
- package/lib/components/widgets/EmailWidget.d.ts +1 -1
- package/lib/components/widgets/EmailWidget.d.ts.map +1 -1
- package/lib/components/widgets/FileWidget.d.ts +1 -1
- package/lib/components/widgets/FileWidget.d.ts.map +1 -1
- package/lib/components/widgets/HiddenWidget.d.ts +1 -1
- package/lib/components/widgets/HiddenWidget.d.ts.map +1 -1
- package/lib/components/widgets/PasswordWidget.d.ts +1 -1
- package/lib/components/widgets/PasswordWidget.d.ts.map +1 -1
- package/lib/components/widgets/RadioWidget.d.ts +1 -1
- package/lib/components/widgets/RadioWidget.d.ts.map +1 -1
- package/lib/components/widgets/RadioWidget.js +2 -2
- package/lib/components/widgets/RangeWidget.d.ts +1 -1
- package/lib/components/widgets/RangeWidget.d.ts.map +1 -1
- package/lib/components/widgets/RatingWidget.d.ts +1 -1
- package/lib/components/widgets/RatingWidget.d.ts.map +1 -1
- package/lib/components/widgets/SelectWidget.d.ts +1 -1
- package/lib/components/widgets/SelectWidget.d.ts.map +1 -1
- package/lib/components/widgets/SelectWidget.js +9 -8
- package/lib/components/widgets/TextWidget.d.ts +1 -1
- package/lib/components/widgets/TextWidget.d.ts.map +1 -1
- package/lib/components/widgets/TextareaWidget.d.ts +1 -1
- package/lib/components/widgets/TextareaWidget.d.ts.map +1 -1
- package/lib/components/widgets/TextareaWidget.js +3 -3
- package/lib/components/widgets/TimeWidget.d.ts +1 -1
- package/lib/components/widgets/TimeWidget.d.ts.map +1 -1
- package/lib/components/widgets/URLWidget.d.ts +1 -1
- package/lib/components/widgets/URLWidget.d.ts.map +1 -1
- package/lib/components/widgets/UpDownWidget.d.ts +1 -1
- package/lib/components/widgets/UpDownWidget.d.ts.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +28 -27
- package/src/components/Form.tsx +34 -15
- package/src/components/fields/CyclicSchemaField.tsx +40 -0
- package/src/components/fields/MultiSchemaField.tsx +201 -230
- package/src/components/fields/NumberField.tsx +30 -16
- package/src/components/fields/ObjectField.tsx +29 -5
- package/src/components/fields/SchemaField.tsx +46 -34
- package/src/components/fields/index.ts +2 -0
- package/src/components/templates/BaseInputTemplate.tsx +2 -5
- package/src/components/templates/CyclicSchemaExpandTemplate.tsx +31 -0
- package/src/components/templates/UnsupportedField.tsx +1 -1
- package/src/components/templates/index.ts +2 -0
- package/src/components/widgets/CheckboxesWidget.tsx +2 -2
- package/src/components/widgets/RadioWidget.tsx +2 -2
- package/src/components/widgets/SelectWidget.tsx +45 -30
- package/src/components/widgets/TextareaWidget.tsx +3 -3
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
FormContextType,
|
|
9
9
|
GenericObjectType,
|
|
10
10
|
Registry,
|
|
11
|
+
RJSFMarkedSchema,
|
|
11
12
|
RJSFSchema,
|
|
12
13
|
StrictRJSFSchema,
|
|
13
14
|
} from '@rjsf/utils';
|
|
@@ -71,6 +72,16 @@ function getDefaultValue<T = any, S extends StrictRJSFSchema = RJSFSchema, F ext
|
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
|
|
75
|
+
function isAdditionalPropertySchema(schema: unknown) {
|
|
76
|
+
return Boolean((schema as RJSFMarkedSchema)?.[ADDITIONAL_PROPERTY_FLAG]);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function getAdditionalPropertyOrder<S extends StrictRJSFSchema = RJSFSchema>(
|
|
80
|
+
schemaProperties: NonNullable<S['properties']>,
|
|
81
|
+
) {
|
|
82
|
+
return Object.keys(schemaProperties).filter((property) => isAdditionalPropertySchema(schemaProperties[property]));
|
|
83
|
+
}
|
|
84
|
+
|
|
74
85
|
/** Props for the `ObjectFieldProperty` component */
|
|
75
86
|
interface ObjectFieldPropertyProps<
|
|
76
87
|
T = any,
|
|
@@ -233,10 +244,17 @@ export default function ObjectField<T = any, S extends StrictRJSFSchema = RJSFSc
|
|
|
233
244
|
[schemaUtils, rawSchema, formData],
|
|
234
245
|
);
|
|
235
246
|
const uiOptions = useMemo(() => getUiOptions<T, S, F>(uiSchema, globalUiOptions), [uiSchema, globalUiOptions]);
|
|
236
|
-
const
|
|
247
|
+
const schemaProperties = useMemo(() => schema.properties ?? {}, [schema.properties]);
|
|
237
248
|
// All the children will use childFieldPathId if present in the props, falling back to the fieldPathId
|
|
238
249
|
const childFieldPathId = props.childFieldPathId ?? fieldPathId;
|
|
239
250
|
const lastRenamedProperty = useRef({ previousKey: '', currentKey: undefined as string | undefined });
|
|
251
|
+
const [additionalPropertyOrder, setAdditionalPropertyOrder] = useState(() =>
|
|
252
|
+
getAdditionalPropertyOrder<S>(schemaProperties),
|
|
253
|
+
);
|
|
254
|
+
const definedPropertyOrder = useMemo(() => {
|
|
255
|
+
const additionalPropertySet = new Set(getAdditionalPropertyOrder<S>(schemaProperties));
|
|
256
|
+
return Object.keys(schemaProperties).filter((property) => !additionalPropertySet.has(property));
|
|
257
|
+
}, [schemaProperties]);
|
|
240
258
|
|
|
241
259
|
const templateTitle = uiOptions.title ?? schema.title ?? title ?? name;
|
|
242
260
|
const description = uiOptions.description ?? schema.description;
|
|
@@ -291,7 +309,7 @@ export default function ObjectField<T = any, S extends StrictRJSFSchema = RJSFSc
|
|
|
291
309
|
apSchema = schemaUtils.retrieveSchema({ [REF_KEY]: apSchema[REF_KEY] } as S, formData);
|
|
292
310
|
type = apSchema.type;
|
|
293
311
|
constValue = apSchema.const;
|
|
294
|
-
defaultValue = apSchema
|
|
312
|
+
defaultValue = schemaUtils.getDefaultFormState(apSchema as S, defaultValue as T) as RJSFSchema['default'];
|
|
295
313
|
}
|
|
296
314
|
if (!type && (ANY_OF_KEY in apSchema || ONE_OF_KEY in apSchema)) {
|
|
297
315
|
type = 'object';
|
|
@@ -307,6 +325,7 @@ export default function ObjectField<T = any, S extends StrictRJSFSchema = RJSFSc
|
|
|
307
325
|
lastRenamedProperty.current.currentKey = newKey;
|
|
308
326
|
lastRenamedProperty.current.previousKey = getAvailableKey(newKey, newFormData);
|
|
309
327
|
}
|
|
328
|
+
setAdditionalPropertyOrder((order) => [...order, newKey]);
|
|
310
329
|
onChange(newFormData, childFieldPathId.path);
|
|
311
330
|
}, [formData, onChange, translateString, schemaUtils, childFieldPathId, getAvailableKey, schema]);
|
|
312
331
|
|
|
@@ -338,6 +357,7 @@ export default function ObjectField<T = any, S extends StrictRJSFSchema = RJSFSc
|
|
|
338
357
|
lastRenamedProperty.current.previousKey = oldKey;
|
|
339
358
|
}
|
|
340
359
|
lastRenamedProperty.current.currentKey = actualNewKey;
|
|
360
|
+
setAdditionalPropertyOrder((order) => order.map((property) => (property === oldKey ? actualNewKey : property)));
|
|
341
361
|
onChange(renamedObj, childFieldPathId.path);
|
|
342
362
|
}
|
|
343
363
|
},
|
|
@@ -349,6 +369,7 @@ export default function ObjectField<T = any, S extends StrictRJSFSchema = RJSFSc
|
|
|
349
369
|
*/
|
|
350
370
|
const handleRemoveProperty = useCallback(
|
|
351
371
|
(key: string) => {
|
|
372
|
+
setAdditionalPropertyOrder((order) => order.filter((property) => property !== key));
|
|
352
373
|
onChange(ADDITIONAL_PROPERTY_KEY_REMOVE as T, [...childFieldPathId.path, key]);
|
|
353
374
|
},
|
|
354
375
|
[onChange, childFieldPathId],
|
|
@@ -368,8 +389,11 @@ export default function ObjectField<T = any, S extends StrictRJSFSchema = RJSFSc
|
|
|
368
389
|
|
|
369
390
|
if (!renderOptionalField || hasFormData) {
|
|
370
391
|
try {
|
|
371
|
-
const
|
|
372
|
-
|
|
392
|
+
const definedPropertySet = new Set(definedPropertyOrder);
|
|
393
|
+
const currentAdditionalProperties = additionalPropertyOrder.filter(
|
|
394
|
+
(property) => Object.hasOwn(schemaProperties, property) && !definedPropertySet.has(property),
|
|
395
|
+
);
|
|
396
|
+
orderedProperties = orderProperties([...definedPropertyOrder, ...currentAdditionalProperties], uiOptions.order);
|
|
373
397
|
} catch (err) {
|
|
374
398
|
return (
|
|
375
399
|
<div>
|
|
@@ -394,7 +418,7 @@ export default function ObjectField<T = any, S extends StrictRJSFSchema = RJSFSc
|
|
|
394
418
|
title: uiOptions.label === false ? '' : templateTitle,
|
|
395
419
|
description: uiOptions.label === false ? undefined : description,
|
|
396
420
|
properties: orderedProperties.map((propertyName) => {
|
|
397
|
-
const addedByAdditionalProperties =
|
|
421
|
+
const addedByAdditionalProperties = isAdditionalPropertySchema(schema.properties?.[propertyName]);
|
|
398
422
|
const fieldUiSchema = addedByAdditionalProperties ? uiSchema.additionalProperties : uiSchema[propertyName];
|
|
399
423
|
const hidden = getUiOptions<T, S, F>(fieldUiSchema).widget === 'hidden';
|
|
400
424
|
const content = (
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ComponentType } from 'react';
|
|
2
|
-
import { useCallback,
|
|
2
|
+
import { useCallback, memo } from 'react';
|
|
3
3
|
import type {
|
|
4
4
|
ErrorSchema,
|
|
5
5
|
Field,
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
FieldTemplateProps,
|
|
10
10
|
FormContextType,
|
|
11
11
|
Registry,
|
|
12
|
+
RJSFMarkedSchema,
|
|
12
13
|
RJSFSchema,
|
|
13
14
|
StrictRJSFSchema,
|
|
14
15
|
UIOptionsType,
|
|
@@ -16,6 +17,7 @@ import type {
|
|
|
16
17
|
import {
|
|
17
18
|
ADDITIONAL_PROPERTY_FLAG,
|
|
18
19
|
ANY_OF_KEY,
|
|
20
|
+
deepEquals,
|
|
19
21
|
descriptionId,
|
|
20
22
|
getSchemaType,
|
|
21
23
|
getTemplate,
|
|
@@ -24,7 +26,8 @@ import {
|
|
|
24
26
|
isFormDataAvailable,
|
|
25
27
|
ONE_OF_KEY,
|
|
26
28
|
resolveUiSchema,
|
|
27
|
-
|
|
29
|
+
RJSF_REF_CYCLE_KEY,
|
|
30
|
+
shallowEquals,
|
|
28
31
|
shouldRenderOptionalField,
|
|
29
32
|
toFieldPathId,
|
|
30
33
|
TranslatableString,
|
|
@@ -59,7 +62,7 @@ function getFieldComponent<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
|
|
|
59
62
|
registry: Registry<T, S, F>,
|
|
60
63
|
): ComponentType<FieldProps<T, S, F>> {
|
|
61
64
|
const { field } = uiOptions;
|
|
62
|
-
const { fields } = registry;
|
|
65
|
+
const { fields, schemaUtils } = registry;
|
|
63
66
|
if (typeof field === 'function') {
|
|
64
67
|
return field;
|
|
65
68
|
}
|
|
@@ -77,9 +80,13 @@ function getFieldComponent<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
|
|
|
77
80
|
componentName = schemaId;
|
|
78
81
|
}
|
|
79
82
|
|
|
80
|
-
// If the
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
+
// If the schema uses 'anyOf' or 'oneOf' and is not a pure select (all-constant options),
|
|
84
|
+
// let the MultiSchemaField component handle the form display entirely.
|
|
85
|
+
// ObjectField is excluded: it renders shared properties (defined at the parent schema
|
|
86
|
+
// level) alongside the XxxOfField option selector.
|
|
87
|
+
// All other field types — including primitives and arrays — have no shared renderable
|
|
88
|
+
// properties, so the outer FieldComponent would only produce a spurious duplicate input.
|
|
89
|
+
if ((schema.anyOf || schema.oneOf) && !schemaUtils.isSelect(schema) && componentName !== 'ObjectField') {
|
|
83
90
|
return () => null;
|
|
84
91
|
}
|
|
85
92
|
|
|
@@ -111,18 +118,7 @@ function SchemaFieldRender<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
|
|
|
111
118
|
wasPropertyKeyModified = false,
|
|
112
119
|
} = props;
|
|
113
120
|
const { schemaUtils, globalFormOptions, globalUiOptions, fields } = registry;
|
|
114
|
-
const { AnyOfField: _AnyOfField, OneOfField: _OneOfField } = fields;
|
|
115
|
-
const uiSchema = resolveUiSchema<T, S, F>(_schema, _uiSchema, registry);
|
|
116
|
-
const uiOptions = getUiOptions<T, S, F>(uiSchema, globalUiOptions);
|
|
117
|
-
const FieldTemplate = getTemplate<'FieldTemplate', T, S, F>('FieldTemplate', registry, uiOptions);
|
|
118
|
-
const DescriptionFieldTemplate = getTemplate<'DescriptionFieldTemplate', T, S, F>(
|
|
119
|
-
'DescriptionFieldTemplate',
|
|
120
|
-
registry,
|
|
121
|
-
uiOptions,
|
|
122
|
-
);
|
|
123
|
-
const FieldHelpTemplate = getTemplate<'FieldHelpTemplate', T, S, F>('FieldHelpTemplate', registry, uiOptions);
|
|
124
|
-
const FieldErrorTemplate = getTemplate<'FieldErrorTemplate', T, S, F>('FieldErrorTemplate', registry, uiOptions);
|
|
125
|
-
const schema = schemaUtils.retrieveSchema(_schema, formData);
|
|
121
|
+
const { AnyOfField: _AnyOfField, OneOfField: _OneOfField, CyclicSchemaField } = fields;
|
|
126
122
|
const fieldId = fieldPathId[ID_KEY];
|
|
127
123
|
|
|
128
124
|
/** Intermediary `onChange` handler for field components that will inject the `id` of the current field into the
|
|
@@ -136,6 +132,24 @@ function SchemaFieldRender<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
|
|
|
136
132
|
[fieldId, onChange],
|
|
137
133
|
);
|
|
138
134
|
|
|
135
|
+
// Stop $ref cycles: when resolveAllReferences detects a repeated property $ref it tags the schema with this flag.
|
|
136
|
+
// The check must come after all hook calls to satisfy React's rules of hooks.
|
|
137
|
+
if ((_schema as RJSFMarkedSchema)[RJSF_REF_CYCLE_KEY]) {
|
|
138
|
+
return <CyclicSchemaField {...props} />;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const uiSchema = resolveUiSchema<T, S, F>(_schema, _uiSchema, registry);
|
|
142
|
+
const uiOptions = getUiOptions<T, S, F>(uiSchema, globalUiOptions);
|
|
143
|
+
const FieldTemplate = getTemplate<'FieldTemplate', T, S, F>('FieldTemplate', registry, uiOptions);
|
|
144
|
+
const DescriptionFieldTemplate = getTemplate<'DescriptionFieldTemplate', T, S, F>(
|
|
145
|
+
'DescriptionFieldTemplate',
|
|
146
|
+
registry,
|
|
147
|
+
uiOptions,
|
|
148
|
+
);
|
|
149
|
+
const FieldHelpTemplate = getTemplate<'FieldHelpTemplate', T, S, F>('FieldHelpTemplate', registry, uiOptions);
|
|
150
|
+
const FieldErrorTemplate = getTemplate<'FieldErrorTemplate', T, S, F>('FieldErrorTemplate', registry, uiOptions);
|
|
151
|
+
const schema = schemaUtils.retrieveSchema(_schema, formData);
|
|
152
|
+
|
|
139
153
|
const FieldComponent = getFieldComponent<T, S, F>(schema, uiOptions, registry);
|
|
140
154
|
|
|
141
155
|
const isDeprecated = Boolean(schema.deprecated);
|
|
@@ -327,24 +341,22 @@ function SchemaFieldRender<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
|
|
|
327
341
|
);
|
|
328
342
|
}
|
|
329
343
|
|
|
330
|
-
/** The `SchemaField` component determines whether it is necessary to rerender the component based on any props changes
|
|
331
|
-
*
|
|
344
|
+
/** The `SchemaField` component wraps `SchemaFieldRender` with a custom memoization comparator that determines whether it is necessary to rerender the component based on any props changes
|
|
345
|
+
* using `experimental_componentUpdateStrategy`.
|
|
346
|
+
*
|
|
347
|
+
* The cast to `typeof SchemaFieldRender` preserves the generic type signature (<T, S, F>) for consumers,
|
|
348
|
+
* since React.memo's return type erases generic parameters.
|
|
332
349
|
*/
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
const {
|
|
338
|
-
registry: { globalFormOptions },
|
|
339
|
-
} = this.props;
|
|
340
|
-
const { experimental_componentUpdateStrategy = 'customDeep' } = globalFormOptions;
|
|
341
|
-
|
|
342
|
-
return shouldRender(this, nextProps, this.state, experimental_componentUpdateStrategy);
|
|
350
|
+
const SchemaField = memo(SchemaFieldRender, (prevProps, nextProps) => {
|
|
351
|
+
const { experimental_componentUpdateStrategy = 'customDeep' } = nextProps.registry.globalFormOptions;
|
|
352
|
+
if (experimental_componentUpdateStrategy === 'always') {
|
|
353
|
+
return false; // always re-render — never consider props equal
|
|
343
354
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
return <SchemaFieldRender<T, S, F> {...this.props} />;
|
|
355
|
+
if (experimental_componentUpdateStrategy === 'shallow') {
|
|
356
|
+
return shallowEquals(prevProps, nextProps);
|
|
347
357
|
}
|
|
348
|
-
|
|
358
|
+
// default: 'customDeep'
|
|
359
|
+
return deepEquals(prevProps, nextProps);
|
|
360
|
+
}) as unknown as typeof SchemaFieldRender;
|
|
349
361
|
|
|
350
362
|
export default SchemaField;
|
|
@@ -2,6 +2,7 @@ import type { Field, FormContextType, RegistryFieldsType, RJSFSchema, StrictRJSF
|
|
|
2
2
|
|
|
3
3
|
import ArrayField from './ArrayField';
|
|
4
4
|
import BooleanField from './BooleanField';
|
|
5
|
+
import CyclicSchemaField from './CyclicSchemaField';
|
|
5
6
|
import FallbackField from './FallbackField';
|
|
6
7
|
import LayoutGridField from './LayoutGridField';
|
|
7
8
|
import LayoutHeaderField from './LayoutHeaderField';
|
|
@@ -24,6 +25,7 @@ function fields<
|
|
|
24
25
|
ArrayField: ArrayField as unknown as Field<T, S, F>,
|
|
25
26
|
// ArrayField falls back to SchemaField if ArraySchemaField is not defined, which it isn't by default
|
|
26
27
|
BooleanField,
|
|
28
|
+
CyclicSchemaField,
|
|
27
29
|
FallbackField,
|
|
28
30
|
LayoutGridField,
|
|
29
31
|
LayoutHeaderField,
|
|
@@ -64,12 +64,9 @@ export default function BaseInputTemplate<
|
|
|
64
64
|
onChange(newValue === '' ? options.emptyValue : newValue),
|
|
65
65
|
[onChange, options],
|
|
66
66
|
);
|
|
67
|
-
const handleBlur = useCallback(
|
|
68
|
-
({ target }: FocusEvent<HTMLInputElement>) => onBlur(id, target && target.value),
|
|
69
|
-
[onBlur, id],
|
|
70
|
-
);
|
|
67
|
+
const handleBlur = useCallback(({ target }: FocusEvent<HTMLInputElement>) => onBlur(id, target?.value), [onBlur, id]);
|
|
71
68
|
const handleFocus = useCallback(
|
|
72
|
-
({ target }: FocusEvent<HTMLInputElement>) => onFocus(id, target
|
|
69
|
+
({ target }: FocusEvent<HTMLInputElement>) => onFocus(id, target?.value),
|
|
73
70
|
[onFocus, id],
|
|
74
71
|
);
|
|
75
72
|
const handleClear = useCallback(
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { CyclicSchemaExpandProps, FormContextType, RJSFSchema, StrictRJSFSchema } from '@rjsf/utils';
|
|
2
|
+
import { ID_KEY, TranslatableString } from '@rjsf/utils';
|
|
3
|
+
|
|
4
|
+
/** The `CyclicSchemaExpandTemplate` is the template to use to render the cyclic schema expand message and controls
|
|
5
|
+
*
|
|
6
|
+
* @param props - The `DescriptionFieldProps` for this component
|
|
7
|
+
*/
|
|
8
|
+
export default function CyclicSchemaExpandTemplate<
|
|
9
|
+
T = any,
|
|
10
|
+
S extends StrictRJSFSchema = RJSFSchema,
|
|
11
|
+
F extends FormContextType = any,
|
|
12
|
+
>(props: CyclicSchemaExpandProps<T, S, F>) {
|
|
13
|
+
const { name, fieldPathId, registry, onExpand } = props;
|
|
14
|
+
const { translateString } = registry;
|
|
15
|
+
const buttonId = `${fieldPathId[ID_KEY]}-button`;
|
|
16
|
+
return (
|
|
17
|
+
<div style={{ marginTop: '1rem' }}>
|
|
18
|
+
<div className='text-danger'>{translateString(TranslatableString.CycleDetected, [name])}</div>
|
|
19
|
+
<div>
|
|
20
|
+
<button
|
|
21
|
+
id={buttonId}
|
|
22
|
+
type='button'
|
|
23
|
+
className='btn btn-sm btn-warning'
|
|
24
|
+
onClick={() => onExpand(fieldPathId[ID_KEY])}
|
|
25
|
+
>
|
|
26
|
+
{translateString(TranslatableString.ExpandButton)}
|
|
27
|
+
</button>{' '}
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -14,7 +14,7 @@ function UnsupportedField<T = any, S extends StrictRJSFSchema = RJSFSchema, F ex
|
|
|
14
14
|
const { translateString } = registry;
|
|
15
15
|
let translateEnum: TranslatableString = TranslatableString.UnsupportedField;
|
|
16
16
|
const translateParams: string[] = [];
|
|
17
|
-
if (fieldPathId
|
|
17
|
+
if (fieldPathId?.$id) {
|
|
18
18
|
translateEnum = TranslatableString.UnsupportedFieldWithId;
|
|
19
19
|
translateParams.push(fieldPathId.$id);
|
|
20
20
|
}
|
|
@@ -7,6 +7,7 @@ import ArrayFieldTemplate from './ArrayFieldTemplate';
|
|
|
7
7
|
import ArrayFieldTitleTemplate from './ArrayFieldTitleTemplate';
|
|
8
8
|
import BaseInputTemplate from './BaseInputTemplate';
|
|
9
9
|
import ButtonTemplates from './ButtonTemplates';
|
|
10
|
+
import CyclicSchemaExpandTemplate from './CyclicSchemaExpandTemplate';
|
|
10
11
|
import DescriptionField from './DescriptionField';
|
|
11
12
|
import ErrorList from './ErrorList';
|
|
12
13
|
import FallbackFieldTemplate from './FallbackFieldTemplate';
|
|
@@ -34,6 +35,7 @@ function templates<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends F
|
|
|
34
35
|
ArrayFieldTitleTemplate,
|
|
35
36
|
ButtonTemplates: ButtonTemplates<T, S, F>(),
|
|
36
37
|
BaseInputTemplate,
|
|
38
|
+
CyclicSchemaExpandTemplate,
|
|
37
39
|
DescriptionFieldTemplate: DescriptionField,
|
|
38
40
|
ErrorListTemplate: ErrorList,
|
|
39
41
|
FallbackFieldTemplate,
|
|
@@ -35,13 +35,13 @@ function CheckboxesWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F ex
|
|
|
35
35
|
|
|
36
36
|
const handleBlur = useCallback(
|
|
37
37
|
({ target }: FocusEvent<HTMLInputElement>) =>
|
|
38
|
-
onBlur(id, enumOptionValueDecoder<S>(target
|
|
38
|
+
onBlur(id, enumOptionValueDecoder<S>(target?.value, enumOptions, optionValueFormat, emptyValue)),
|
|
39
39
|
[onBlur, id, enumOptions, emptyValue, optionValueFormat],
|
|
40
40
|
);
|
|
41
41
|
|
|
42
42
|
const handleFocus = useCallback(
|
|
43
43
|
({ target }: FocusEvent<HTMLInputElement>) =>
|
|
44
|
-
onFocus(id, enumOptionValueDecoder<S>(target
|
|
44
|
+
onFocus(id, enumOptionValueDecoder<S>(target?.value, enumOptions, optionValueFormat, emptyValue)),
|
|
45
45
|
[onFocus, id, enumOptions, emptyValue, optionValueFormat],
|
|
46
46
|
);
|
|
47
47
|
|
|
@@ -33,13 +33,13 @@ function RadioWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends
|
|
|
33
33
|
|
|
34
34
|
const handleBlur = useCallback(
|
|
35
35
|
({ target }: FocusEvent<HTMLInputElement>) =>
|
|
36
|
-
onBlur(id, enumOptionValueDecoder<S>(target
|
|
36
|
+
onBlur(id, enumOptionValueDecoder<S>(target?.value, enumOptions, optionValueFormat, emptyValue)),
|
|
37
37
|
[onBlur, enumOptions, emptyValue, id, optionValueFormat],
|
|
38
38
|
);
|
|
39
39
|
|
|
40
40
|
const handleFocus = useCallback(
|
|
41
41
|
({ target }: FocusEvent<HTMLInputElement>) =>
|
|
42
|
-
onFocus(id, enumOptionValueDecoder<S>(target
|
|
42
|
+
onFocus(id, enumOptionValueDecoder<S>(target?.value, enumOptions, optionValueFormat, emptyValue)),
|
|
43
43
|
[onFocus, enumOptions, emptyValue, id, optionValueFormat],
|
|
44
44
|
);
|
|
45
45
|
|
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
enumOptionValueDecoder,
|
|
8
8
|
enumOptionValueEncoder,
|
|
9
9
|
getOptionValueFormat,
|
|
10
|
+
logUnsupportedDefaultForEnum,
|
|
11
|
+
SelectedOptionDescription,
|
|
10
12
|
} from '@rjsf/utils';
|
|
11
13
|
|
|
12
14
|
function getValue(event: SyntheticEvent<HTMLSelectElement>, multiple: boolean) {
|
|
@@ -39,6 +41,8 @@ function SelectWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extend
|
|
|
39
41
|
onFocus,
|
|
40
42
|
placeholder,
|
|
41
43
|
htmlName,
|
|
44
|
+
registry,
|
|
45
|
+
uiSchema,
|
|
42
46
|
}: WidgetProps<T, S, F>) {
|
|
43
47
|
const { enumOptions, enumDisabled, emptyValue: optEmptyVal } = options;
|
|
44
48
|
const emptyValue = multiple ? [] : '';
|
|
@@ -70,38 +74,49 @@ function SelectWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extend
|
|
|
70
74
|
|
|
71
75
|
const selectValue = enumOptionSelectedValue<S>(value, enumOptions, multiple, optionValueFormat, emptyValue);
|
|
72
76
|
const showPlaceholderOption = !multiple && schema.default === undefined;
|
|
77
|
+
logUnsupportedDefaultForEnum<S>(id, schema, enumOptions, multiple);
|
|
73
78
|
|
|
74
79
|
return (
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
80
|
+
<>
|
|
81
|
+
<SelectedOptionDescription
|
|
82
|
+
id={id}
|
|
83
|
+
multiple={multiple}
|
|
84
|
+
options={options}
|
|
85
|
+
registry={registry}
|
|
86
|
+
uiSchema={uiSchema}
|
|
87
|
+
value={value}
|
|
88
|
+
/>
|
|
89
|
+
{/* oxlint-disable-next-line jsx-a11y/no-autofocus */}
|
|
90
|
+
<select
|
|
91
|
+
id={id}
|
|
92
|
+
name={htmlName || id}
|
|
93
|
+
multiple={multiple}
|
|
94
|
+
className='form-control'
|
|
95
|
+
value={selectValue}
|
|
96
|
+
required={required}
|
|
97
|
+
disabled={disabled || readonly}
|
|
98
|
+
autoFocus={autofocus}
|
|
99
|
+
onBlur={handleBlur}
|
|
100
|
+
onFocus={handleFocus}
|
|
101
|
+
onChange={handleChange}
|
|
102
|
+
aria-describedby={ariaDescribedByIds(id)}
|
|
103
|
+
>
|
|
104
|
+
{showPlaceholderOption && <option value=''>{placeholder}</option>}
|
|
105
|
+
{Array.isArray(enumOptions) &&
|
|
106
|
+
enumOptions.map(({ value: enumValue, label: enumLabel }, i) => {
|
|
107
|
+
const isDisabled = enumDisabled && enumDisabled.includes(enumValue);
|
|
108
|
+
return (
|
|
109
|
+
<option
|
|
110
|
+
key={String(enumValue)}
|
|
111
|
+
value={enumOptionValueEncoder(enumValue, i, optionValueFormat)}
|
|
112
|
+
disabled={isDisabled}
|
|
113
|
+
>
|
|
114
|
+
{enumLabel}
|
|
115
|
+
</option>
|
|
116
|
+
);
|
|
117
|
+
})}
|
|
118
|
+
</select>
|
|
119
|
+
</>
|
|
105
120
|
);
|
|
106
121
|
}
|
|
107
122
|
|
|
@@ -9,7 +9,7 @@ import { ariaDescribedByIds } from '@rjsf/utils';
|
|
|
9
9
|
*/
|
|
10
10
|
function TextareaWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>({
|
|
11
11
|
id,
|
|
12
|
-
options
|
|
12
|
+
options,
|
|
13
13
|
placeholder,
|
|
14
14
|
value,
|
|
15
15
|
required,
|
|
@@ -28,12 +28,12 @@ function TextareaWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F exte
|
|
|
28
28
|
);
|
|
29
29
|
|
|
30
30
|
const handleBlur = useCallback(
|
|
31
|
-
({ target }: FocusEvent<HTMLTextAreaElement>) => onBlur(id, target
|
|
31
|
+
({ target }: FocusEvent<HTMLTextAreaElement>) => onBlur(id, target?.value),
|
|
32
32
|
[onBlur, id],
|
|
33
33
|
);
|
|
34
34
|
|
|
35
35
|
const handleFocus = useCallback(
|
|
36
|
-
({ target }: FocusEvent<HTMLTextAreaElement>) => onFocus(id, target
|
|
36
|
+
({ target }: FocusEvent<HTMLTextAreaElement>) => onFocus(id, target?.value),
|
|
37
37
|
[id, onFocus],
|
|
38
38
|
);
|
|
39
39
|
|