@rjsf/core 6.0.0-beta.9 → 6.0.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/README.md +2 -0
- package/dist/core.umd.js +2042 -1987
- package/dist/index.cjs +4909 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.esm.js +2509 -2389
- package/dist/index.esm.js.map +4 -4
- package/lib/components/Form.d.ts +137 -34
- package/lib/components/Form.d.ts.map +1 -1
- package/lib/components/Form.js +318 -173
- package/lib/components/fields/ArrayField.d.ts +2 -187
- package/lib/components/fields/ArrayField.d.ts.map +1 -1
- package/lib/components/fields/ArrayField.js +526 -492
- package/lib/components/fields/BooleanField.d.ts.map +1 -1
- package/lib/components/fields/BooleanField.js +8 -3
- package/lib/components/fields/FallbackField.d.ts +7 -0
- package/lib/components/fields/FallbackField.d.ts.map +1 -0
- package/lib/components/fields/FallbackField.js +72 -0
- package/lib/components/fields/LayoutGridField.d.ts +109 -186
- package/lib/components/fields/LayoutGridField.d.ts.map +1 -1
- package/lib/components/fields/LayoutGridField.js +426 -426
- package/lib/components/fields/LayoutHeaderField.d.ts +1 -1
- package/lib/components/fields/LayoutHeaderField.js +3 -3
- package/lib/components/fields/LayoutMultiSchemaField.d.ts.map +1 -1
- package/lib/components/fields/LayoutMultiSchemaField.js +6 -6
- package/lib/components/fields/MultiSchemaField.d.ts.map +1 -1
- package/lib/components/fields/MultiSchemaField.js +16 -10
- package/lib/components/fields/NullField.js +3 -3
- package/lib/components/fields/NumberField.d.ts.map +1 -1
- package/lib/components/fields/NumberField.js +3 -3
- package/lib/components/fields/ObjectField.d.ts +2 -68
- package/lib/components/fields/ObjectField.d.ts.map +1 -1
- package/lib/components/fields/ObjectField.js +163 -163
- package/lib/components/fields/OptionalDataControlsField.d.ts +8 -0
- package/lib/components/fields/OptionalDataControlsField.d.ts.map +1 -0
- package/lib/components/fields/OptionalDataControlsField.js +43 -0
- package/lib/components/fields/SchemaField.d.ts.map +1 -1
- package/lib/components/fields/SchemaField.js +52 -30
- package/lib/components/fields/StringField.d.ts.map +1 -1
- package/lib/components/fields/StringField.js +8 -3
- package/lib/components/fields/index.d.ts.map +1 -1
- package/lib/components/fields/index.js +4 -0
- package/lib/components/templates/ArrayFieldDescriptionTemplate.d.ts +1 -1
- package/lib/components/templates/ArrayFieldDescriptionTemplate.js +3 -3
- package/lib/components/templates/ArrayFieldItemButtonsTemplate.d.ts +3 -3
- package/lib/components/templates/ArrayFieldItemButtonsTemplate.d.ts.map +1 -1
- package/lib/components/templates/ArrayFieldItemButtonsTemplate.js +3 -8
- package/lib/components/templates/ArrayFieldItemTemplate.d.ts +3 -3
- package/lib/components/templates/ArrayFieldItemTemplate.d.ts.map +1 -1
- package/lib/components/templates/ArrayFieldItemTemplate.js +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/ArrayFieldTemplate.js +4 -5
- package/lib/components/templates/ArrayFieldTitleTemplate.d.ts +1 -1
- package/lib/components/templates/ArrayFieldTitleTemplate.d.ts.map +1 -1
- package/lib/components/templates/ArrayFieldTitleTemplate.js +3 -3
- 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/AddButton.js +2 -2
- package/lib/components/templates/FallbackFieldTemplate.d.ts +7 -0
- package/lib/components/templates/FallbackFieldTemplate.d.ts.map +1 -0
- package/lib/components/templates/FallbackFieldTemplate.js +12 -0
- package/lib/components/templates/FieldErrorTemplate.js +2 -2
- package/lib/components/templates/FieldHelpTemplate.js +2 -2
- package/lib/components/templates/MultiSchemaFieldTemplate.d.ts +8 -0
- package/lib/components/templates/MultiSchemaFieldTemplate.d.ts.map +1 -0
- package/lib/components/templates/MultiSchemaFieldTemplate.js +10 -0
- package/lib/components/templates/ObjectFieldTemplate.d.ts.map +1 -1
- package/lib/components/templates/ObjectFieldTemplate.js +3 -2
- package/lib/components/templates/OptionalDataControlsTemplate.d.ts +11 -0
- package/lib/components/templates/OptionalDataControlsTemplate.d.ts.map +1 -0
- package/lib/components/templates/OptionalDataControlsTemplate.js +20 -0
- package/lib/components/templates/TitleField.d.ts.map +1 -1
- package/lib/components/templates/TitleField.js +2 -2
- package/lib/components/templates/UnsupportedField.js +3 -3
- package/lib/components/templates/WrapIfAdditionalTemplate.js +2 -2
- package/lib/components/templates/index.d.ts.map +1 -1
- package/lib/components/templates/index.js +6 -0
- package/lib/components/widgets/AltDateWidget.d.ts +1 -1
- package/lib/components/widgets/AltDateWidget.d.ts.map +1 -1
- package/lib/components/widgets/AltDateWidget.js +5 -46
- package/lib/components/widgets/CheckboxWidget.d.ts +1 -1
- package/lib/components/widgets/CheckboxWidget.d.ts.map +1 -1
- package/lib/components/widgets/CheckboxWidget.js +2 -2
- 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 +4 -4
- package/lib/components/widgets/FileWidget.d.ts.map +1 -1
- package/lib/components/widgets/FileWidget.js +7 -87
- package/lib/components/widgets/HiddenWidget.d.ts +1 -1
- package/lib/components/widgets/HiddenWidget.d.ts.map +1 -1
- package/lib/components/widgets/HiddenWidget.js +2 -2
- 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/RatingWidget.d.ts +1 -1
- package/lib/components/widgets/RatingWidget.d.ts.map +1 -1
- package/lib/components/widgets/RatingWidget.js +2 -2
- 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 +2 -2
- 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 +2 -2
- package/lib/getDefaultRegistry.d.ts.map +1 -1
- package/lib/getDefaultRegistry.js +6 -1
- package/lib/getTestRegistry.d.ts +5 -0
- package/lib/getTestRegistry.d.ts.map +1 -0
- package/lib/getTestRegistry.js +23 -0
- package/lib/index.d.ts +2 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +35 -20
- package/src/components/Form.tsx +468 -206
- package/src/components/fields/ArrayField.tsx +871 -723
- package/src/components/fields/BooleanField.tsx +14 -5
- package/src/components/fields/FallbackField.tsx +157 -0
- package/src/components/fields/LayoutGridField.tsx +626 -603
- package/src/components/fields/LayoutHeaderField.tsx +3 -3
- package/src/components/fields/LayoutMultiSchemaField.tsx +9 -10
- package/src/components/fields/MultiSchemaField.tsx +57 -36
- package/src/components/fields/NullField.tsx +3 -3
- package/src/components/fields/NumberField.tsx +11 -3
- package/src/components/fields/ObjectField.tsx +308 -239
- package/src/components/fields/OptionalDataControlsField.tsx +84 -0
- package/src/components/fields/SchemaField.tsx +75 -94
- package/src/components/fields/StringField.tsx +14 -5
- package/src/components/fields/index.ts +4 -0
- package/src/components/templates/ArrayFieldDescriptionTemplate.tsx +3 -3
- package/src/components/templates/ArrayFieldItemButtonsTemplate.tsx +16 -21
- package/src/components/templates/ArrayFieldItemTemplate.tsx +3 -3
- package/src/components/templates/ArrayFieldTemplate.tsx +11 -18
- package/src/components/templates/ArrayFieldTitleTemplate.tsx +4 -3
- package/src/components/templates/BaseInputTemplate.tsx +5 -5
- package/src/components/templates/ButtonTemplates/AddButton.tsx +2 -0
- package/src/components/templates/FallbackFieldTemplate.tsx +28 -0
- package/src/components/templates/FieldErrorTemplate.tsx +2 -2
- package/src/components/templates/FieldHelpTemplate.tsx +2 -2
- package/src/components/templates/MultiSchemaFieldTemplate.tsx +20 -0
- package/src/components/templates/ObjectFieldTemplate.tsx +12 -7
- package/src/components/templates/OptionalDataControlsTemplate.tsx +43 -0
- package/src/components/templates/TitleField.tsx +6 -1
- package/src/components/templates/UnsupportedField.tsx +3 -3
- package/src/components/templates/WrapIfAdditionalTemplate.tsx +5 -5
- package/src/components/templates/index.ts +6 -0
- package/src/components/widgets/AltDateWidget.tsx +8 -126
- package/src/components/widgets/CheckboxWidget.tsx +4 -3
- package/src/components/widgets/CheckboxesWidget.tsx +5 -4
- package/src/components/widgets/FileWidget.tsx +11 -102
- package/src/components/widgets/HiddenWidget.tsx +2 -1
- package/src/components/widgets/RadioWidget.tsx +3 -2
- package/src/components/widgets/RatingWidget.tsx +2 -1
- package/src/components/widgets/SelectWidget.tsx +3 -2
- package/src/components/widgets/TextareaWidget.tsx +3 -2
- package/src/getDefaultRegistry.ts +14 -1
- package/src/getTestRegistry.tsx +38 -0
- package/src/index.ts +2 -1
- package/dist/index.js +0 -4834
- package/dist/index.js.map +0 -7
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
3
|
+
import { allowAdditionalItems, getTemplate, getUiOptions, getWidget, hashObject, isCustomWidget, isFixedItems, isFormDataAvailable, optionsList, shouldRenderOptionalField, toFieldPathId, useDeepCompareMemo, ITEMS_KEY, ID_KEY, TranslatableString, } from '@rjsf/utils';
|
|
4
4
|
import cloneDeep from 'lodash-es/cloneDeep.js';
|
|
5
|
-
import get from 'lodash-es/get.js';
|
|
6
5
|
import isObject from 'lodash-es/isObject.js';
|
|
7
6
|
import set from 'lodash-es/set.js';
|
|
8
|
-
import
|
|
7
|
+
import uniqueId from 'lodash-es/uniqueId.js';
|
|
9
8
|
/** Used to generate a unique ID for an element in a row */
|
|
10
9
|
function generateRowId() {
|
|
11
|
-
return
|
|
10
|
+
return uniqueId('rjsf-array-item-');
|
|
12
11
|
}
|
|
13
12
|
/** Converts the `formData` into `KeyedFormDataType` data, using the `generateRowId()` function to create the key
|
|
14
13
|
*
|
|
@@ -36,108 +35,428 @@ function keyedToPlainFormData(keyedFormData) {
|
|
|
36
35
|
}
|
|
37
36
|
return [];
|
|
38
37
|
}
|
|
39
|
-
/**
|
|
40
|
-
*
|
|
38
|
+
/** Determines whether the item described in the schema is always required, which is determined by whether any item
|
|
39
|
+
* may be null.
|
|
40
|
+
*
|
|
41
|
+
* @param itemSchema - The schema for the item
|
|
42
|
+
* @return - True if the item schema type does not contain the "null" type
|
|
41
43
|
*/
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
constructor(props) {
|
|
48
|
-
super(props);
|
|
49
|
-
const { formData = [] } = props;
|
|
50
|
-
const keyedFormData = generateKeyedFormData(formData);
|
|
51
|
-
this.state = {
|
|
52
|
-
keyedFormData,
|
|
53
|
-
updatedKeyedFormData: false,
|
|
54
|
-
};
|
|
44
|
+
function isItemRequired(itemSchema) {
|
|
45
|
+
if (Array.isArray(itemSchema.type)) {
|
|
46
|
+
// While we don't yet support composite/nullable jsonschema types, it's
|
|
47
|
+
// future-proof to check for requirement against these.
|
|
48
|
+
return !itemSchema.type.includes('null');
|
|
55
49
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
50
|
+
// All non-null array item types are inherently required by design
|
|
51
|
+
return itemSchema.type !== 'null';
|
|
52
|
+
}
|
|
53
|
+
/** Determines whether more items can be added to the array. If the uiSchema indicates the array doesn't allow adding
|
|
54
|
+
* then false is returned. Otherwise, if the schema indicates that there are a maximum number of items and the
|
|
55
|
+
* `formData` matches that value, then false is returned, otherwise true is returned.
|
|
56
|
+
*
|
|
57
|
+
* @param registry - The registry
|
|
58
|
+
* @param schema - The schema for the field
|
|
59
|
+
* @param formItems - The list of items in the form
|
|
60
|
+
* @param [uiSchema] - The UiSchema for the field
|
|
61
|
+
* @returns - True if the item is addable otherwise false
|
|
62
|
+
*/
|
|
63
|
+
function canAddItem(registry, schema, formItems, uiSchema) {
|
|
64
|
+
let { addable } = getUiOptions(uiSchema, registry.globalUiOptions);
|
|
65
|
+
if (addable !== false) {
|
|
66
|
+
// if ui:options.addable was not explicitly set to false, we can add
|
|
67
|
+
// another item if we have not exceeded maxItems yet
|
|
68
|
+
if (schema.maxItems !== undefined) {
|
|
69
|
+
addable = formItems.length < schema.maxItems;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
addable = true;
|
|
68
73
|
}
|
|
69
|
-
const nextFormData = Array.isArray(nextProps.formData) ? nextProps.formData : [];
|
|
70
|
-
const previousKeyedFormData = prevState.keyedFormData || [];
|
|
71
|
-
const newKeyedFormData = nextFormData.length === previousKeyedFormData.length
|
|
72
|
-
? previousKeyedFormData.map((previousKeyedFormDatum, index) => {
|
|
73
|
-
return {
|
|
74
|
-
key: previousKeyedFormDatum.key,
|
|
75
|
-
item: nextFormData[index],
|
|
76
|
-
};
|
|
77
|
-
})
|
|
78
|
-
: generateKeyedFormData(nextFormData);
|
|
79
|
-
return {
|
|
80
|
-
keyedFormData: newKeyedFormData,
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
/** Returns the appropriate title for an item by getting first the title from the schema.items, then falling back to
|
|
84
|
-
* the description from the schema.items, and finally the string "Item"
|
|
85
|
-
*/
|
|
86
|
-
get itemTitle() {
|
|
87
|
-
const { schema, registry } = this.props;
|
|
88
|
-
const { translateString } = registry;
|
|
89
|
-
return get(schema, [ITEMS_KEY, 'title'], get(schema, [ITEMS_KEY, 'description'], translateString(TranslatableString.ArrayItemTitle)));
|
|
90
74
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
75
|
+
return addable;
|
|
76
|
+
}
|
|
77
|
+
/** Helper method to compute item UI schema for both normal and fixed arrays
|
|
78
|
+
* Handles both static object and dynamic function cases
|
|
79
|
+
*
|
|
80
|
+
* @param uiSchema - The parent UI schema containing items definition
|
|
81
|
+
* @param item - The item data
|
|
82
|
+
* @param index - The index of the item
|
|
83
|
+
* @param formContext - The form context
|
|
84
|
+
* @returns The computed UI schema for the item
|
|
85
|
+
*/
|
|
86
|
+
function computeItemUiSchema(uiSchema, item, index, formContext) {
|
|
87
|
+
if (typeof uiSchema.items === 'function') {
|
|
88
|
+
try {
|
|
89
|
+
// Call the function with item data, index, and form context
|
|
90
|
+
// TypeScript now correctly infers the types thanks to the ArrayElement type in UiSchema
|
|
91
|
+
const result = uiSchema.items(item, index, formContext);
|
|
92
|
+
// Only use the result if it's truthy
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
console.error(`Error executing dynamic uiSchema.items function for item at index ${index}:`, e);
|
|
97
|
+
// Fall back to undefined to allow the field to still render
|
|
98
|
+
return undefined;
|
|
102
99
|
}
|
|
103
|
-
// All non-null array item types are inherently required by design
|
|
104
|
-
return itemSchema.type !== 'null';
|
|
105
100
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
101
|
+
else {
|
|
102
|
+
// Static object case - preserve undefined to maintain backward compatibility
|
|
103
|
+
return uiSchema.items;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/** Returns the default form information for an item based on the schema for that item. Deals with the possibility
|
|
107
|
+
* that the schema is fixed and allows additional items.
|
|
108
|
+
*/
|
|
109
|
+
function getNewFormDataRow(registry, schema) {
|
|
110
|
+
const { schemaUtils } = registry;
|
|
111
|
+
let itemSchema = schema.items;
|
|
112
|
+
if (isFixedItems(schema) && allowAdditionalItems(schema)) {
|
|
113
|
+
itemSchema = schema.additionalItems;
|
|
114
|
+
}
|
|
115
|
+
// Cast this as a T to work around schema utils being for T[] caused by the FieldProps<T[], S, F> call on the class
|
|
116
|
+
return schemaUtils.getDefaultFormState(itemSchema);
|
|
117
|
+
}
|
|
118
|
+
/** Renders an array as a set of checkboxes using the 'select' widget
|
|
119
|
+
*/
|
|
120
|
+
function ArrayAsMultiSelect(props) {
|
|
121
|
+
const { schema, fieldPathId, uiSchema, formData: items = [], disabled = false, readonly = false, autofocus = false, required = false, placeholder, onBlur, onFocus, registry, rawErrors, name, onSelectChange, } = props;
|
|
122
|
+
const { widgets, schemaUtils, globalFormOptions, globalUiOptions } = registry;
|
|
123
|
+
const itemsSchema = schemaUtils.retrieveSchema(schema.items, items);
|
|
124
|
+
const enumOptions = optionsList(itemsSchema, uiSchema);
|
|
125
|
+
const { widget = 'select', title: uiTitle, ...options } = getUiOptions(uiSchema, globalUiOptions);
|
|
126
|
+
const Widget = getWidget(schema, widget, widgets);
|
|
127
|
+
const label = uiTitle ?? schema.title ?? name;
|
|
128
|
+
const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema, globalUiOptions);
|
|
129
|
+
// For custom widgets with multiple=true, generate a fieldPathId with isMultiValue flag
|
|
130
|
+
const multiValueFieldPathId = useDeepCompareMemo(toFieldPathId('', globalFormOptions, fieldPathId, true));
|
|
131
|
+
return (_jsx(Widget, { id: multiValueFieldPathId[ID_KEY], name: name, multiple: true, onChange: onSelectChange, onBlur: onBlur, onFocus: onFocus, options: { ...options, enumOptions }, schema: schema, uiSchema: uiSchema, registry: registry, value: items, disabled: disabled, readonly: readonly, required: required, label: label, hideLabel: !displayLabel, placeholder: placeholder, autofocus: autofocus, rawErrors: rawErrors, htmlName: multiValueFieldPathId.name }));
|
|
132
|
+
}
|
|
133
|
+
/** Renders an array using the custom widget provided by the user in the `uiSchema`
|
|
134
|
+
*/
|
|
135
|
+
function ArrayAsCustomWidget(props) {
|
|
136
|
+
const { schema, fieldPathId, uiSchema, disabled = false, readonly = false, autofocus = false, required = false, hideError, placeholder, onBlur, onFocus, formData: items = [], registry, rawErrors, name, onSelectChange, } = props;
|
|
137
|
+
const { widgets, schemaUtils, globalFormOptions, globalUiOptions } = registry;
|
|
138
|
+
const { widget, title: uiTitle, ...options } = getUiOptions(uiSchema, globalUiOptions);
|
|
139
|
+
const Widget = getWidget(schema, widget, widgets);
|
|
140
|
+
const label = uiTitle ?? schema.title ?? name;
|
|
141
|
+
const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema, globalUiOptions);
|
|
142
|
+
// For custom widgets with multiple=true, generate a fieldPathId with isMultiValue flag
|
|
143
|
+
const multiValueFieldPathId = useDeepCompareMemo(toFieldPathId('', globalFormOptions, fieldPathId, true));
|
|
144
|
+
return (_jsx(Widget, { id: multiValueFieldPathId[ID_KEY], name: name, multiple: true, onChange: onSelectChange, onBlur: onBlur, onFocus: onFocus, options: options, schema: schema, uiSchema: uiSchema, registry: registry, value: items, disabled: disabled, readonly: readonly, hideError: hideError, required: required, label: label, hideLabel: !displayLabel, placeholder: placeholder, autofocus: autofocus, rawErrors: rawErrors, htmlName: multiValueFieldPathId.name }));
|
|
145
|
+
}
|
|
146
|
+
/** Renders an array of files using the `FileWidget`
|
|
147
|
+
*/
|
|
148
|
+
function ArrayAsFiles(props) {
|
|
149
|
+
const { schema, uiSchema, fieldPathId, name, disabled = false, readonly = false, autofocus = false, required = false, onBlur, onFocus, registry, formData: items = [], rawErrors, onSelectChange, } = props;
|
|
150
|
+
const { widgets, schemaUtils, globalFormOptions, globalUiOptions } = registry;
|
|
151
|
+
const { widget = 'files', title: uiTitle, ...options } = getUiOptions(uiSchema, globalUiOptions);
|
|
152
|
+
const Widget = getWidget(schema, widget, widgets);
|
|
153
|
+
const label = uiTitle ?? schema.title ?? name;
|
|
154
|
+
const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema, globalUiOptions);
|
|
155
|
+
// For custom widgets with multiple=true, generate a fieldPathId with isMultiValue flag
|
|
156
|
+
const multiValueFieldPathId = useDeepCompareMemo(toFieldPathId('', globalFormOptions, fieldPathId, true));
|
|
157
|
+
return (_jsx(Widget, { options: options, id: multiValueFieldPathId[ID_KEY], name: name, multiple: true, onChange: onSelectChange, onBlur: onBlur, onFocus: onFocus, schema: schema, uiSchema: uiSchema, value: items, disabled: disabled, readonly: readonly, required: required, registry: registry, autofocus: autofocus, rawErrors: rawErrors, label: label, hideLabel: !displayLabel, htmlName: multiValueFieldPathId.name }));
|
|
158
|
+
}
|
|
159
|
+
/** Renders the individual array item using a `SchemaField` along with the additional properties that are needed to
|
|
160
|
+
* render the whole of the `ArrayFieldItemTemplate`.
|
|
161
|
+
*/
|
|
162
|
+
function ArrayFieldItem(props) {
|
|
163
|
+
const { itemKey, index, name, disabled, hideError, readonly, registry, uiOptions, parentUiSchema, canAdd, canRemove = true, canMoveUp, canMoveDown, itemSchema, itemData, itemUiSchema, itemFieldPathId, itemErrorSchema, autofocus, onBlur, onFocus, onChange, rawErrors, totalItems, title, handleAddItem, handleCopyItem, handleRemoveItem, handleReorderItems, } = props;
|
|
164
|
+
const { fields: { ArraySchemaField, SchemaField }, } = registry;
|
|
165
|
+
const fieldPathId = useDeepCompareMemo(itemFieldPathId);
|
|
166
|
+
const ItemSchemaField = ArraySchemaField || SchemaField;
|
|
167
|
+
const ArrayFieldItemTemplate = getTemplate('ArrayFieldItemTemplate', registry, uiOptions);
|
|
168
|
+
const { orderable = true, removable = true, copyable = false } = uiOptions;
|
|
169
|
+
const has = {
|
|
170
|
+
moveUp: orderable && canMoveUp,
|
|
171
|
+
moveDown: orderable && canMoveDown,
|
|
172
|
+
copy: copyable && canAdd,
|
|
173
|
+
remove: removable && canRemove,
|
|
174
|
+
toolbar: false,
|
|
175
|
+
};
|
|
176
|
+
has.toolbar = Object.keys(has).some((key) => has[key]);
|
|
177
|
+
const onAddItem = useCallback((event) => {
|
|
178
|
+
handleAddItem(event, index + 1);
|
|
179
|
+
}, [handleAddItem, index]);
|
|
180
|
+
const onCopyItem = useCallback((event) => {
|
|
181
|
+
handleCopyItem(event, index);
|
|
182
|
+
}, [handleCopyItem, index]);
|
|
183
|
+
const onRemoveItem = useCallback((event) => {
|
|
184
|
+
handleRemoveItem(event, index);
|
|
185
|
+
}, [handleRemoveItem, index]);
|
|
186
|
+
const onMoveUpItem = useCallback((event) => {
|
|
187
|
+
handleReorderItems(event, index, index - 1);
|
|
188
|
+
}, [handleReorderItems, index]);
|
|
189
|
+
const onMoveDownItem = useCallback((event) => {
|
|
190
|
+
handleReorderItems(event, index, index + 1);
|
|
191
|
+
}, [handleReorderItems, index]);
|
|
192
|
+
const templateProps = {
|
|
193
|
+
children: (_jsx(ItemSchemaField, { name: name, title: title, index: index, schema: itemSchema, uiSchema: itemUiSchema, formData: itemData, errorSchema: itemErrorSchema, fieldPathId: fieldPathId, required: isItemRequired(itemSchema), onChange: onChange, onBlur: onBlur, onFocus: onFocus, registry: registry, disabled: disabled, readonly: readonly, hideError: hideError, autofocus: autofocus, rawErrors: rawErrors })),
|
|
194
|
+
buttonsProps: {
|
|
195
|
+
fieldPathId,
|
|
196
|
+
disabled,
|
|
197
|
+
readonly,
|
|
198
|
+
canAdd,
|
|
199
|
+
hasCopy: has.copy,
|
|
200
|
+
hasMoveUp: has.moveUp,
|
|
201
|
+
hasMoveDown: has.moveDown,
|
|
202
|
+
hasRemove: has.remove,
|
|
203
|
+
index: index,
|
|
204
|
+
totalItems,
|
|
205
|
+
onAddItem,
|
|
206
|
+
onCopyItem,
|
|
207
|
+
onRemoveItem,
|
|
208
|
+
onMoveUpItem,
|
|
209
|
+
onMoveDownItem,
|
|
210
|
+
registry,
|
|
211
|
+
schema: itemSchema,
|
|
212
|
+
uiSchema: itemUiSchema,
|
|
213
|
+
},
|
|
214
|
+
itemKey,
|
|
215
|
+
className: 'rjsf-array-item',
|
|
216
|
+
disabled,
|
|
217
|
+
hasToolbar: has.toolbar,
|
|
218
|
+
index,
|
|
219
|
+
totalItems,
|
|
220
|
+
readonly,
|
|
221
|
+
registry,
|
|
222
|
+
schema: itemSchema,
|
|
223
|
+
uiSchema: itemUiSchema,
|
|
224
|
+
parentUiSchema,
|
|
225
|
+
};
|
|
226
|
+
return _jsx(ArrayFieldItemTemplate, { ...templateProps });
|
|
227
|
+
}
|
|
228
|
+
/** Renders a normal array without any limitations of length
|
|
229
|
+
*/
|
|
230
|
+
function NormalArray(props) {
|
|
231
|
+
const { schema, uiSchema = {}, errorSchema, fieldPathId, formData: formDataFromProps, name, title, disabled = false, readonly = false, autofocus = false, required = false, hideError = false, registry, onBlur, onFocus, rawErrors, onChange, keyedFormData, handleAddItem, handleCopyItem, handleRemoveItem, handleReorderItems, } = props;
|
|
232
|
+
const fieldTitle = schema.title || title || name;
|
|
233
|
+
const { schemaUtils, fields, formContext, globalFormOptions, globalUiOptions } = registry;
|
|
234
|
+
const { OptionalDataControlsField } = fields;
|
|
235
|
+
const uiOptions = getUiOptions(uiSchema, globalUiOptions);
|
|
236
|
+
const _schemaItems = isObject(schema.items) ? schema.items : {};
|
|
237
|
+
const itemsSchema = schemaUtils.retrieveSchema(_schemaItems);
|
|
238
|
+
const formData = keyedToPlainFormData(keyedFormData);
|
|
239
|
+
const renderOptionalField = shouldRenderOptionalField(registry, schema, required, uiSchema);
|
|
240
|
+
const hasFormData = isFormDataAvailable(formDataFromProps);
|
|
241
|
+
const canAdd = canAddItem(registry, schema, formData, uiSchema) && (!renderOptionalField || hasFormData);
|
|
242
|
+
const actualFormData = hasFormData ? keyedFormData : [];
|
|
243
|
+
const extraClass = renderOptionalField ? ' rjsf-optional-array-field' : '';
|
|
244
|
+
// All the children will use childFieldPathId if present in the props, falling back to the fieldPathId
|
|
245
|
+
const childFieldPathId = props.childFieldPathId ?? fieldPathId;
|
|
246
|
+
const optionalDataControl = renderOptionalField ? (_jsx(OptionalDataControlsField, { ...props, fieldPathId: childFieldPathId })) : undefined;
|
|
247
|
+
const arrayProps = {
|
|
248
|
+
canAdd,
|
|
249
|
+
items: actualFormData.map((keyedItem, index) => {
|
|
250
|
+
const { key, item } = keyedItem;
|
|
251
|
+
// While we are actually dealing with a single item of type T, the types require a T[], so cast
|
|
252
|
+
const itemCast = item;
|
|
253
|
+
const itemSchema = schemaUtils.retrieveSchema(_schemaItems, itemCast);
|
|
254
|
+
const itemErrorSchema = errorSchema ? errorSchema[index] : undefined;
|
|
255
|
+
const itemFieldPathId = toFieldPathId(index, globalFormOptions, childFieldPathId);
|
|
256
|
+
// Compute the item UI schema using the helper method
|
|
257
|
+
const itemUiSchema = computeItemUiSchema(uiSchema, item, index, formContext);
|
|
258
|
+
const itemProps = {
|
|
259
|
+
itemKey: key,
|
|
260
|
+
index,
|
|
261
|
+
name: name && `${name}-${index}`,
|
|
262
|
+
registry,
|
|
263
|
+
uiOptions,
|
|
264
|
+
hideError,
|
|
265
|
+
readonly,
|
|
266
|
+
disabled,
|
|
267
|
+
required,
|
|
268
|
+
title: fieldTitle ? `${fieldTitle}-${index + 1}` : undefined,
|
|
269
|
+
canAdd,
|
|
270
|
+
canMoveUp: index > 0,
|
|
271
|
+
canMoveDown: index < formData.length - 1,
|
|
272
|
+
itemSchema,
|
|
273
|
+
itemFieldPathId,
|
|
274
|
+
itemErrorSchema,
|
|
275
|
+
itemData: itemCast,
|
|
276
|
+
itemUiSchema,
|
|
277
|
+
autofocus: autofocus && index === 0,
|
|
278
|
+
onBlur,
|
|
279
|
+
onFocus,
|
|
280
|
+
rawErrors,
|
|
281
|
+
totalItems: keyedFormData.length,
|
|
282
|
+
handleAddItem,
|
|
283
|
+
handleCopyItem,
|
|
284
|
+
handleRemoveItem,
|
|
285
|
+
handleReorderItems,
|
|
286
|
+
onChange,
|
|
287
|
+
};
|
|
288
|
+
return _jsx(ArrayFieldItem, { ...itemProps }, key);
|
|
289
|
+
}),
|
|
290
|
+
className: `rjsf-field rjsf-field-array rjsf-field-array-of-${itemsSchema.type}${extraClass}`,
|
|
291
|
+
disabled,
|
|
292
|
+
fieldPathId,
|
|
293
|
+
uiSchema,
|
|
294
|
+
onAddClick: handleAddItem,
|
|
295
|
+
readonly,
|
|
296
|
+
required,
|
|
297
|
+
schema,
|
|
298
|
+
title: fieldTitle,
|
|
299
|
+
formData,
|
|
300
|
+
rawErrors,
|
|
301
|
+
registry,
|
|
302
|
+
optionalDataControl,
|
|
303
|
+
};
|
|
304
|
+
const Template = getTemplate('ArrayFieldTemplate', registry, uiOptions);
|
|
305
|
+
return _jsx(Template, { ...arrayProps });
|
|
306
|
+
}
|
|
307
|
+
/** Renders an array that has a maximum limit of items
|
|
308
|
+
*/
|
|
309
|
+
function FixedArray(props) {
|
|
310
|
+
const { schema, uiSchema = {}, formData, errorSchema, fieldPathId, name, title, disabled = false, readonly = false, autofocus = false, required = false, hideError = false, registry, onBlur, onFocus, rawErrors, keyedFormData, onChange, handleAddItem, handleCopyItem, handleRemoveItem, handleReorderItems, } = props;
|
|
311
|
+
let { formData: items = [] } = props;
|
|
312
|
+
const fieldTitle = schema.title || title || name;
|
|
313
|
+
const { schemaUtils, fields, formContext, globalFormOptions, globalUiOptions } = registry;
|
|
314
|
+
const uiOptions = getUiOptions(uiSchema, globalUiOptions);
|
|
315
|
+
const { OptionalDataControlsField } = fields;
|
|
316
|
+
const renderOptionalField = shouldRenderOptionalField(registry, schema, required, uiSchema);
|
|
317
|
+
const hasFormData = isFormDataAvailable(formData);
|
|
318
|
+
const _schemaItems = isObject(schema.items) ? schema.items : [];
|
|
319
|
+
const itemSchemas = _schemaItems.map((item, index) => schemaUtils.retrieveSchema(item, items[index]));
|
|
320
|
+
const additionalSchema = isObject(schema.additionalItems)
|
|
321
|
+
? schemaUtils.retrieveSchema(schema.additionalItems, formData)
|
|
322
|
+
: null;
|
|
323
|
+
// All the children will use childFieldPathId if present in the props, falling back to the fieldPathId
|
|
324
|
+
const childFieldPathId = props.childFieldPathId ?? fieldPathId;
|
|
325
|
+
if (items.length < itemSchemas.length) {
|
|
326
|
+
// to make sure at least all fixed items are generated
|
|
327
|
+
items = items.concat(new Array(itemSchemas.length - items.length));
|
|
328
|
+
}
|
|
329
|
+
const actualFormData = hasFormData ? keyedFormData : [];
|
|
330
|
+
const extraClass = renderOptionalField ? ' rjsf-optional-array-field' : '';
|
|
331
|
+
const optionalDataControl = renderOptionalField ? (_jsx(OptionalDataControlsField, { ...props, fieldPathId: childFieldPathId })) : undefined;
|
|
332
|
+
// These are the props passed into the render function
|
|
333
|
+
const canAdd = canAddItem(registry, schema, items, uiSchema) &&
|
|
334
|
+
!!additionalSchema &&
|
|
335
|
+
(!renderOptionalField || hasFormData);
|
|
336
|
+
const arrayProps = {
|
|
337
|
+
canAdd,
|
|
338
|
+
className: `rjsf-field rjsf-field-array rjsf-field-array-fixed-items${extraClass}`,
|
|
339
|
+
disabled,
|
|
340
|
+
fieldPathId,
|
|
341
|
+
formData,
|
|
342
|
+
items: actualFormData.map((keyedItem, index) => {
|
|
343
|
+
const { key, item } = keyedItem;
|
|
344
|
+
// While we are actually dealing with a single item of type T, the types require a T[], so cast
|
|
345
|
+
const itemCast = item;
|
|
346
|
+
const additional = index >= itemSchemas.length;
|
|
347
|
+
const itemSchema = (additional && isObject(schema.additionalItems)
|
|
348
|
+
? schemaUtils.retrieveSchema(schema.additionalItems, itemCast)
|
|
349
|
+
: itemSchemas[index]) || {};
|
|
350
|
+
const itemFieldPathId = toFieldPathId(index, globalFormOptions, childFieldPathId);
|
|
351
|
+
// Compute the item UI schema - handle both static and dynamic cases
|
|
352
|
+
let itemUiSchema;
|
|
353
|
+
if (additional) {
|
|
354
|
+
// For additional items, use additionalItems uiSchema
|
|
355
|
+
itemUiSchema = uiSchema.additionalItems;
|
|
121
356
|
}
|
|
122
357
|
else {
|
|
123
|
-
|
|
358
|
+
// For fixed items, uiSchema.items can be an array, a function, or a single object
|
|
359
|
+
if (Array.isArray(uiSchema.items)) {
|
|
360
|
+
itemUiSchema = uiSchema.items[index];
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
// Use the helper method for function or static object cases
|
|
364
|
+
itemUiSchema = computeItemUiSchema(uiSchema, item, index, formContext);
|
|
365
|
+
}
|
|
124
366
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
367
|
+
const itemErrorSchema = errorSchema ? errorSchema[index] : undefined;
|
|
368
|
+
const itemProps = {
|
|
369
|
+
index,
|
|
370
|
+
itemKey: key,
|
|
371
|
+
name: name && `${name}-${index}`,
|
|
372
|
+
registry,
|
|
373
|
+
uiOptions,
|
|
374
|
+
hideError,
|
|
375
|
+
readonly,
|
|
376
|
+
disabled,
|
|
377
|
+
required,
|
|
378
|
+
title: fieldTitle ? `${fieldTitle}-${index + 1}` : undefined,
|
|
379
|
+
canAdd,
|
|
380
|
+
canRemove: additional,
|
|
381
|
+
canMoveUp: index >= itemSchemas.length + 1,
|
|
382
|
+
canMoveDown: additional && index < items.length - 1,
|
|
383
|
+
itemSchema,
|
|
384
|
+
itemData: itemCast,
|
|
385
|
+
itemUiSchema,
|
|
386
|
+
itemFieldPathId,
|
|
387
|
+
itemErrorSchema,
|
|
388
|
+
autofocus: autofocus && index === 0,
|
|
389
|
+
onBlur,
|
|
390
|
+
onFocus,
|
|
391
|
+
rawErrors,
|
|
392
|
+
totalItems: keyedFormData.length,
|
|
393
|
+
onChange,
|
|
394
|
+
handleAddItem,
|
|
395
|
+
handleCopyItem,
|
|
396
|
+
handleRemoveItem,
|
|
397
|
+
handleReorderItems,
|
|
398
|
+
};
|
|
399
|
+
return _jsx(ArrayFieldItem, { ...itemProps }, key);
|
|
400
|
+
}),
|
|
401
|
+
onAddClick: handleAddItem,
|
|
402
|
+
readonly,
|
|
403
|
+
required,
|
|
404
|
+
registry,
|
|
405
|
+
schema,
|
|
406
|
+
uiSchema,
|
|
407
|
+
title: fieldTitle,
|
|
408
|
+
errorSchema,
|
|
409
|
+
rawErrors,
|
|
410
|
+
optionalDataControl,
|
|
140
411
|
};
|
|
412
|
+
const Template = getTemplate('ArrayFieldTemplate', registry, uiOptions);
|
|
413
|
+
return _jsx(Template, { ...arrayProps });
|
|
414
|
+
}
|
|
415
|
+
/** A custom hook that handles the updating of the keyedFormData from an external `formData` change as well as
|
|
416
|
+
* internally by the `ArrayField`. If there was an external `formData` change, then the `keyedFormData` is recomputed
|
|
417
|
+
* in order to preserve the unique keys from the old `keyedFormData` to the new `formData`. Along with the
|
|
418
|
+
* `keyedFormData` this hook also returns an `updateKeyedFormData()` function for use by the `ArrayField`. The detection
|
|
419
|
+
* of external `formData` are handled by storing the hash of that `formData` along with the `keyedFormData` associated
|
|
420
|
+
* with it. The `updateKeyedFormData()` will update that hash whenever the `keyedFormData` is modified and as well as
|
|
421
|
+
* returning the plain `formData` from the `keyedFormData`.
|
|
422
|
+
*/
|
|
423
|
+
function useKeyedFormData(formData = []) {
|
|
424
|
+
const newHash = useMemo(() => hashObject(formData), [formData]);
|
|
425
|
+
const [state, setState] = useState(() => ({
|
|
426
|
+
formDataHash: newHash,
|
|
427
|
+
keyedFormData: generateKeyedFormData(formData),
|
|
428
|
+
}));
|
|
429
|
+
let { keyedFormData, formDataHash } = state;
|
|
430
|
+
if (newHash !== formDataHash) {
|
|
431
|
+
const nextFormData = Array.isArray(formData) ? formData : [];
|
|
432
|
+
const previousKeyedFormData = keyedFormData || [];
|
|
433
|
+
keyedFormData =
|
|
434
|
+
nextFormData.length === previousKeyedFormData.length
|
|
435
|
+
? previousKeyedFormData.map((previousKeyedFormDatum, index) => ({
|
|
436
|
+
key: previousKeyedFormDatum.key,
|
|
437
|
+
item: nextFormData[index],
|
|
438
|
+
}))
|
|
439
|
+
: generateKeyedFormData(nextFormData);
|
|
440
|
+
formDataHash = newHash;
|
|
441
|
+
setState({ formDataHash, keyedFormData });
|
|
442
|
+
}
|
|
443
|
+
const updateKeyedFormData = useCallback((newData) => {
|
|
444
|
+
const plainFormData = keyedToPlainFormData(newData);
|
|
445
|
+
const newHash = hashObject(plainFormData);
|
|
446
|
+
setState({ formDataHash: newHash, keyedFormData: newData });
|
|
447
|
+
return plainFormData;
|
|
448
|
+
}, []);
|
|
449
|
+
return { keyedFormData, updateKeyedFormData };
|
|
450
|
+
}
|
|
451
|
+
/** The `ArrayField` component is used to render a field in the schema that is of type `array`. It supports both normal
|
|
452
|
+
* and fixed array, allowing user to add and remove elements from the array data.
|
|
453
|
+
*/
|
|
454
|
+
export default function ArrayField(props) {
|
|
455
|
+
const { schema, uiSchema, errorSchema, fieldPathId, registry, formData, onChange } = props;
|
|
456
|
+
const { schemaUtils, translateString } = registry;
|
|
457
|
+
const { keyedFormData, updateKeyedFormData } = useKeyedFormData(formData);
|
|
458
|
+
// All the children will use childFieldPathId if present in the props, falling back to the fieldPathId
|
|
459
|
+
const childFieldPathId = props.childFieldPathId ?? fieldPathId;
|
|
141
460
|
/** Callback handler for when the user clicks on the add or add at index buttons. Creates a new row of keyed form data
|
|
142
461
|
* either at the end of the list (when index is not specified) or inserted at the `index` when it is, adding it into
|
|
143
462
|
* the state, and then returning `onChange()` with the plain form data converted from the keyed data
|
|
@@ -145,13 +464,10 @@ class ArrayField extends Component {
|
|
|
145
464
|
* @param event - The event for the click
|
|
146
465
|
* @param [index] - The optional index at which to add the new data
|
|
147
466
|
*/
|
|
148
|
-
|
|
467
|
+
const handleAddItem = useCallback((event, index) => {
|
|
149
468
|
if (event) {
|
|
150
469
|
event.preventDefault();
|
|
151
470
|
}
|
|
152
|
-
const { onChange, errorSchema } = this.props;
|
|
153
|
-
const { keyedFormData } = this.state;
|
|
154
|
-
// refs #195: revalidate to ensure properly reindexing errors
|
|
155
471
|
let newErrorSchema;
|
|
156
472
|
if (errorSchema) {
|
|
157
473
|
newErrorSchema = {};
|
|
@@ -167,7 +483,7 @@ class ArrayField extends Component {
|
|
|
167
483
|
}
|
|
168
484
|
const newKeyedFormDataRow = {
|
|
169
485
|
key: generateRowId(),
|
|
170
|
-
item:
|
|
486
|
+
item: getNewFormDataRow(registry, schema),
|
|
171
487
|
};
|
|
172
488
|
const newKeyedFormData = [...keyedFormData];
|
|
173
489
|
if (index !== undefined) {
|
|
@@ -176,109 +492,71 @@ class ArrayField extends Component {
|
|
|
176
492
|
else {
|
|
177
493
|
newKeyedFormData.push(newKeyedFormDataRow);
|
|
178
494
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
updatedKeyedFormData: true,
|
|
182
|
-
}, () => onChange(keyedToPlainFormData(newKeyedFormData), newErrorSchema));
|
|
183
|
-
}
|
|
184
|
-
/** Callback handler for when the user clicks on the add button. Creates a new row of keyed form data at the end of
|
|
185
|
-
* the list, adding it into the state, and then returning `onChange()` with the plain form data converted from the
|
|
186
|
-
* keyed data
|
|
187
|
-
*
|
|
188
|
-
* @param event - The event for the click
|
|
189
|
-
*/
|
|
190
|
-
onAddClick = (event) => {
|
|
191
|
-
this._handleAddClick(event);
|
|
192
|
-
};
|
|
193
|
-
/** Callback handler for when the user clicks on the add button on an existing array element. Creates a new row of
|
|
194
|
-
* keyed form data inserted at the `index`, adding it into the state, and then returning `onChange()` with the plain
|
|
195
|
-
* form data converted from the keyed data
|
|
196
|
-
*
|
|
197
|
-
* @param index - The index at which the add button is clicked
|
|
198
|
-
*/
|
|
199
|
-
onAddIndexClick = (index) => {
|
|
200
|
-
return (event) => {
|
|
201
|
-
this._handleAddClick(event, index);
|
|
202
|
-
};
|
|
203
|
-
};
|
|
495
|
+
onChange(updateKeyedFormData(newKeyedFormData), childFieldPathId.path, newErrorSchema);
|
|
496
|
+
}, [keyedFormData, registry, schema, onChange, updateKeyedFormData, errorSchema, childFieldPathId]);
|
|
204
497
|
/** Callback handler for when the user clicks on the copy button on an existing array element. Clones the row of
|
|
205
498
|
* keyed form data at the `index` into the next position in the state, and then returning `onChange()` with the plain
|
|
206
499
|
* form data converted from the keyed data
|
|
207
500
|
*
|
|
208
501
|
* @param index - The index at which the copy button is clicked
|
|
209
502
|
*/
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
set(newErrorSchema, [i], errorSchema[idx]);
|
|
225
|
-
}
|
|
226
|
-
else if (i > index) {
|
|
227
|
-
set(newErrorSchema, [i + 1], errorSchema[idx]);
|
|
228
|
-
}
|
|
503
|
+
const handleCopyItem = useCallback((event, index) => {
|
|
504
|
+
if (event) {
|
|
505
|
+
event.preventDefault();
|
|
506
|
+
}
|
|
507
|
+
let newErrorSchema;
|
|
508
|
+
if (errorSchema) {
|
|
509
|
+
newErrorSchema = {};
|
|
510
|
+
for (const idx in errorSchema) {
|
|
511
|
+
const i = parseInt(idx);
|
|
512
|
+
if (i <= index) {
|
|
513
|
+
set(newErrorSchema, [i], errorSchema[idx]);
|
|
514
|
+
}
|
|
515
|
+
else if (i > index) {
|
|
516
|
+
set(newErrorSchema, [i + 1], errorSchema[idx]);
|
|
229
517
|
}
|
|
230
518
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const newKeyedFormData = [...keyedFormData];
|
|
236
|
-
if (index !== undefined) {
|
|
237
|
-
newKeyedFormData.splice(index + 1, 0, newKeyedFormDataRow);
|
|
238
|
-
}
|
|
239
|
-
else {
|
|
240
|
-
newKeyedFormData.push(newKeyedFormDataRow);
|
|
241
|
-
}
|
|
242
|
-
this.setState({
|
|
243
|
-
keyedFormData: newKeyedFormData,
|
|
244
|
-
updatedKeyedFormData: true,
|
|
245
|
-
}, () => onChange(keyedToPlainFormData(newKeyedFormData), newErrorSchema));
|
|
519
|
+
}
|
|
520
|
+
const newKeyedFormDataRow = {
|
|
521
|
+
key: generateRowId(),
|
|
522
|
+
item: cloneDeep(keyedFormData[index].item),
|
|
246
523
|
};
|
|
247
|
-
|
|
524
|
+
const newKeyedFormData = [...keyedFormData];
|
|
525
|
+
if (index !== undefined) {
|
|
526
|
+
newKeyedFormData.splice(index + 1, 0, newKeyedFormDataRow);
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
newKeyedFormData.push(newKeyedFormDataRow);
|
|
530
|
+
}
|
|
531
|
+
onChange(updateKeyedFormData(newKeyedFormData), childFieldPathId.path, newErrorSchema);
|
|
532
|
+
}, [keyedFormData, onChange, updateKeyedFormData, errorSchema, childFieldPathId]);
|
|
248
533
|
/** Callback handler for when the user clicks on the remove button on an existing array element. Removes the row of
|
|
249
534
|
* keyed form data at the `index` in the state, and then returning `onChange()` with the plain form data converted
|
|
250
535
|
* from the keyed data
|
|
251
536
|
*
|
|
252
537
|
* @param index - The index at which the remove button is clicked
|
|
253
538
|
*/
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
270
|
-
else if (i > index) {
|
|
271
|
-
set(newErrorSchema, [i - 1], errorSchema[idx]);
|
|
272
|
-
}
|
|
539
|
+
const handleRemoveItem = useCallback((event, index) => {
|
|
540
|
+
if (event) {
|
|
541
|
+
event.preventDefault();
|
|
542
|
+
}
|
|
543
|
+
// refs #195: revalidate to ensure properly reindexing errors
|
|
544
|
+
let newErrorSchema;
|
|
545
|
+
if (errorSchema) {
|
|
546
|
+
newErrorSchema = {};
|
|
547
|
+
for (const idx in errorSchema) {
|
|
548
|
+
const i = parseInt(idx);
|
|
549
|
+
if (i < index) {
|
|
550
|
+
set(newErrorSchema, [i], errorSchema[idx]);
|
|
551
|
+
}
|
|
552
|
+
else if (i > index) {
|
|
553
|
+
set(newErrorSchema, [i - 1], errorSchema[idx]);
|
|
273
554
|
}
|
|
274
555
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}, () => onChange(keyedToPlainFormData(newKeyedFormData), newErrorSchema));
|
|
280
|
-
};
|
|
281
|
-
};
|
|
556
|
+
}
|
|
557
|
+
const newKeyedFormData = keyedFormData.filter((_, i) => i !== index);
|
|
558
|
+
onChange(updateKeyedFormData(newKeyedFormData), childFieldPathId.path, newErrorSchema);
|
|
559
|
+
}, [keyedFormData, onChange, updateKeyedFormData, errorSchema, childFieldPathId]);
|
|
282
560
|
/** Callback handler for when the user clicks on one of the move item buttons on an existing array element. Moves the
|
|
283
561
|
* row of keyed form data at the `index` to the `newIndex` in the state, and then returning `onChange()` with the
|
|
284
562
|
* plain form data converted from the keyed data
|
|
@@ -286,322 +564,78 @@ class ArrayField extends Component {
|
|
|
286
564
|
* @param index - The index of the item to move
|
|
287
565
|
* @param newIndex - The index to where the item is to be moved
|
|
288
566
|
*/
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
else {
|
|
308
|
-
set(newErrorSchema, [idx], errorSchema[i]);
|
|
309
|
-
}
|
|
567
|
+
const handleReorderItems = useCallback((event, index, newIndex) => {
|
|
568
|
+
if (event) {
|
|
569
|
+
event.preventDefault();
|
|
570
|
+
event.currentTarget.blur();
|
|
571
|
+
}
|
|
572
|
+
let newErrorSchema;
|
|
573
|
+
if (errorSchema) {
|
|
574
|
+
newErrorSchema = {};
|
|
575
|
+
for (const idx in errorSchema) {
|
|
576
|
+
const i = parseInt(idx);
|
|
577
|
+
if (i == index) {
|
|
578
|
+
set(newErrorSchema, [newIndex], errorSchema[index]);
|
|
579
|
+
}
|
|
580
|
+
else if (i == newIndex) {
|
|
581
|
+
set(newErrorSchema, [index], errorSchema[newIndex]);
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
set(newErrorSchema, [idx], errorSchema[i]);
|
|
310
585
|
}
|
|
311
586
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
}, () => onChange(keyedToPlainFormData(newKeyedFormData), newErrorSchema));
|
|
325
|
-
};
|
|
326
|
-
};
|
|
587
|
+
}
|
|
588
|
+
function reOrderArray() {
|
|
589
|
+
// Copy item
|
|
590
|
+
const _newKeyedFormData = keyedFormData.slice();
|
|
591
|
+
// Moves item from index to newIndex
|
|
592
|
+
_newKeyedFormData.splice(index, 1);
|
|
593
|
+
_newKeyedFormData.splice(newIndex, 0, keyedFormData[index]);
|
|
594
|
+
return _newKeyedFormData;
|
|
595
|
+
}
|
|
596
|
+
const newKeyedFormData = reOrderArray();
|
|
597
|
+
onChange(updateKeyedFormData(newKeyedFormData), childFieldPathId.path, newErrorSchema);
|
|
598
|
+
}, [keyedFormData, onChange, updateKeyedFormData, errorSchema, childFieldPathId]);
|
|
327
599
|
/** Callback handler used to deal with changing the value of the data in the array at the `index`. Calls the
|
|
328
600
|
* `onChange` callback with the updated form data
|
|
329
601
|
*
|
|
330
602
|
* @param index - The index of the item being changed
|
|
331
603
|
*/
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
// See https://github.com/tdegrunt/jsonschema/issues/206
|
|
339
|
-
const jsonValue = typeof value === 'undefined' ? null : value;
|
|
340
|
-
return index === i ? jsonValue : item;
|
|
341
|
-
});
|
|
342
|
-
onChange(newFormData, errorSchema &&
|
|
343
|
-
errorSchema && {
|
|
344
|
-
...errorSchema,
|
|
345
|
-
[index]: newErrorSchema,
|
|
346
|
-
}, id);
|
|
347
|
-
};
|
|
348
|
-
};
|
|
604
|
+
const handleChange = useCallback((value, path, newErrorSchema, id) => {
|
|
605
|
+
onChange(
|
|
606
|
+
// We need to treat undefined items as nulls to have validation.
|
|
607
|
+
// See https://github.com/tdegrunt/jsonschema/issues/206
|
|
608
|
+
value === undefined ? null : value, path, newErrorSchema, id);
|
|
609
|
+
}, [onChange]);
|
|
349
610
|
/** Callback handler used to change the value for a checkbox */
|
|
350
|
-
onSelectChange = (value) => {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
/** Renders the `ArrayField` depending on the specific needs of the schema and uischema elements
|
|
355
|
-
*/
|
|
356
|
-
render() {
|
|
357
|
-
const { schema, uiSchema, idSchema, registry } = this.props;
|
|
358
|
-
const { schemaUtils, translateString } = registry;
|
|
359
|
-
if (!(ITEMS_KEY in schema)) {
|
|
360
|
-
const uiOptions = getUiOptions(uiSchema);
|
|
361
|
-
const UnsupportedFieldTemplate = getTemplate('UnsupportedFieldTemplate', registry, uiOptions);
|
|
362
|
-
return (_jsx(UnsupportedFieldTemplate, { schema: schema, idSchema: idSchema, reason: translateString(TranslatableString.MissingItems), registry: registry }));
|
|
363
|
-
}
|
|
364
|
-
if (schemaUtils.isMultiSelect(schema)) {
|
|
365
|
-
// If array has enum or uniqueItems set to true, call renderMultiSelect() to render the default multiselect widget or a custom widget, if specified.
|
|
366
|
-
return this.renderMultiSelect();
|
|
367
|
-
}
|
|
368
|
-
if (isCustomWidget(uiSchema)) {
|
|
369
|
-
return this.renderCustomWidget();
|
|
370
|
-
}
|
|
371
|
-
if (isFixedItems(schema)) {
|
|
372
|
-
return this.renderFixedArray();
|
|
373
|
-
}
|
|
374
|
-
if (schemaUtils.isFilesArray(schema, uiSchema)) {
|
|
375
|
-
return this.renderFiles();
|
|
376
|
-
}
|
|
377
|
-
return this.renderNormalArray();
|
|
378
|
-
}
|
|
379
|
-
/** Renders a normal array without any limitations of length
|
|
380
|
-
*/
|
|
381
|
-
renderNormalArray() {
|
|
382
|
-
const { schema, uiSchema = {}, errorSchema, idSchema, name, title, disabled = false, readonly = false, autofocus = false, required = false, registry, onBlur, onFocus, idPrefix, idSeparator = '_', rawErrors, } = this.props;
|
|
383
|
-
const { keyedFormData } = this.state;
|
|
384
|
-
const fieldTitle = schema.title || title || name;
|
|
385
|
-
const { schemaUtils, formContext } = registry;
|
|
611
|
+
const onSelectChange = useCallback((value) => {
|
|
612
|
+
onChange(value, childFieldPathId.path, undefined, childFieldPathId?.[ID_KEY]);
|
|
613
|
+
}, [onChange, childFieldPathId]);
|
|
614
|
+
if (!(ITEMS_KEY in schema)) {
|
|
386
615
|
const uiOptions = getUiOptions(uiSchema);
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
const formData = keyedToPlainFormData(this.state.keyedFormData);
|
|
390
|
-
const canAdd = this.canAddItem(formData);
|
|
391
|
-
const arrayProps = {
|
|
392
|
-
canAdd,
|
|
393
|
-
items: keyedFormData.map((keyedItem, index) => {
|
|
394
|
-
const { key, item } = keyedItem;
|
|
395
|
-
// While we are actually dealing with a single item of type T, the types require a T[], so cast
|
|
396
|
-
const itemCast = item;
|
|
397
|
-
const itemSchema = schemaUtils.retrieveSchema(_schemaItems, itemCast);
|
|
398
|
-
const itemErrorSchema = errorSchema ? errorSchema[index] : undefined;
|
|
399
|
-
const itemIdPrefix = idSchema.$id + idSeparator + index;
|
|
400
|
-
const itemIdSchema = schemaUtils.toIdSchema(itemSchema, itemIdPrefix, itemCast, idPrefix, idSeparator);
|
|
401
|
-
return this.renderArrayFieldItem({
|
|
402
|
-
key,
|
|
403
|
-
index,
|
|
404
|
-
name: name && `${name}-${index}`,
|
|
405
|
-
title: fieldTitle ? `${fieldTitle}-${index + 1}` : undefined,
|
|
406
|
-
canAdd,
|
|
407
|
-
canMoveUp: index > 0,
|
|
408
|
-
canMoveDown: index < formData.length - 1,
|
|
409
|
-
itemSchema,
|
|
410
|
-
itemIdSchema,
|
|
411
|
-
itemErrorSchema,
|
|
412
|
-
itemData: itemCast,
|
|
413
|
-
itemUiSchema: uiSchema.items,
|
|
414
|
-
autofocus: autofocus && index === 0,
|
|
415
|
-
onBlur,
|
|
416
|
-
onFocus,
|
|
417
|
-
rawErrors,
|
|
418
|
-
totalItems: keyedFormData.length,
|
|
419
|
-
});
|
|
420
|
-
}),
|
|
421
|
-
className: `rjsf-field rjsf-field-array rjsf-field-array-of-${itemsSchema.type}`,
|
|
422
|
-
disabled,
|
|
423
|
-
idSchema,
|
|
424
|
-
uiSchema,
|
|
425
|
-
onAddClick: this.onAddClick,
|
|
426
|
-
readonly,
|
|
427
|
-
required,
|
|
428
|
-
schema,
|
|
429
|
-
title: fieldTitle,
|
|
430
|
-
formContext,
|
|
431
|
-
formData,
|
|
432
|
-
rawErrors,
|
|
433
|
-
registry,
|
|
434
|
-
};
|
|
435
|
-
const Template = getTemplate('ArrayFieldTemplate', registry, uiOptions);
|
|
436
|
-
return _jsx(Template, { ...arrayProps });
|
|
437
|
-
}
|
|
438
|
-
/** Renders an array using the custom widget provided by the user in the `uiSchema`
|
|
439
|
-
*/
|
|
440
|
-
renderCustomWidget() {
|
|
441
|
-
const { schema, idSchema, uiSchema, disabled = false, readonly = false, autofocus = false, required = false, hideError, placeholder, onBlur, onFocus, formData: items = [], registry, rawErrors, name, } = this.props;
|
|
442
|
-
const { widgets, formContext, globalUiOptions, schemaUtils } = registry;
|
|
443
|
-
const { widget, title: uiTitle, ...options } = getUiOptions(uiSchema, globalUiOptions);
|
|
444
|
-
const Widget = getWidget(schema, widget, widgets);
|
|
445
|
-
const label = uiTitle ?? schema.title ?? name;
|
|
446
|
-
const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema, globalUiOptions);
|
|
447
|
-
return (_jsx(Widget, { id: idSchema.$id, name: name, multiple: true, onChange: this.onSelectChange, onBlur: onBlur, onFocus: onFocus, options: options, schema: schema, uiSchema: uiSchema, registry: registry, value: items, disabled: disabled, readonly: readonly, hideError: hideError, required: required, label: label, hideLabel: !displayLabel, placeholder: placeholder, formContext: formContext, autofocus: autofocus, rawErrors: rawErrors }));
|
|
616
|
+
const UnsupportedFieldTemplate = getTemplate('UnsupportedFieldTemplate', registry, uiOptions);
|
|
617
|
+
return (_jsx(UnsupportedFieldTemplate, { schema: schema, fieldPathId: fieldPathId, reason: translateString(TranslatableString.MissingItems), registry: registry }));
|
|
448
618
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
return (_jsx(Widget, { id: idSchema.$id, name: name, multiple: true, onChange: this.onSelectChange, onBlur: onBlur, onFocus: onFocus, options: { ...options, enumOptions }, schema: schema, uiSchema: uiSchema, registry: registry, value: items, disabled: disabled, readonly: readonly, required: required, label: label, hideLabel: !displayLabel, placeholder: placeholder, formContext: formContext, autofocus: autofocus, rawErrors: rawErrors }));
|
|
619
|
+
const arrayProps = {
|
|
620
|
+
handleAddItem,
|
|
621
|
+
handleCopyItem,
|
|
622
|
+
handleRemoveItem,
|
|
623
|
+
handleReorderItems,
|
|
624
|
+
keyedFormData,
|
|
625
|
+
onChange: handleChange,
|
|
626
|
+
};
|
|
627
|
+
if (schemaUtils.isMultiSelect(schema)) {
|
|
628
|
+
// If array has enum or uniqueItems set to true, call renderMultiSelect() to render the default multiselect widget or a custom widget, if specified.
|
|
629
|
+
return _jsx(ArrayAsMultiSelect, { ...props, fieldPathId: childFieldPathId, onSelectChange: onSelectChange });
|
|
461
630
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
renderFiles() {
|
|
465
|
-
const { schema, uiSchema, idSchema, name, disabled = false, readonly = false, autofocus = false, required = false, onBlur, onFocus, registry, formData: items = [], rawErrors, } = this.props;
|
|
466
|
-
const { widgets, formContext, globalUiOptions, schemaUtils } = registry;
|
|
467
|
-
const { widget = 'files', title: uiTitle, ...options } = getUiOptions(uiSchema, globalUiOptions);
|
|
468
|
-
const Widget = getWidget(schema, widget, widgets);
|
|
469
|
-
const label = uiTitle ?? schema.title ?? name;
|
|
470
|
-
const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema, globalUiOptions);
|
|
471
|
-
return (_jsx(Widget, { options: options, id: idSchema.$id, name: name, multiple: true, onChange: this.onSelectChange, onBlur: onBlur, onFocus: onFocus, schema: schema, uiSchema: uiSchema, value: items, disabled: disabled, readonly: readonly, required: required, registry: registry, formContext: formContext, autofocus: autofocus, rawErrors: rawErrors, label: label, hideLabel: !displayLabel }));
|
|
631
|
+
if (isCustomWidget(uiSchema)) {
|
|
632
|
+
return _jsx(ArrayAsCustomWidget, { ...props, fieldPathId: childFieldPathId, onSelectChange: onSelectChange });
|
|
472
633
|
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
renderFixedArray() {
|
|
476
|
-
const { schema, uiSchema = {}, formData = [], errorSchema, idPrefix, idSeparator = '_', idSchema, name, title, disabled = false, readonly = false, autofocus = false, required = false, registry, onBlur, onFocus, rawErrors, } = this.props;
|
|
477
|
-
const { keyedFormData } = this.state;
|
|
478
|
-
let { formData: items = [] } = this.props;
|
|
479
|
-
const fieldTitle = schema.title || title || name;
|
|
480
|
-
const uiOptions = getUiOptions(uiSchema);
|
|
481
|
-
const { schemaUtils, formContext } = registry;
|
|
482
|
-
const _schemaItems = isObject(schema.items) ? schema.items : [];
|
|
483
|
-
const itemSchemas = _schemaItems.map((item, index) => schemaUtils.retrieveSchema(item, formData[index]));
|
|
484
|
-
const additionalSchema = isObject(schema.additionalItems)
|
|
485
|
-
? schemaUtils.retrieveSchema(schema.additionalItems, formData)
|
|
486
|
-
: null;
|
|
487
|
-
if (!items || items.length < itemSchemas.length) {
|
|
488
|
-
// to make sure at least all fixed items are generated
|
|
489
|
-
items = items || [];
|
|
490
|
-
items = items.concat(new Array(itemSchemas.length - items.length));
|
|
491
|
-
}
|
|
492
|
-
// These are the props passed into the render function
|
|
493
|
-
const canAdd = this.canAddItem(items) && !!additionalSchema;
|
|
494
|
-
const arrayProps = {
|
|
495
|
-
canAdd,
|
|
496
|
-
className: 'rjsf-field rjsf-field-array rjsf-field-array-fixed-items',
|
|
497
|
-
disabled,
|
|
498
|
-
idSchema,
|
|
499
|
-
formData,
|
|
500
|
-
items: keyedFormData.map((keyedItem, index) => {
|
|
501
|
-
const { key, item } = keyedItem;
|
|
502
|
-
// While we are actually dealing with a single item of type T, the types require a T[], so cast
|
|
503
|
-
const itemCast = item;
|
|
504
|
-
const additional = index >= itemSchemas.length;
|
|
505
|
-
const itemSchema = (additional && isObject(schema.additionalItems)
|
|
506
|
-
? schemaUtils.retrieveSchema(schema.additionalItems, itemCast)
|
|
507
|
-
: itemSchemas[index]) || {};
|
|
508
|
-
const itemIdPrefix = idSchema.$id + idSeparator + index;
|
|
509
|
-
const itemIdSchema = schemaUtils.toIdSchema(itemSchema, itemIdPrefix, itemCast, idPrefix, idSeparator);
|
|
510
|
-
const itemUiSchema = additional
|
|
511
|
-
? uiSchema.additionalItems || {}
|
|
512
|
-
: Array.isArray(uiSchema.items)
|
|
513
|
-
? uiSchema.items[index]
|
|
514
|
-
: uiSchema.items || {};
|
|
515
|
-
const itemErrorSchema = errorSchema ? errorSchema[index] : undefined;
|
|
516
|
-
return this.renderArrayFieldItem({
|
|
517
|
-
key,
|
|
518
|
-
index,
|
|
519
|
-
name: name && `${name}-${index}`,
|
|
520
|
-
title: fieldTitle ? `${fieldTitle}-${index + 1}` : undefined,
|
|
521
|
-
canAdd,
|
|
522
|
-
canRemove: additional,
|
|
523
|
-
canMoveUp: index >= itemSchemas.length + 1,
|
|
524
|
-
canMoveDown: additional && index < items.length - 1,
|
|
525
|
-
itemSchema,
|
|
526
|
-
itemData: itemCast,
|
|
527
|
-
itemUiSchema,
|
|
528
|
-
itemIdSchema,
|
|
529
|
-
itemErrorSchema,
|
|
530
|
-
autofocus: autofocus && index === 0,
|
|
531
|
-
onBlur,
|
|
532
|
-
onFocus,
|
|
533
|
-
rawErrors,
|
|
534
|
-
totalItems: keyedFormData.length,
|
|
535
|
-
});
|
|
536
|
-
}),
|
|
537
|
-
onAddClick: this.onAddClick,
|
|
538
|
-
readonly,
|
|
539
|
-
required,
|
|
540
|
-
registry,
|
|
541
|
-
schema,
|
|
542
|
-
uiSchema,
|
|
543
|
-
title: fieldTitle,
|
|
544
|
-
formContext,
|
|
545
|
-
errorSchema,
|
|
546
|
-
rawErrors,
|
|
547
|
-
};
|
|
548
|
-
const Template = getTemplate('ArrayFieldTemplate', registry, uiOptions);
|
|
549
|
-
return _jsx(Template, { ...arrayProps });
|
|
634
|
+
if (isFixedItems(schema)) {
|
|
635
|
+
return _jsx(FixedArray, { ...props, ...arrayProps });
|
|
550
636
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
*
|
|
554
|
-
* @param props - The props for the individual array item to be rendered
|
|
555
|
-
*/
|
|
556
|
-
renderArrayFieldItem(props) {
|
|
557
|
-
const { key, index, name, canAdd, canRemove = true, canMoveUp, canMoveDown, itemSchema, itemData, itemUiSchema, itemIdSchema, itemErrorSchema, autofocus, onBlur, onFocus, rawErrors, totalItems, title, } = props;
|
|
558
|
-
const { disabled, hideError, idPrefix, idSeparator, readonly, uiSchema, registry, formContext } = this.props;
|
|
559
|
-
const { fields: { ArraySchemaField, SchemaField }, globalUiOptions, } = registry;
|
|
560
|
-
const ItemSchemaField = ArraySchemaField || SchemaField;
|
|
561
|
-
const { orderable = true, removable = true, copyable = false } = getUiOptions(uiSchema, globalUiOptions);
|
|
562
|
-
const has = {
|
|
563
|
-
moveUp: orderable && canMoveUp,
|
|
564
|
-
moveDown: orderable && canMoveDown,
|
|
565
|
-
copy: copyable && canAdd,
|
|
566
|
-
remove: removable && canRemove,
|
|
567
|
-
toolbar: false,
|
|
568
|
-
};
|
|
569
|
-
has.toolbar = Object.keys(has).some((key) => has[key]);
|
|
570
|
-
return {
|
|
571
|
-
children: (_jsx(ItemSchemaField, { name: name, title: title, index: index, schema: itemSchema, uiSchema: itemUiSchema, formData: itemData, formContext: formContext, errorSchema: itemErrorSchema, idPrefix: idPrefix, idSeparator: idSeparator, idSchema: itemIdSchema, required: this.isItemRequired(itemSchema), onChange: this.onChangeForIndex(index), onBlur: onBlur, onFocus: onFocus, registry: registry, disabled: disabled, readonly: readonly, hideError: hideError, autofocus: autofocus, rawErrors: rawErrors })),
|
|
572
|
-
buttonsProps: {
|
|
573
|
-
idSchema: itemIdSchema,
|
|
574
|
-
disabled: disabled,
|
|
575
|
-
readonly: readonly,
|
|
576
|
-
canAdd,
|
|
577
|
-
hasCopy: has.copy,
|
|
578
|
-
hasMoveUp: has.moveUp,
|
|
579
|
-
hasMoveDown: has.moveDown,
|
|
580
|
-
hasRemove: has.remove,
|
|
581
|
-
index: index,
|
|
582
|
-
totalItems: totalItems,
|
|
583
|
-
onAddIndexClick: this.onAddIndexClick,
|
|
584
|
-
onCopyIndexClick: this.onCopyIndexClick,
|
|
585
|
-
onDropIndexClick: this.onDropIndexClick,
|
|
586
|
-
onReorderClick: this.onReorderClick,
|
|
587
|
-
registry: registry,
|
|
588
|
-
schema: itemSchema,
|
|
589
|
-
uiSchema: itemUiSchema,
|
|
590
|
-
},
|
|
591
|
-
className: 'rjsf-array-item',
|
|
592
|
-
disabled,
|
|
593
|
-
hasToolbar: has.toolbar,
|
|
594
|
-
index,
|
|
595
|
-
totalItems,
|
|
596
|
-
key,
|
|
597
|
-
readonly,
|
|
598
|
-
registry,
|
|
599
|
-
schema: itemSchema,
|
|
600
|
-
uiSchema: itemUiSchema,
|
|
601
|
-
};
|
|
637
|
+
if (schemaUtils.isFilesArray(schema, uiSchema)) {
|
|
638
|
+
return _jsx(ArrayAsFiles, { ...props, fieldPathId: childFieldPathId, onSelectChange: onSelectChange });
|
|
602
639
|
}
|
|
640
|
+
return _jsx(NormalArray, { ...props, ...arrayProps });
|
|
603
641
|
}
|
|
604
|
-
/** `ArrayField` is `React.ComponentType<FieldProps<T[], S, F>>` (necessarily) but the `registry` requires things to be a
|
|
605
|
-
* `Field` which is defined as `React.ComponentType<FieldProps<T, S, F>>`, so cast it to make `registry` happy.
|
|
606
|
-
*/
|
|
607
|
-
export default ArrayField;
|