@rjsf/core 6.0.0-beta.9 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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,43 +1,43 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MouseEvent, useCallback, useMemo, useState } from 'react';
|
|
2
2
|
import {
|
|
3
|
+
allowAdditionalItems,
|
|
3
4
|
getTemplate,
|
|
4
|
-
getWidget,
|
|
5
5
|
getUiOptions,
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
getWidget,
|
|
7
|
+
hashObject,
|
|
8
8
|
isCustomWidget,
|
|
9
|
+
isFixedItems,
|
|
10
|
+
isFormDataAvailable,
|
|
9
11
|
optionsList,
|
|
12
|
+
shouldRenderOptionalField,
|
|
13
|
+
toFieldPathId,
|
|
14
|
+
useDeepCompareMemo,
|
|
15
|
+
ITEMS_KEY,
|
|
16
|
+
ID_KEY,
|
|
10
17
|
ArrayFieldTemplateProps,
|
|
11
18
|
ErrorSchema,
|
|
19
|
+
FieldPathId,
|
|
20
|
+
FieldPathList,
|
|
12
21
|
FieldProps,
|
|
13
22
|
FormContextType,
|
|
14
|
-
|
|
23
|
+
Registry,
|
|
15
24
|
RJSFSchema,
|
|
16
25
|
StrictRJSFSchema,
|
|
17
26
|
TranslatableString,
|
|
18
27
|
UiSchema,
|
|
19
|
-
|
|
28
|
+
UIOptionsType,
|
|
20
29
|
} from '@rjsf/utils';
|
|
21
30
|
import cloneDeep from 'lodash/cloneDeep';
|
|
22
|
-
import get from 'lodash/get';
|
|
23
31
|
import isObject from 'lodash/isObject';
|
|
24
32
|
import set from 'lodash/set';
|
|
25
|
-
import
|
|
33
|
+
import uniqueId from 'lodash/uniqueId';
|
|
26
34
|
|
|
27
35
|
/** Type used to represent the keyed form data used in the state */
|
|
28
36
|
type KeyedFormDataType<T> = { key: string; item: T };
|
|
29
37
|
|
|
30
|
-
/** Type used for the state of the `ArrayField` component */
|
|
31
|
-
type ArrayFieldState<T> = {
|
|
32
|
-
/** The keyed form data elements */
|
|
33
|
-
keyedFormData: KeyedFormDataType<T>[];
|
|
34
|
-
/** Flag indicating whether any of the keyed form data has been updated */
|
|
35
|
-
updatedKeyedFormData: boolean;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
38
|
/** Used to generate a unique ID for an element in a row */
|
|
39
39
|
function generateRowId() {
|
|
40
|
-
return
|
|
40
|
+
return uniqueId('rjsf-array-item-');
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
/** Converts the `formData` into `KeyedFormDataType` data, using the `generateRowId()` function to create the key
|
|
@@ -45,7 +45,7 @@ function generateRowId() {
|
|
|
45
45
|
* @param formData - The data for the form
|
|
46
46
|
* @returns - The `formData` converted into a `KeyedFormDataType` element
|
|
47
47
|
*/
|
|
48
|
-
function generateKeyedFormData<T>(formData
|
|
48
|
+
function generateKeyedFormData<T>(formData?: T[]): KeyedFormDataType<T>[] {
|
|
49
49
|
return !Array.isArray(formData)
|
|
50
50
|
? []
|
|
51
51
|
: formData.map((item) => {
|
|
@@ -68,124 +68,783 @@ function keyedToPlainFormData<T>(keyedFormData: KeyedFormDataType<T> | KeyedForm
|
|
|
68
68
|
return [];
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
/**
|
|
72
|
-
*
|
|
71
|
+
/** Determines whether the item described in the schema is always required, which is determined by whether any item
|
|
72
|
+
* may be null.
|
|
73
|
+
*
|
|
74
|
+
* @param itemSchema - The schema for the item
|
|
75
|
+
* @return - True if the item schema type does not contain the "null" type
|
|
73
76
|
*/
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
*
|
|
80
|
-
* @param props - The `FieldProps` for this template
|
|
81
|
-
*/
|
|
82
|
-
constructor(props: FieldProps<T[], S, F>) {
|
|
83
|
-
super(props);
|
|
84
|
-
const { formData = [] } = props;
|
|
85
|
-
const keyedFormData = generateKeyedFormData<T>(formData);
|
|
86
|
-
this.state = {
|
|
87
|
-
keyedFormData,
|
|
88
|
-
updatedKeyedFormData: false,
|
|
89
|
-
};
|
|
77
|
+
function isItemRequired<S extends StrictRJSFSchema = RJSFSchema>(itemSchema: S) {
|
|
78
|
+
if (Array.isArray(itemSchema.type)) {
|
|
79
|
+
// While we don't yet support composite/nullable jsonschema types, it's
|
|
80
|
+
// future-proof to check for requirement against these.
|
|
81
|
+
return !itemSchema.type.includes('null');
|
|
90
82
|
}
|
|
83
|
+
// All non-null array item types are inherently required by design
|
|
84
|
+
return itemSchema.type !== 'null';
|
|
85
|
+
}
|
|
91
86
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
87
|
+
/** Determines whether more items can be added to the array. If the uiSchema indicates the array doesn't allow adding
|
|
88
|
+
* then false is returned. Otherwise, if the schema indicates that there are a maximum number of items and the
|
|
89
|
+
* `formData` matches that value, then false is returned, otherwise true is returned.
|
|
90
|
+
*
|
|
91
|
+
* @param registry - The registry
|
|
92
|
+
* @param schema - The schema for the field
|
|
93
|
+
* @param formItems - The list of items in the form
|
|
94
|
+
* @param [uiSchema] - The UiSchema for the field
|
|
95
|
+
* @returns - True if the item is addable otherwise false
|
|
96
|
+
*/
|
|
97
|
+
function canAddItem<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
|
|
98
|
+
registry: Registry<T[], S, F>,
|
|
99
|
+
schema: S,
|
|
100
|
+
formItems: T[],
|
|
101
|
+
uiSchema?: UiSchema<T[], S, F>,
|
|
102
|
+
) {
|
|
103
|
+
let { addable } = getUiOptions<T[], S, F>(uiSchema, registry.globalUiOptions);
|
|
104
|
+
if (addable !== false) {
|
|
105
|
+
// if ui:options.addable was not explicitly set to false, we can add
|
|
106
|
+
// another item if we have not exceeded maxItems yet
|
|
107
|
+
if (schema.maxItems !== undefined) {
|
|
108
|
+
addable = formItems.length < schema.maxItems;
|
|
109
|
+
} else {
|
|
110
|
+
addable = true;
|
|
107
111
|
}
|
|
108
|
-
const nextFormData = Array.isArray(nextProps.formData) ? nextProps.formData : [];
|
|
109
|
-
const previousKeyedFormData = prevState.keyedFormData || [];
|
|
110
|
-
const newKeyedFormData =
|
|
111
|
-
nextFormData.length === previousKeyedFormData.length
|
|
112
|
-
? previousKeyedFormData.map((previousKeyedFormDatum, index) => {
|
|
113
|
-
return {
|
|
114
|
-
key: previousKeyedFormDatum.key,
|
|
115
|
-
item: nextFormData[index],
|
|
116
|
-
};
|
|
117
|
-
})
|
|
118
|
-
: generateKeyedFormData<T>(nextFormData);
|
|
119
|
-
return {
|
|
120
|
-
keyedFormData: newKeyedFormData,
|
|
121
|
-
};
|
|
122
112
|
}
|
|
113
|
+
return addable;
|
|
114
|
+
}
|
|
123
115
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
116
|
+
/** Helper method to compute item UI schema for both normal and fixed arrays
|
|
117
|
+
* Handles both static object and dynamic function cases
|
|
118
|
+
*
|
|
119
|
+
* @param uiSchema - The parent UI schema containing items definition
|
|
120
|
+
* @param item - The item data
|
|
121
|
+
* @param index - The index of the item
|
|
122
|
+
* @param formContext - The form context
|
|
123
|
+
* @returns The computed UI schema for the item
|
|
124
|
+
*/
|
|
125
|
+
function computeItemUiSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
|
|
126
|
+
uiSchema: UiSchema<T[], S, F>,
|
|
127
|
+
item: T,
|
|
128
|
+
index: number,
|
|
129
|
+
formContext: F,
|
|
130
|
+
): UiSchema<T[], S, F> | undefined {
|
|
131
|
+
if (typeof uiSchema.items === 'function') {
|
|
132
|
+
try {
|
|
133
|
+
// Call the function with item data, index, and form context
|
|
134
|
+
// TypeScript now correctly infers the types thanks to the ArrayElement type in UiSchema
|
|
135
|
+
const result = uiSchema.items(item, index, formContext);
|
|
136
|
+
// Only use the result if it's truthy
|
|
137
|
+
return result as UiSchema<T[], S, F>;
|
|
138
|
+
} catch (e) {
|
|
139
|
+
console.error(`Error executing dynamic uiSchema.items function for item at index ${index}:`, e);
|
|
140
|
+
// Fall back to undefined to allow the field to still render
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
} else {
|
|
144
|
+
// Static object case - preserve undefined to maintain backward compatibility
|
|
145
|
+
return uiSchema.items as UiSchema<T[], S, F> | undefined;
|
|
135
146
|
}
|
|
147
|
+
}
|
|
136
148
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
// All non-null array item types are inherently required by design
|
|
150
|
-
return itemSchema.type !== 'null';
|
|
149
|
+
/** Returns the default form information for an item based on the schema for that item. Deals with the possibility
|
|
150
|
+
* that the schema is fixed and allows additional items.
|
|
151
|
+
*/
|
|
152
|
+
function getNewFormDataRow<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
|
|
153
|
+
registry: Registry<T[], S, F>,
|
|
154
|
+
schema: S,
|
|
155
|
+
): T {
|
|
156
|
+
const { schemaUtils } = registry;
|
|
157
|
+
let itemSchema = schema.items as S;
|
|
158
|
+
if (isFixedItems(schema) && allowAdditionalItems(schema)) {
|
|
159
|
+
itemSchema = schema.additionalItems as S;
|
|
151
160
|
}
|
|
161
|
+
// Cast this as a T to work around schema utils being for T[] caused by the FieldProps<T[], S, F> call on the class
|
|
162
|
+
return schemaUtils.getDefaultFormState(itemSchema) as unknown as T;
|
|
163
|
+
}
|
|
152
164
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
165
|
+
/** Props used for ArrayAsXxxx type components*/
|
|
166
|
+
interface ArrayAsFieldProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>
|
|
167
|
+
extends FieldProps<T, S, F> {
|
|
168
|
+
/** The callback used to update the array when the selector changes */
|
|
169
|
+
onSelectChange: (value: T) => void;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Renders an array as a set of checkboxes using the 'select' widget
|
|
173
|
+
*/
|
|
174
|
+
function ArrayAsMultiSelect<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
|
|
175
|
+
props: ArrayAsFieldProps<T[], S, F>,
|
|
176
|
+
) {
|
|
177
|
+
const {
|
|
178
|
+
schema,
|
|
179
|
+
fieldPathId,
|
|
180
|
+
uiSchema,
|
|
181
|
+
formData: items = [],
|
|
182
|
+
disabled = false,
|
|
183
|
+
readonly = false,
|
|
184
|
+
autofocus = false,
|
|
185
|
+
required = false,
|
|
186
|
+
placeholder,
|
|
187
|
+
onBlur,
|
|
188
|
+
onFocus,
|
|
189
|
+
registry,
|
|
190
|
+
rawErrors,
|
|
191
|
+
name,
|
|
192
|
+
onSelectChange,
|
|
193
|
+
} = props;
|
|
194
|
+
const { widgets, schemaUtils, globalFormOptions, globalUiOptions } = registry;
|
|
195
|
+
const itemsSchema = schemaUtils.retrieveSchema(schema.items as S, items);
|
|
196
|
+
const enumOptions = optionsList<T[], S, F>(itemsSchema, uiSchema);
|
|
197
|
+
const { widget = 'select', title: uiTitle, ...options } = getUiOptions<T[], S, F>(uiSchema, globalUiOptions);
|
|
198
|
+
const Widget = getWidget<T[], S, F>(schema, widget, widgets);
|
|
199
|
+
const label = uiTitle ?? schema.title ?? name;
|
|
200
|
+
const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema, globalUiOptions);
|
|
201
|
+
// For custom widgets with multiple=true, generate a fieldPathId with isMultiValue flag
|
|
202
|
+
const multiValueFieldPathId = useDeepCompareMemo(toFieldPathId('', globalFormOptions, fieldPathId, true));
|
|
203
|
+
return (
|
|
204
|
+
<Widget
|
|
205
|
+
id={multiValueFieldPathId[ID_KEY]}
|
|
206
|
+
name={name}
|
|
207
|
+
multiple
|
|
208
|
+
onChange={onSelectChange}
|
|
209
|
+
onBlur={onBlur}
|
|
210
|
+
onFocus={onFocus}
|
|
211
|
+
options={{ ...options, enumOptions }}
|
|
212
|
+
schema={schema}
|
|
213
|
+
uiSchema={uiSchema}
|
|
214
|
+
registry={registry}
|
|
215
|
+
value={items}
|
|
216
|
+
disabled={disabled}
|
|
217
|
+
readonly={readonly}
|
|
218
|
+
required={required}
|
|
219
|
+
label={label}
|
|
220
|
+
hideLabel={!displayLabel}
|
|
221
|
+
placeholder={placeholder}
|
|
222
|
+
autofocus={autofocus}
|
|
223
|
+
rawErrors={rawErrors}
|
|
224
|
+
htmlName={multiValueFieldPathId.name}
|
|
225
|
+
/>
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Renders an array using the custom widget provided by the user in the `uiSchema`
|
|
230
|
+
*/
|
|
231
|
+
function ArrayAsCustomWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
|
|
232
|
+
props: ArrayAsFieldProps<T[], S, F>,
|
|
233
|
+
) {
|
|
234
|
+
const {
|
|
235
|
+
schema,
|
|
236
|
+
fieldPathId,
|
|
237
|
+
uiSchema,
|
|
238
|
+
disabled = false,
|
|
239
|
+
readonly = false,
|
|
240
|
+
autofocus = false,
|
|
241
|
+
required = false,
|
|
242
|
+
hideError,
|
|
243
|
+
placeholder,
|
|
244
|
+
onBlur,
|
|
245
|
+
onFocus,
|
|
246
|
+
formData: items = [],
|
|
247
|
+
registry,
|
|
248
|
+
rawErrors,
|
|
249
|
+
name,
|
|
250
|
+
onSelectChange,
|
|
251
|
+
} = props;
|
|
252
|
+
const { widgets, schemaUtils, globalFormOptions, globalUiOptions } = registry;
|
|
253
|
+
const { widget, title: uiTitle, ...options } = getUiOptions<T[], S, F>(uiSchema, globalUiOptions);
|
|
254
|
+
const Widget = getWidget<T[], S, F>(schema, widget, widgets);
|
|
255
|
+
const label = uiTitle ?? schema.title ?? name;
|
|
256
|
+
const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema, globalUiOptions);
|
|
257
|
+
// For custom widgets with multiple=true, generate a fieldPathId with isMultiValue flag
|
|
258
|
+
const multiValueFieldPathId = useDeepCompareMemo(toFieldPathId('', globalFormOptions, fieldPathId, true));
|
|
259
|
+
return (
|
|
260
|
+
<Widget
|
|
261
|
+
id={multiValueFieldPathId[ID_KEY]}
|
|
262
|
+
name={name}
|
|
263
|
+
multiple
|
|
264
|
+
onChange={onSelectChange}
|
|
265
|
+
onBlur={onBlur}
|
|
266
|
+
onFocus={onFocus}
|
|
267
|
+
options={options}
|
|
268
|
+
schema={schema}
|
|
269
|
+
uiSchema={uiSchema}
|
|
270
|
+
registry={registry}
|
|
271
|
+
value={items}
|
|
272
|
+
disabled={disabled}
|
|
273
|
+
readonly={readonly}
|
|
274
|
+
hideError={hideError}
|
|
275
|
+
required={required}
|
|
276
|
+
label={label}
|
|
277
|
+
hideLabel={!displayLabel}
|
|
278
|
+
placeholder={placeholder}
|
|
279
|
+
autofocus={autofocus}
|
|
280
|
+
rawErrors={rawErrors}
|
|
281
|
+
htmlName={multiValueFieldPathId.name}
|
|
282
|
+
/>
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** Renders an array of files using the `FileWidget`
|
|
287
|
+
*/
|
|
288
|
+
function ArrayAsFiles<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
|
|
289
|
+
props: ArrayAsFieldProps<T[], S, F>,
|
|
290
|
+
) {
|
|
291
|
+
const {
|
|
292
|
+
schema,
|
|
293
|
+
uiSchema,
|
|
294
|
+
fieldPathId,
|
|
295
|
+
name,
|
|
296
|
+
disabled = false,
|
|
297
|
+
readonly = false,
|
|
298
|
+
autofocus = false,
|
|
299
|
+
required = false,
|
|
300
|
+
onBlur,
|
|
301
|
+
onFocus,
|
|
302
|
+
registry,
|
|
303
|
+
formData: items = [],
|
|
304
|
+
rawErrors,
|
|
305
|
+
onSelectChange,
|
|
306
|
+
} = props;
|
|
307
|
+
const { widgets, schemaUtils, globalFormOptions, globalUiOptions } = registry;
|
|
308
|
+
const { widget = 'files', title: uiTitle, ...options } = getUiOptions<T[], S, F>(uiSchema, globalUiOptions);
|
|
309
|
+
const Widget = getWidget<T[], S, F>(schema, widget, widgets);
|
|
310
|
+
const label = uiTitle ?? schema.title ?? name;
|
|
311
|
+
const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema, globalUiOptions);
|
|
312
|
+
// For custom widgets with multiple=true, generate a fieldPathId with isMultiValue flag
|
|
313
|
+
const multiValueFieldPathId = useDeepCompareMemo(toFieldPathId('', globalFormOptions, fieldPathId, true));
|
|
314
|
+
return (
|
|
315
|
+
<Widget
|
|
316
|
+
options={options}
|
|
317
|
+
id={multiValueFieldPathId[ID_KEY]}
|
|
318
|
+
name={name}
|
|
319
|
+
multiple
|
|
320
|
+
onChange={onSelectChange}
|
|
321
|
+
onBlur={onBlur}
|
|
322
|
+
onFocus={onFocus}
|
|
323
|
+
schema={schema}
|
|
324
|
+
uiSchema={uiSchema}
|
|
325
|
+
value={items}
|
|
326
|
+
disabled={disabled}
|
|
327
|
+
readonly={readonly}
|
|
328
|
+
required={required}
|
|
329
|
+
registry={registry}
|
|
330
|
+
autofocus={autofocus}
|
|
331
|
+
rawErrors={rawErrors}
|
|
332
|
+
label={label}
|
|
333
|
+
hideLabel={!displayLabel}
|
|
334
|
+
htmlName={multiValueFieldPathId.name}
|
|
335
|
+
/>
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** Renders the individual array item using a `SchemaField` along with the additional properties that are needed to
|
|
340
|
+
* render the whole of the `ArrayFieldItemTemplate`.
|
|
341
|
+
*/
|
|
342
|
+
function ArrayFieldItem<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(props: {
|
|
343
|
+
itemKey: string;
|
|
344
|
+
index: number;
|
|
345
|
+
name: string;
|
|
346
|
+
disabled: boolean;
|
|
347
|
+
readonly: boolean;
|
|
348
|
+
required: boolean;
|
|
349
|
+
hideError: boolean;
|
|
350
|
+
registry: Registry<T[], S, F>;
|
|
351
|
+
uiOptions: UIOptionsType<T[], S, F>;
|
|
352
|
+
parentUiSchema?: UiSchema<T[], S, F>;
|
|
353
|
+
title: string | undefined;
|
|
354
|
+
canAdd: boolean;
|
|
355
|
+
canRemove?: boolean;
|
|
356
|
+
canMoveUp: boolean;
|
|
357
|
+
canMoveDown: boolean;
|
|
358
|
+
itemSchema: S;
|
|
359
|
+
itemData: T[];
|
|
360
|
+
itemUiSchema: UiSchema<T[], S, F> | undefined;
|
|
361
|
+
itemFieldPathId: FieldPathId;
|
|
362
|
+
itemErrorSchema?: ErrorSchema<T[]>;
|
|
363
|
+
autofocus?: boolean;
|
|
364
|
+
onBlur: FieldProps<T[], S, F>['onBlur'];
|
|
365
|
+
onFocus: FieldProps<T[], S, F>['onFocus'];
|
|
366
|
+
onChange: FieldProps<T[], S, F>['onChange'];
|
|
367
|
+
rawErrors?: string[];
|
|
368
|
+
totalItems: number;
|
|
369
|
+
handleAddItem: (event: MouseEvent, index?: number) => void;
|
|
370
|
+
handleCopyItem: (event: MouseEvent, index: number) => void;
|
|
371
|
+
handleRemoveItem: (event: MouseEvent, index: number) => void;
|
|
372
|
+
handleReorderItems: (event: MouseEvent<HTMLButtonElement>, index: number, newIndex: number) => void;
|
|
373
|
+
}) {
|
|
374
|
+
const {
|
|
375
|
+
itemKey,
|
|
376
|
+
index,
|
|
377
|
+
name,
|
|
378
|
+
disabled,
|
|
379
|
+
hideError,
|
|
380
|
+
readonly,
|
|
381
|
+
registry,
|
|
382
|
+
uiOptions,
|
|
383
|
+
parentUiSchema,
|
|
384
|
+
canAdd,
|
|
385
|
+
canRemove = true,
|
|
386
|
+
canMoveUp,
|
|
387
|
+
canMoveDown,
|
|
388
|
+
itemSchema,
|
|
389
|
+
itemData,
|
|
390
|
+
itemUiSchema,
|
|
391
|
+
itemFieldPathId,
|
|
392
|
+
itemErrorSchema,
|
|
393
|
+
autofocus,
|
|
394
|
+
onBlur,
|
|
395
|
+
onFocus,
|
|
396
|
+
onChange,
|
|
397
|
+
rawErrors,
|
|
398
|
+
totalItems,
|
|
399
|
+
title,
|
|
400
|
+
handleAddItem,
|
|
401
|
+
handleCopyItem,
|
|
402
|
+
handleRemoveItem,
|
|
403
|
+
handleReorderItems,
|
|
404
|
+
} = props;
|
|
405
|
+
const {
|
|
406
|
+
fields: { ArraySchemaField, SchemaField },
|
|
407
|
+
} = registry;
|
|
408
|
+
const fieldPathId = useDeepCompareMemo<FieldPathId>(itemFieldPathId);
|
|
409
|
+
const ItemSchemaField = ArraySchemaField || SchemaField;
|
|
410
|
+
const ArrayFieldItemTemplate = getTemplate<'ArrayFieldItemTemplate', T[], S, F>(
|
|
411
|
+
'ArrayFieldItemTemplate',
|
|
412
|
+
registry,
|
|
413
|
+
uiOptions,
|
|
414
|
+
);
|
|
415
|
+
const { orderable = true, removable = true, copyable = false } = uiOptions;
|
|
416
|
+
const has: { [key: string]: boolean } = {
|
|
417
|
+
moveUp: orderable && canMoveUp,
|
|
418
|
+
moveDown: orderable && canMoveDown,
|
|
419
|
+
copy: copyable && canAdd,
|
|
420
|
+
remove: removable && canRemove,
|
|
421
|
+
toolbar: false,
|
|
422
|
+
};
|
|
423
|
+
has.toolbar = Object.keys(has).some((key: keyof typeof has) => has[key]);
|
|
424
|
+
|
|
425
|
+
const onAddItem = useCallback(
|
|
426
|
+
(event: MouseEvent) => {
|
|
427
|
+
handleAddItem(event, index + 1);
|
|
428
|
+
},
|
|
429
|
+
[handleAddItem, index],
|
|
430
|
+
);
|
|
431
|
+
const onCopyItem = useCallback(
|
|
432
|
+
(event: MouseEvent) => {
|
|
433
|
+
handleCopyItem(event, index);
|
|
434
|
+
},
|
|
435
|
+
[handleCopyItem, index],
|
|
436
|
+
);
|
|
437
|
+
const onRemoveItem = useCallback(
|
|
438
|
+
(event: MouseEvent) => {
|
|
439
|
+
handleRemoveItem(event, index);
|
|
440
|
+
},
|
|
441
|
+
[handleRemoveItem, index],
|
|
442
|
+
);
|
|
443
|
+
const onMoveUpItem = useCallback(
|
|
444
|
+
(event: MouseEvent<HTMLButtonElement>) => {
|
|
445
|
+
handleReorderItems(event, index, index - 1);
|
|
446
|
+
},
|
|
447
|
+
[handleReorderItems, index],
|
|
448
|
+
);
|
|
449
|
+
const onMoveDownItem = useCallback(
|
|
450
|
+
(event: MouseEvent<HTMLButtonElement>) => {
|
|
451
|
+
handleReorderItems(event, index, index + 1);
|
|
452
|
+
},
|
|
453
|
+
[handleReorderItems, index],
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
const templateProps = {
|
|
457
|
+
children: (
|
|
458
|
+
<ItemSchemaField
|
|
459
|
+
name={name}
|
|
460
|
+
title={title}
|
|
461
|
+
index={index}
|
|
462
|
+
schema={itemSchema}
|
|
463
|
+
uiSchema={itemUiSchema}
|
|
464
|
+
formData={itemData}
|
|
465
|
+
errorSchema={itemErrorSchema}
|
|
466
|
+
fieldPathId={fieldPathId}
|
|
467
|
+
required={isItemRequired<S>(itemSchema)}
|
|
468
|
+
onChange={onChange}
|
|
469
|
+
onBlur={onBlur}
|
|
470
|
+
onFocus={onFocus}
|
|
471
|
+
registry={registry}
|
|
472
|
+
disabled={disabled}
|
|
473
|
+
readonly={readonly}
|
|
474
|
+
hideError={hideError}
|
|
475
|
+
autofocus={autofocus}
|
|
476
|
+
rawErrors={rawErrors}
|
|
477
|
+
/>
|
|
478
|
+
),
|
|
479
|
+
buttonsProps: {
|
|
480
|
+
fieldPathId,
|
|
481
|
+
disabled,
|
|
482
|
+
readonly,
|
|
483
|
+
canAdd,
|
|
484
|
+
hasCopy: has.copy,
|
|
485
|
+
hasMoveUp: has.moveUp,
|
|
486
|
+
hasMoveDown: has.moveDown,
|
|
487
|
+
hasRemove: has.remove,
|
|
488
|
+
index: index,
|
|
489
|
+
totalItems,
|
|
490
|
+
onAddItem,
|
|
491
|
+
onCopyItem,
|
|
492
|
+
onRemoveItem,
|
|
493
|
+
onMoveUpItem,
|
|
494
|
+
onMoveDownItem,
|
|
495
|
+
registry,
|
|
496
|
+
schema: itemSchema,
|
|
497
|
+
uiSchema: itemUiSchema,
|
|
498
|
+
},
|
|
499
|
+
itemKey,
|
|
500
|
+
className: 'rjsf-array-item',
|
|
501
|
+
disabled,
|
|
502
|
+
hasToolbar: has.toolbar,
|
|
503
|
+
index,
|
|
504
|
+
totalItems,
|
|
505
|
+
readonly,
|
|
506
|
+
registry,
|
|
507
|
+
schema: itemSchema,
|
|
508
|
+
uiSchema: itemUiSchema,
|
|
509
|
+
parentUiSchema,
|
|
510
|
+
};
|
|
511
|
+
return <ArrayFieldItemTemplate {...templateProps} />;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/** The properties required by the stateless components that render the items using the `ArrayFieldItem` */
|
|
515
|
+
interface InternalArrayFieldProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>
|
|
516
|
+
extends FieldProps<T[], S, F> {
|
|
517
|
+
/** The keyedFormData from the `ArrayField` state */
|
|
518
|
+
keyedFormData: KeyedFormDataType<T>[];
|
|
519
|
+
/** The callback used to handle the adding of an item at the given index (or the end, if missing) */
|
|
520
|
+
handleAddItem: (event: MouseEvent, index?: number) => void;
|
|
521
|
+
/** The callback used to handle the copying of the item at the given index, below itself */
|
|
522
|
+
handleCopyItem: (event: MouseEvent, index: number) => void;
|
|
523
|
+
/** The callback used to handle removing an item at the given index */
|
|
524
|
+
handleRemoveItem: (event: MouseEvent, index: number) => void;
|
|
525
|
+
/** The callback used to handle reordering an item at the given index to its newIndex */
|
|
526
|
+
handleReorderItems: (event: MouseEvent<HTMLButtonElement>, index: number, newIndex: number) => void;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/** Renders a normal array without any limitations of length
|
|
530
|
+
*/
|
|
531
|
+
function NormalArray<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
|
|
532
|
+
props: InternalArrayFieldProps<T, S, F>,
|
|
533
|
+
) {
|
|
534
|
+
const {
|
|
535
|
+
schema,
|
|
536
|
+
uiSchema = {},
|
|
537
|
+
errorSchema,
|
|
538
|
+
fieldPathId,
|
|
539
|
+
formData: formDataFromProps,
|
|
540
|
+
name,
|
|
541
|
+
title,
|
|
542
|
+
disabled = false,
|
|
543
|
+
readonly = false,
|
|
544
|
+
autofocus = false,
|
|
545
|
+
required = false,
|
|
546
|
+
hideError = false,
|
|
547
|
+
registry,
|
|
548
|
+
onBlur,
|
|
549
|
+
onFocus,
|
|
550
|
+
rawErrors,
|
|
551
|
+
onChange,
|
|
552
|
+
keyedFormData,
|
|
553
|
+
handleAddItem,
|
|
554
|
+
handleCopyItem,
|
|
555
|
+
handleRemoveItem,
|
|
556
|
+
handleReorderItems,
|
|
557
|
+
} = props;
|
|
558
|
+
const fieldTitle = schema.title || title || name;
|
|
559
|
+
const { schemaUtils, fields, formContext, globalFormOptions, globalUiOptions } = registry;
|
|
560
|
+
const { OptionalDataControlsField } = fields;
|
|
561
|
+
const uiOptions = getUiOptions<T[], S, F>(uiSchema, globalUiOptions);
|
|
562
|
+
const _schemaItems: S = isObject(schema.items) ? (schema.items as S) : ({} as S);
|
|
563
|
+
const itemsSchema: S = schemaUtils.retrieveSchema(_schemaItems);
|
|
564
|
+
const formData = keyedToPlainFormData<T>(keyedFormData);
|
|
565
|
+
const renderOptionalField = shouldRenderOptionalField<T[], S, F>(registry, schema, required, uiSchema);
|
|
566
|
+
const hasFormData = isFormDataAvailable<T[]>(formDataFromProps);
|
|
567
|
+
const canAdd = canAddItem<T, S, F>(registry, schema, formData, uiSchema) && (!renderOptionalField || hasFormData);
|
|
568
|
+
const actualFormData = hasFormData ? keyedFormData : [];
|
|
569
|
+
const extraClass = renderOptionalField ? ' rjsf-optional-array-field' : '';
|
|
570
|
+
// All the children will use childFieldPathId if present in the props, falling back to the fieldPathId
|
|
571
|
+
const childFieldPathId = props.childFieldPathId ?? fieldPathId;
|
|
572
|
+
const optionalDataControl = renderOptionalField ? (
|
|
573
|
+
<OptionalDataControlsField {...props} fieldPathId={childFieldPathId} />
|
|
574
|
+
) : undefined;
|
|
575
|
+
const arrayProps: ArrayFieldTemplateProps<T[], S, F> = {
|
|
576
|
+
canAdd,
|
|
577
|
+
items: actualFormData.map((keyedItem, index: number) => {
|
|
578
|
+
const { key, item } = keyedItem;
|
|
579
|
+
// While we are actually dealing with a single item of type T, the types require a T[], so cast
|
|
580
|
+
const itemCast = item as unknown as T[];
|
|
581
|
+
const itemSchema = schemaUtils.retrieveSchema(_schemaItems, itemCast);
|
|
582
|
+
const itemErrorSchema = errorSchema ? (errorSchema[index] as ErrorSchema<T[]>) : undefined;
|
|
583
|
+
const itemFieldPathId = toFieldPathId(index, globalFormOptions, childFieldPathId);
|
|
584
|
+
|
|
585
|
+
// Compute the item UI schema using the helper method
|
|
586
|
+
const itemUiSchema = computeItemUiSchema<T, S, F>(uiSchema, item, index, formContext);
|
|
587
|
+
|
|
588
|
+
const itemProps = {
|
|
589
|
+
itemKey: key,
|
|
590
|
+
index,
|
|
591
|
+
name: name && `${name}-${index}`,
|
|
592
|
+
registry,
|
|
593
|
+
uiOptions,
|
|
594
|
+
hideError,
|
|
595
|
+
readonly,
|
|
596
|
+
disabled,
|
|
597
|
+
required,
|
|
598
|
+
title: fieldTitle ? `${fieldTitle}-${index + 1}` : undefined,
|
|
599
|
+
canAdd,
|
|
600
|
+
canMoveUp: index > 0,
|
|
601
|
+
canMoveDown: index < formData.length - 1,
|
|
602
|
+
itemSchema,
|
|
603
|
+
itemFieldPathId,
|
|
604
|
+
itemErrorSchema,
|
|
605
|
+
itemData: itemCast,
|
|
606
|
+
itemUiSchema,
|
|
607
|
+
autofocus: autofocus && index === 0,
|
|
608
|
+
onBlur,
|
|
609
|
+
onFocus,
|
|
610
|
+
rawErrors,
|
|
611
|
+
totalItems: keyedFormData.length,
|
|
612
|
+
handleAddItem,
|
|
613
|
+
handleCopyItem,
|
|
614
|
+
handleRemoveItem,
|
|
615
|
+
handleReorderItems,
|
|
616
|
+
onChange,
|
|
617
|
+
};
|
|
618
|
+
return <ArrayFieldItem key={key} {...itemProps} />;
|
|
619
|
+
}),
|
|
620
|
+
className: `rjsf-field rjsf-field-array rjsf-field-array-of-${itemsSchema.type}${extraClass}`,
|
|
621
|
+
disabled,
|
|
622
|
+
fieldPathId,
|
|
623
|
+
uiSchema,
|
|
624
|
+
onAddClick: handleAddItem,
|
|
625
|
+
readonly,
|
|
626
|
+
required,
|
|
627
|
+
schema,
|
|
628
|
+
title: fieldTitle,
|
|
629
|
+
formData,
|
|
630
|
+
rawErrors,
|
|
631
|
+
registry,
|
|
632
|
+
optionalDataControl,
|
|
633
|
+
};
|
|
634
|
+
|
|
635
|
+
const Template = getTemplate<'ArrayFieldTemplate', T[], S, F>('ArrayFieldTemplate', registry, uiOptions);
|
|
636
|
+
return <Template {...arrayProps} />;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/** Renders an array that has a maximum limit of items
|
|
640
|
+
*/
|
|
641
|
+
function FixedArray<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
|
|
642
|
+
props: InternalArrayFieldProps<T, S, F>,
|
|
643
|
+
) {
|
|
644
|
+
const {
|
|
645
|
+
schema,
|
|
646
|
+
uiSchema = {},
|
|
647
|
+
formData,
|
|
648
|
+
errorSchema,
|
|
649
|
+
fieldPathId,
|
|
650
|
+
name,
|
|
651
|
+
title,
|
|
652
|
+
disabled = false,
|
|
653
|
+
readonly = false,
|
|
654
|
+
autofocus = false,
|
|
655
|
+
required = false,
|
|
656
|
+
hideError = false,
|
|
657
|
+
registry,
|
|
658
|
+
onBlur,
|
|
659
|
+
onFocus,
|
|
660
|
+
rawErrors,
|
|
661
|
+
keyedFormData,
|
|
662
|
+
onChange,
|
|
663
|
+
handleAddItem,
|
|
664
|
+
handleCopyItem,
|
|
665
|
+
handleRemoveItem,
|
|
666
|
+
handleReorderItems,
|
|
667
|
+
} = props;
|
|
668
|
+
let { formData: items = [] } = props;
|
|
669
|
+
const fieldTitle = schema.title || title || name;
|
|
670
|
+
const { schemaUtils, fields, formContext, globalFormOptions, globalUiOptions } = registry;
|
|
671
|
+
const uiOptions = getUiOptions<T[], S, F>(uiSchema, globalUiOptions);
|
|
672
|
+
const { OptionalDataControlsField } = fields;
|
|
673
|
+
const renderOptionalField = shouldRenderOptionalField<T[], S, F>(registry, schema, required, uiSchema);
|
|
674
|
+
const hasFormData = isFormDataAvailable<T[]>(formData);
|
|
675
|
+
const _schemaItems: S[] = isObject(schema.items) ? (schema.items as S[]) : ([] as S[]);
|
|
676
|
+
const itemSchemas = _schemaItems.map((item: S, index: number) =>
|
|
677
|
+
schemaUtils.retrieveSchema(item, items[index] as unknown as T[]),
|
|
678
|
+
);
|
|
679
|
+
const additionalSchema = isObject(schema.additionalItems)
|
|
680
|
+
? schemaUtils.retrieveSchema(schema.additionalItems as S, formData)
|
|
681
|
+
: null;
|
|
682
|
+
// All the children will use childFieldPathId if present in the props, falling back to the fieldPathId
|
|
683
|
+
const childFieldPathId = props.childFieldPathId ?? fieldPathId;
|
|
684
|
+
|
|
685
|
+
if (items.length < itemSchemas.length) {
|
|
686
|
+
// to make sure at least all fixed items are generated
|
|
687
|
+
items = items.concat(new Array(itemSchemas.length - items.length));
|
|
688
|
+
}
|
|
689
|
+
const actualFormData = hasFormData ? keyedFormData : [];
|
|
690
|
+
const extraClass = renderOptionalField ? ' rjsf-optional-array-field' : '';
|
|
691
|
+
const optionalDataControl = renderOptionalField ? (
|
|
692
|
+
<OptionalDataControlsField {...props} fieldPathId={childFieldPathId} />
|
|
693
|
+
) : undefined;
|
|
694
|
+
|
|
695
|
+
// These are the props passed into the render function
|
|
696
|
+
const canAdd =
|
|
697
|
+
canAddItem<T, S, F>(registry, schema, items, uiSchema) &&
|
|
698
|
+
!!additionalSchema &&
|
|
699
|
+
(!renderOptionalField || hasFormData);
|
|
700
|
+
const arrayProps: ArrayFieldTemplateProps<T[], S, F> = {
|
|
701
|
+
canAdd,
|
|
702
|
+
className: `rjsf-field rjsf-field-array rjsf-field-array-fixed-items${extraClass}`,
|
|
703
|
+
disabled,
|
|
704
|
+
fieldPathId,
|
|
705
|
+
formData,
|
|
706
|
+
items: actualFormData.map((keyedItem, index) => {
|
|
707
|
+
const { key, item } = keyedItem;
|
|
708
|
+
// While we are actually dealing with a single item of type T, the types require a T[], so cast
|
|
709
|
+
const itemCast = item as unknown as T[];
|
|
710
|
+
const additional = index >= itemSchemas.length;
|
|
711
|
+
const itemSchema =
|
|
712
|
+
(additional && isObject(schema.additionalItems)
|
|
713
|
+
? schemaUtils.retrieveSchema(schema.additionalItems as S, itemCast)
|
|
714
|
+
: itemSchemas[index]) || {};
|
|
715
|
+
const itemFieldPathId = toFieldPathId(index, globalFormOptions, childFieldPathId);
|
|
716
|
+
// Compute the item UI schema - handle both static and dynamic cases
|
|
717
|
+
let itemUiSchema: UiSchema<T[], S, F> | undefined;
|
|
718
|
+
if (additional) {
|
|
719
|
+
// For additional items, use additionalItems uiSchema
|
|
720
|
+
itemUiSchema = uiSchema.additionalItems as UiSchema<T[], S, F>;
|
|
168
721
|
} else {
|
|
169
|
-
|
|
722
|
+
// For fixed items, uiSchema.items can be an array, a function, or a single object
|
|
723
|
+
if (Array.isArray(uiSchema.items)) {
|
|
724
|
+
itemUiSchema = uiSchema.items[index] as UiSchema<T[], S, F>;
|
|
725
|
+
} else {
|
|
726
|
+
// Use the helper method for function or static object cases
|
|
727
|
+
itemUiSchema = computeItemUiSchema<T, S, F>(uiSchema, item, index, formContext);
|
|
728
|
+
}
|
|
170
729
|
}
|
|
171
|
-
|
|
172
|
-
return addable;
|
|
173
|
-
}
|
|
730
|
+
const itemErrorSchema = errorSchema ? (errorSchema[index] as ErrorSchema<T[]>) : undefined;
|
|
174
731
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
732
|
+
const itemProps = {
|
|
733
|
+
index,
|
|
734
|
+
itemKey: key,
|
|
735
|
+
name: name && `${name}-${index}`,
|
|
736
|
+
registry,
|
|
737
|
+
uiOptions,
|
|
738
|
+
hideError,
|
|
739
|
+
readonly,
|
|
740
|
+
disabled,
|
|
741
|
+
required,
|
|
742
|
+
title: fieldTitle ? `${fieldTitle}-${index + 1}` : undefined,
|
|
743
|
+
canAdd,
|
|
744
|
+
canRemove: additional,
|
|
745
|
+
canMoveUp: index >= itemSchemas.length + 1,
|
|
746
|
+
canMoveDown: additional && index < items.length - 1,
|
|
747
|
+
itemSchema,
|
|
748
|
+
itemData: itemCast,
|
|
749
|
+
itemUiSchema,
|
|
750
|
+
itemFieldPathId,
|
|
751
|
+
itemErrorSchema,
|
|
752
|
+
autofocus: autofocus && index === 0,
|
|
753
|
+
onBlur,
|
|
754
|
+
onFocus,
|
|
755
|
+
rawErrors,
|
|
756
|
+
totalItems: keyedFormData.length,
|
|
757
|
+
onChange,
|
|
758
|
+
handleAddItem,
|
|
759
|
+
handleCopyItem,
|
|
760
|
+
handleRemoveItem,
|
|
761
|
+
handleReorderItems,
|
|
762
|
+
};
|
|
763
|
+
return <ArrayFieldItem key={key} {...itemProps} />;
|
|
764
|
+
}),
|
|
765
|
+
onAddClick: handleAddItem,
|
|
766
|
+
readonly,
|
|
767
|
+
required,
|
|
768
|
+
registry,
|
|
769
|
+
schema,
|
|
770
|
+
uiSchema,
|
|
771
|
+
title: fieldTitle,
|
|
772
|
+
errorSchema,
|
|
773
|
+
rawErrors,
|
|
774
|
+
optionalDataControl,
|
|
187
775
|
};
|
|
188
776
|
|
|
777
|
+
const Template = getTemplate<'ArrayFieldTemplate', T[], S, F>('ArrayFieldTemplate', registry, uiOptions);
|
|
778
|
+
return <Template {...arrayProps} />;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
interface KeyedFormDataState<T = any> {
|
|
782
|
+
/** The keyed form data elements */
|
|
783
|
+
keyedFormData: KeyedFormDataType<T>[];
|
|
784
|
+
/** Updates the keyed form data elements to the given value */
|
|
785
|
+
updateKeyedFormData: (newData: KeyedFormDataType<T>[]) => T[];
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/** Type used for the state of the `ArrayField` component */
|
|
789
|
+
type ArrayFieldState<T> = {
|
|
790
|
+
/** The hash of the last formData passed in */
|
|
791
|
+
formDataHash: string;
|
|
792
|
+
/** The keyed form data elements */
|
|
793
|
+
keyedFormData: KeyedFormDataType<T>[];
|
|
794
|
+
};
|
|
795
|
+
|
|
796
|
+
/** A custom hook that handles the updating of the keyedFormData from an external `formData` change as well as
|
|
797
|
+
* internally by the `ArrayField`. If there was an external `formData` change, then the `keyedFormData` is recomputed
|
|
798
|
+
* in order to preserve the unique keys from the old `keyedFormData` to the new `formData`. Along with the
|
|
799
|
+
* `keyedFormData` this hook also returns an `updateKeyedFormData()` function for use by the `ArrayField`. The detection
|
|
800
|
+
* of external `formData` are handled by storing the hash of that `formData` along with the `keyedFormData` associated
|
|
801
|
+
* with it. The `updateKeyedFormData()` will update that hash whenever the `keyedFormData` is modified and as well as
|
|
802
|
+
* returning the plain `formData` from the `keyedFormData`.
|
|
803
|
+
*/
|
|
804
|
+
function useKeyedFormData<T = any>(formData: T[] = []): KeyedFormDataState<T> {
|
|
805
|
+
const newHash = useMemo(() => hashObject(formData), [formData]);
|
|
806
|
+
const [state, setState] = useState<ArrayFieldState<T>>(() => ({
|
|
807
|
+
formDataHash: newHash,
|
|
808
|
+
keyedFormData: generateKeyedFormData<T>(formData),
|
|
809
|
+
}));
|
|
810
|
+
|
|
811
|
+
let { keyedFormData, formDataHash } = state;
|
|
812
|
+
if (newHash !== formDataHash) {
|
|
813
|
+
const nextFormData = Array.isArray(formData) ? formData : [];
|
|
814
|
+
const previousKeyedFormData = keyedFormData || [];
|
|
815
|
+
keyedFormData =
|
|
816
|
+
nextFormData.length === previousKeyedFormData.length
|
|
817
|
+
? previousKeyedFormData.map((previousKeyedFormDatum, index) => ({
|
|
818
|
+
key: previousKeyedFormDatum.key,
|
|
819
|
+
item: nextFormData[index],
|
|
820
|
+
}))
|
|
821
|
+
: generateKeyedFormData<T>(nextFormData);
|
|
822
|
+
formDataHash = newHash;
|
|
823
|
+
setState({ formDataHash, keyedFormData });
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
const updateKeyedFormData = useCallback((newData: KeyedFormDataType<T>[]) => {
|
|
827
|
+
const plainFormData = keyedToPlainFormData(newData);
|
|
828
|
+
const newHash = hashObject(plainFormData);
|
|
829
|
+
setState({ formDataHash: newHash, keyedFormData: newData });
|
|
830
|
+
return plainFormData;
|
|
831
|
+
}, []);
|
|
832
|
+
|
|
833
|
+
return { keyedFormData, updateKeyedFormData };
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/** The `ArrayField` component is used to render a field in the schema that is of type `array`. It supports both normal
|
|
837
|
+
* and fixed array, allowing user to add and remove elements from the array data.
|
|
838
|
+
*/
|
|
839
|
+
export default function ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
|
|
840
|
+
props: FieldProps<T[], S, F>,
|
|
841
|
+
) {
|
|
842
|
+
const { schema, uiSchema, errorSchema, fieldPathId, registry, formData, onChange } = props;
|
|
843
|
+
const { schemaUtils, translateString } = registry;
|
|
844
|
+
const { keyedFormData, updateKeyedFormData } = useKeyedFormData<T>(formData);
|
|
845
|
+
// All the children will use childFieldPathId if present in the props, falling back to the fieldPathId
|
|
846
|
+
const childFieldPathId = props.childFieldPathId ?? fieldPathId;
|
|
847
|
+
|
|
189
848
|
/** Callback handler for when the user clicks on the add or add at index buttons. Creates a new row of keyed form data
|
|
190
849
|
* either at the end of the list (when index is not specified) or inserted at the `index` when it is, adding it into
|
|
191
850
|
* the state, and then returning `onChange()` with the plain form data converted from the keyed data
|
|
@@ -193,67 +852,39 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
|
|
|
193
852
|
* @param event - The event for the click
|
|
194
853
|
* @param [index] - The optional index at which to add the new data
|
|
195
854
|
*/
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
event
|
|
199
|
-
|
|
855
|
+
const handleAddItem = useCallback(
|
|
856
|
+
(event: MouseEvent, index?: number) => {
|
|
857
|
+
if (event) {
|
|
858
|
+
event.preventDefault();
|
|
859
|
+
}
|
|
200
860
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
} else if (i >= index) {
|
|
212
|
-
set(newErrorSchema, [i + 1], errorSchema[idx]);
|
|
861
|
+
let newErrorSchema: ErrorSchema<T> | undefined;
|
|
862
|
+
if (errorSchema) {
|
|
863
|
+
newErrorSchema = {};
|
|
864
|
+
for (const idx in errorSchema) {
|
|
865
|
+
const i = parseInt(idx);
|
|
866
|
+
if (index === undefined || i < index) {
|
|
867
|
+
set(newErrorSchema, [i], errorSchema[idx]);
|
|
868
|
+
} else if (i >= index) {
|
|
869
|
+
set(newErrorSchema, [i + 1], errorSchema[idx]);
|
|
870
|
+
}
|
|
213
871
|
}
|
|
214
872
|
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const newKeyedFormDataRow: KeyedFormDataType<T> = {
|
|
218
|
-
key: generateRowId(),
|
|
219
|
-
item: this._getNewFormDataRow(),
|
|
220
|
-
};
|
|
221
|
-
const newKeyedFormData = [...keyedFormData];
|
|
222
|
-
if (index !== undefined) {
|
|
223
|
-
newKeyedFormData.splice(index, 0, newKeyedFormDataRow);
|
|
224
|
-
} else {
|
|
225
|
-
newKeyedFormData.push(newKeyedFormDataRow);
|
|
226
|
-
}
|
|
227
|
-
this.setState(
|
|
228
|
-
{
|
|
229
|
-
keyedFormData: newKeyedFormData,
|
|
230
|
-
updatedKeyedFormData: true,
|
|
231
|
-
},
|
|
232
|
-
() => onChange(keyedToPlainFormData(newKeyedFormData), newErrorSchema as ErrorSchema<T[]>),
|
|
233
|
-
);
|
|
234
|
-
}
|
|
235
873
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
* @param index - The index at which the add button is clicked
|
|
251
|
-
*/
|
|
252
|
-
onAddIndexClick = (index: number) => {
|
|
253
|
-
return (event: MouseEvent) => {
|
|
254
|
-
this._handleAddClick(event, index);
|
|
255
|
-
};
|
|
256
|
-
};
|
|
874
|
+
const newKeyedFormDataRow: KeyedFormDataType<T> = {
|
|
875
|
+
key: generateRowId(),
|
|
876
|
+
item: getNewFormDataRow<T, S, F>(registry, schema),
|
|
877
|
+
};
|
|
878
|
+
const newKeyedFormData = [...keyedFormData];
|
|
879
|
+
if (index !== undefined) {
|
|
880
|
+
newKeyedFormData.splice(index, 0, newKeyedFormDataRow);
|
|
881
|
+
} else {
|
|
882
|
+
newKeyedFormData.push(newKeyedFormDataRow);
|
|
883
|
+
}
|
|
884
|
+
onChange(updateKeyedFormData(newKeyedFormData), childFieldPathId.path, newErrorSchema as ErrorSchema<T[]>);
|
|
885
|
+
},
|
|
886
|
+
[keyedFormData, registry, schema, onChange, updateKeyedFormData, errorSchema, childFieldPathId],
|
|
887
|
+
);
|
|
257
888
|
|
|
258
889
|
/** Callback handler for when the user clicks on the copy button on an existing array element. Clones the row of
|
|
259
890
|
* keyed form data at the `index` into the next position in the state, and then returning `onChange()` with the plain
|
|
@@ -261,16 +892,13 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
|
|
|
261
892
|
*
|
|
262
893
|
* @param index - The index at which the copy button is clicked
|
|
263
894
|
*/
|
|
264
|
-
|
|
265
|
-
|
|
895
|
+
const handleCopyItem = useCallback(
|
|
896
|
+
(event: MouseEvent, index: number) => {
|
|
266
897
|
if (event) {
|
|
267
898
|
event.preventDefault();
|
|
268
899
|
}
|
|
269
900
|
|
|
270
|
-
|
|
271
|
-
const { keyedFormData } = this.state;
|
|
272
|
-
// refs #195: revalidate to ensure properly reindexing errors
|
|
273
|
-
let newErrorSchema: ErrorSchema<T>;
|
|
901
|
+
let newErrorSchema: ErrorSchema<T> | undefined;
|
|
274
902
|
if (errorSchema) {
|
|
275
903
|
newErrorSchema = {};
|
|
276
904
|
for (const idx in errorSchema) {
|
|
@@ -293,15 +921,10 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
|
|
|
293
921
|
} else {
|
|
294
922
|
newKeyedFormData.push(newKeyedFormDataRow);
|
|
295
923
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
},
|
|
301
|
-
() => onChange(keyedToPlainFormData(newKeyedFormData), newErrorSchema as ErrorSchema<T[]>),
|
|
302
|
-
);
|
|
303
|
-
};
|
|
304
|
-
};
|
|
924
|
+
onChange(updateKeyedFormData(newKeyedFormData), childFieldPathId.path, newErrorSchema as ErrorSchema<T[]>);
|
|
925
|
+
},
|
|
926
|
+
[keyedFormData, onChange, updateKeyedFormData, errorSchema, childFieldPathId],
|
|
927
|
+
);
|
|
305
928
|
|
|
306
929
|
/** Callback handler for when the user clicks on the remove button on an existing array element. Removes the row of
|
|
307
930
|
* keyed form data at the `index` in the state, and then returning `onChange()` with the plain form data converted
|
|
@@ -309,15 +932,13 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
|
|
|
309
932
|
*
|
|
310
933
|
* @param index - The index at which the remove button is clicked
|
|
311
934
|
*/
|
|
312
|
-
|
|
313
|
-
|
|
935
|
+
const handleRemoveItem = useCallback(
|
|
936
|
+
(event: MouseEvent, index: number) => {
|
|
314
937
|
if (event) {
|
|
315
938
|
event.preventDefault();
|
|
316
939
|
}
|
|
317
|
-
const { onChange, errorSchema } = this.props;
|
|
318
|
-
const { keyedFormData } = this.state;
|
|
319
940
|
// refs #195: revalidate to ensure properly reindexing errors
|
|
320
|
-
let newErrorSchema: ErrorSchema<T
|
|
941
|
+
let newErrorSchema: ErrorSchema<T> | undefined;
|
|
321
942
|
if (errorSchema) {
|
|
322
943
|
newErrorSchema = {};
|
|
323
944
|
for (const idx in errorSchema) {
|
|
@@ -330,15 +951,10 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
|
|
|
330
951
|
}
|
|
331
952
|
}
|
|
332
953
|
const newKeyedFormData = keyedFormData.filter((_, i) => i !== index);
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
},
|
|
338
|
-
() => onChange(keyedToPlainFormData(newKeyedFormData), newErrorSchema as ErrorSchema<T[]>),
|
|
339
|
-
);
|
|
340
|
-
};
|
|
341
|
-
};
|
|
954
|
+
onChange(updateKeyedFormData(newKeyedFormData), childFieldPathId.path, newErrorSchema as ErrorSchema<T[]>);
|
|
955
|
+
},
|
|
956
|
+
[keyedFormData, onChange, updateKeyedFormData, errorSchema, childFieldPathId],
|
|
957
|
+
);
|
|
342
958
|
|
|
343
959
|
/** Callback handler for when the user clicks on one of the move item buttons on an existing array element. Moves the
|
|
344
960
|
* row of keyed form data at the `index` to the `newIndex` in the state, and then returning `onChange()` with the
|
|
@@ -347,14 +963,13 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
|
|
|
347
963
|
* @param index - The index of the item to move
|
|
348
964
|
* @param newIndex - The index to where the item is to be moved
|
|
349
965
|
*/
|
|
350
|
-
|
|
351
|
-
|
|
966
|
+
const handleReorderItems = useCallback(
|
|
967
|
+
(event: MouseEvent<HTMLButtonElement>, index: number, newIndex: number) => {
|
|
352
968
|
if (event) {
|
|
353
969
|
event.preventDefault();
|
|
354
970
|
event.currentTarget.blur();
|
|
355
971
|
}
|
|
356
|
-
|
|
357
|
-
let newErrorSchema: ErrorSchema<T>;
|
|
972
|
+
let newErrorSchema: ErrorSchema<T> | undefined;
|
|
358
973
|
if (errorSchema) {
|
|
359
974
|
newErrorSchema = {};
|
|
360
975
|
for (const idx in errorSchema) {
|
|
@@ -369,7 +984,6 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
|
|
|
369
984
|
}
|
|
370
985
|
}
|
|
371
986
|
|
|
372
|
-
const { keyedFormData } = this.state;
|
|
373
987
|
function reOrderArray() {
|
|
374
988
|
// Copy item
|
|
375
989
|
const _newKeyedFormData = keyedFormData.slice();
|
|
@@ -381,541 +995,75 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
|
|
|
381
995
|
return _newKeyedFormData;
|
|
382
996
|
}
|
|
383
997
|
const newKeyedFormData = reOrderArray();
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
() => onChange(keyedToPlainFormData(newKeyedFormData), newErrorSchema as ErrorSchema<T[]>),
|
|
389
|
-
);
|
|
390
|
-
};
|
|
391
|
-
};
|
|
998
|
+
onChange(updateKeyedFormData(newKeyedFormData), childFieldPathId.path, newErrorSchema as ErrorSchema<T[]>);
|
|
999
|
+
},
|
|
1000
|
+
[keyedFormData, onChange, updateKeyedFormData, errorSchema, childFieldPathId],
|
|
1001
|
+
);
|
|
392
1002
|
|
|
393
1003
|
/** Callback handler used to deal with changing the value of the data in the array at the `index`. Calls the
|
|
394
1004
|
* `onChange` callback with the updated form data
|
|
395
1005
|
*
|
|
396
1006
|
* @param index - The index of the item being changed
|
|
397
1007
|
*/
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
const arrayData = Array.isArray(formData) ? formData : [];
|
|
402
|
-
const newFormData = arrayData.map((item: T, i: number) => {
|
|
1008
|
+
const handleChange = useCallback(
|
|
1009
|
+
(value: any, path: FieldPathList, newErrorSchema?: ErrorSchema<T>, id?: string) => {
|
|
1010
|
+
onChange(
|
|
403
1011
|
// We need to treat undefined items as nulls to have validation.
|
|
404
1012
|
// See https://github.com/tdegrunt/jsonschema/issues/206
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
onChange(
|
|
409
|
-
newFormData,
|
|
410
|
-
errorSchema &&
|
|
411
|
-
errorSchema && {
|
|
412
|
-
...errorSchema,
|
|
413
|
-
[index]: newErrorSchema,
|
|
414
|
-
},
|
|
1013
|
+
value === undefined ? null : value,
|
|
1014
|
+
path,
|
|
1015
|
+
newErrorSchema as ErrorSchema<T[]>,
|
|
415
1016
|
id,
|
|
416
1017
|
);
|
|
417
|
-
}
|
|
418
|
-
|
|
1018
|
+
},
|
|
1019
|
+
[onChange],
|
|
1020
|
+
);
|
|
419
1021
|
|
|
420
1022
|
/** Callback handler used to change the value for a checkbox */
|
|
421
|
-
onSelectChange = (
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
*/
|
|
428
|
-
render() {
|
|
429
|
-
const { schema, uiSchema, idSchema, registry } = this.props;
|
|
430
|
-
const { schemaUtils, translateString } = registry;
|
|
431
|
-
if (!(ITEMS_KEY in schema)) {
|
|
432
|
-
const uiOptions = getUiOptions<T[], S, F>(uiSchema);
|
|
433
|
-
const UnsupportedFieldTemplate = getTemplate<'UnsupportedFieldTemplate', T[], S, F>(
|
|
434
|
-
'UnsupportedFieldTemplate',
|
|
435
|
-
registry,
|
|
436
|
-
uiOptions,
|
|
437
|
-
);
|
|
1023
|
+
const onSelectChange = useCallback(
|
|
1024
|
+
(value: any) => {
|
|
1025
|
+
onChange(value, childFieldPathId.path, undefined, childFieldPathId?.[ID_KEY]);
|
|
1026
|
+
},
|
|
1027
|
+
[onChange, childFieldPathId],
|
|
1028
|
+
);
|
|
438
1029
|
|
|
439
|
-
|
|
440
|
-
<UnsupportedFieldTemplate
|
|
441
|
-
schema={schema}
|
|
442
|
-
idSchema={idSchema}
|
|
443
|
-
reason={translateString(TranslatableString.MissingItems)}
|
|
444
|
-
registry={registry}
|
|
445
|
-
/>
|
|
446
|
-
);
|
|
447
|
-
}
|
|
448
|
-
if (schemaUtils.isMultiSelect(schema)) {
|
|
449
|
-
// If array has enum or uniqueItems set to true, call renderMultiSelect() to render the default multiselect widget or a custom widget, if specified.
|
|
450
|
-
return this.renderMultiSelect();
|
|
451
|
-
}
|
|
452
|
-
if (isCustomWidget<T[], S, F>(uiSchema)) {
|
|
453
|
-
return this.renderCustomWidget();
|
|
454
|
-
}
|
|
455
|
-
if (isFixedItems(schema)) {
|
|
456
|
-
return this.renderFixedArray();
|
|
457
|
-
}
|
|
458
|
-
if (schemaUtils.isFilesArray(schema, uiSchema)) {
|
|
459
|
-
return this.renderFiles();
|
|
460
|
-
}
|
|
461
|
-
return this.renderNormalArray();
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
/** Renders a normal array without any limitations of length
|
|
465
|
-
*/
|
|
466
|
-
renderNormalArray() {
|
|
467
|
-
const {
|
|
468
|
-
schema,
|
|
469
|
-
uiSchema = {},
|
|
470
|
-
errorSchema,
|
|
471
|
-
idSchema,
|
|
472
|
-
name,
|
|
473
|
-
title,
|
|
474
|
-
disabled = false,
|
|
475
|
-
readonly = false,
|
|
476
|
-
autofocus = false,
|
|
477
|
-
required = false,
|
|
478
|
-
registry,
|
|
479
|
-
onBlur,
|
|
480
|
-
onFocus,
|
|
481
|
-
idPrefix,
|
|
482
|
-
idSeparator = '_',
|
|
483
|
-
rawErrors,
|
|
484
|
-
} = this.props;
|
|
485
|
-
const { keyedFormData } = this.state;
|
|
486
|
-
const fieldTitle = schema.title || title || name;
|
|
487
|
-
const { schemaUtils, formContext } = registry;
|
|
1030
|
+
if (!(ITEMS_KEY in schema)) {
|
|
488
1031
|
const uiOptions = getUiOptions<T[], S, F>(uiSchema);
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
const formData = keyedToPlainFormData(this.state.keyedFormData);
|
|
492
|
-
const canAdd = this.canAddItem(formData);
|
|
493
|
-
const arrayProps: ArrayFieldTemplateProps<T[], S, F> = {
|
|
494
|
-
canAdd,
|
|
495
|
-
items: keyedFormData.map((keyedItem, index) => {
|
|
496
|
-
const { key, item } = keyedItem;
|
|
497
|
-
// While we are actually dealing with a single item of type T, the types require a T[], so cast
|
|
498
|
-
const itemCast = item as unknown as T[];
|
|
499
|
-
const itemSchema = schemaUtils.retrieveSchema(_schemaItems, itemCast);
|
|
500
|
-
const itemErrorSchema = errorSchema ? (errorSchema[index] as ErrorSchema<T[]>) : undefined;
|
|
501
|
-
const itemIdPrefix = idSchema.$id + idSeparator + index;
|
|
502
|
-
const itemIdSchema = schemaUtils.toIdSchema(itemSchema, itemIdPrefix, itemCast, idPrefix, idSeparator);
|
|
503
|
-
return this.renderArrayFieldItem({
|
|
504
|
-
key,
|
|
505
|
-
index,
|
|
506
|
-
name: name && `${name}-${index}`,
|
|
507
|
-
title: fieldTitle ? `${fieldTitle}-${index + 1}` : undefined,
|
|
508
|
-
canAdd,
|
|
509
|
-
canMoveUp: index > 0,
|
|
510
|
-
canMoveDown: index < formData.length - 1,
|
|
511
|
-
itemSchema,
|
|
512
|
-
itemIdSchema,
|
|
513
|
-
itemErrorSchema,
|
|
514
|
-
itemData: itemCast,
|
|
515
|
-
itemUiSchema: uiSchema.items,
|
|
516
|
-
autofocus: autofocus && index === 0,
|
|
517
|
-
onBlur,
|
|
518
|
-
onFocus,
|
|
519
|
-
rawErrors,
|
|
520
|
-
totalItems: keyedFormData.length,
|
|
521
|
-
});
|
|
522
|
-
}),
|
|
523
|
-
className: `rjsf-field rjsf-field-array rjsf-field-array-of-${itemsSchema.type}`,
|
|
524
|
-
disabled,
|
|
525
|
-
idSchema,
|
|
526
|
-
uiSchema,
|
|
527
|
-
onAddClick: this.onAddClick,
|
|
528
|
-
readonly,
|
|
529
|
-
required,
|
|
530
|
-
schema,
|
|
531
|
-
title: fieldTitle,
|
|
532
|
-
formContext,
|
|
533
|
-
formData,
|
|
534
|
-
rawErrors,
|
|
1032
|
+
const UnsupportedFieldTemplate = getTemplate<'UnsupportedFieldTemplate', T[], S, F>(
|
|
1033
|
+
'UnsupportedFieldTemplate',
|
|
535
1034
|
registry,
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
const Template = getTemplate<'ArrayFieldTemplate', T[], S, F>('ArrayFieldTemplate', registry, uiOptions);
|
|
539
|
-
return <Template {...arrayProps} />;
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
/** Renders an array using the custom widget provided by the user in the `uiSchema`
|
|
543
|
-
*/
|
|
544
|
-
renderCustomWidget() {
|
|
545
|
-
const {
|
|
546
|
-
schema,
|
|
547
|
-
idSchema,
|
|
548
|
-
uiSchema,
|
|
549
|
-
disabled = false,
|
|
550
|
-
readonly = false,
|
|
551
|
-
autofocus = false,
|
|
552
|
-
required = false,
|
|
553
|
-
hideError,
|
|
554
|
-
placeholder,
|
|
555
|
-
onBlur,
|
|
556
|
-
onFocus,
|
|
557
|
-
formData: items = [],
|
|
558
|
-
registry,
|
|
559
|
-
rawErrors,
|
|
560
|
-
name,
|
|
561
|
-
} = this.props;
|
|
562
|
-
const { widgets, formContext, globalUiOptions, schemaUtils } = registry;
|
|
563
|
-
const { widget, title: uiTitle, ...options } = getUiOptions<T[], S, F>(uiSchema, globalUiOptions);
|
|
564
|
-
const Widget = getWidget<T[], S, F>(schema, widget, widgets);
|
|
565
|
-
const label = uiTitle ?? schema.title ?? name;
|
|
566
|
-
const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema, globalUiOptions);
|
|
567
|
-
return (
|
|
568
|
-
<Widget
|
|
569
|
-
id={idSchema.$id}
|
|
570
|
-
name={name}
|
|
571
|
-
multiple
|
|
572
|
-
onChange={this.onSelectChange}
|
|
573
|
-
onBlur={onBlur}
|
|
574
|
-
onFocus={onFocus}
|
|
575
|
-
options={options}
|
|
576
|
-
schema={schema}
|
|
577
|
-
uiSchema={uiSchema}
|
|
578
|
-
registry={registry}
|
|
579
|
-
value={items}
|
|
580
|
-
disabled={disabled}
|
|
581
|
-
readonly={readonly}
|
|
582
|
-
hideError={hideError}
|
|
583
|
-
required={required}
|
|
584
|
-
label={label}
|
|
585
|
-
hideLabel={!displayLabel}
|
|
586
|
-
placeholder={placeholder}
|
|
587
|
-
formContext={formContext}
|
|
588
|
-
autofocus={autofocus}
|
|
589
|
-
rawErrors={rawErrors}
|
|
590
|
-
/>
|
|
1035
|
+
uiOptions,
|
|
591
1036
|
);
|
|
592
|
-
}
|
|
593
1037
|
|
|
594
|
-
/** Renders an array as a set of checkboxes
|
|
595
|
-
*/
|
|
596
|
-
renderMultiSelect() {
|
|
597
|
-
const {
|
|
598
|
-
schema,
|
|
599
|
-
idSchema,
|
|
600
|
-
uiSchema,
|
|
601
|
-
formData: items = [],
|
|
602
|
-
disabled = false,
|
|
603
|
-
readonly = false,
|
|
604
|
-
autofocus = false,
|
|
605
|
-
required = false,
|
|
606
|
-
placeholder,
|
|
607
|
-
onBlur,
|
|
608
|
-
onFocus,
|
|
609
|
-
registry,
|
|
610
|
-
rawErrors,
|
|
611
|
-
name,
|
|
612
|
-
} = this.props;
|
|
613
|
-
const { widgets, schemaUtils, formContext, globalUiOptions } = registry;
|
|
614
|
-
const itemsSchema = schemaUtils.retrieveSchema(schema.items as S, items);
|
|
615
|
-
const enumOptions = optionsList<T[], S, F>(itemsSchema, uiSchema);
|
|
616
|
-
const { widget = 'select', title: uiTitle, ...options } = getUiOptions<T[], S, F>(uiSchema, globalUiOptions);
|
|
617
|
-
const Widget = getWidget<T[], S, F>(schema, widget, widgets);
|
|
618
|
-
const label = uiTitle ?? schema.title ?? name;
|
|
619
|
-
const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema, globalUiOptions);
|
|
620
1038
|
return (
|
|
621
|
-
<
|
|
622
|
-
id={idSchema.$id}
|
|
623
|
-
name={name}
|
|
624
|
-
multiple
|
|
625
|
-
onChange={this.onSelectChange}
|
|
626
|
-
onBlur={onBlur}
|
|
627
|
-
onFocus={onFocus}
|
|
628
|
-
options={{ ...options, enumOptions }}
|
|
1039
|
+
<UnsupportedFieldTemplate
|
|
629
1040
|
schema={schema}
|
|
630
|
-
|
|
1041
|
+
fieldPathId={fieldPathId}
|
|
1042
|
+
reason={translateString(TranslatableString.MissingItems)}
|
|
631
1043
|
registry={registry}
|
|
632
|
-
value={items}
|
|
633
|
-
disabled={disabled}
|
|
634
|
-
readonly={readonly}
|
|
635
|
-
required={required}
|
|
636
|
-
label={label}
|
|
637
|
-
hideLabel={!displayLabel}
|
|
638
|
-
placeholder={placeholder}
|
|
639
|
-
formContext={formContext}
|
|
640
|
-
autofocus={autofocus}
|
|
641
|
-
rawErrors={rawErrors}
|
|
642
1044
|
/>
|
|
643
1045
|
);
|
|
644
1046
|
}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
autofocus = false,
|
|
657
|
-
required = false,
|
|
658
|
-
onBlur,
|
|
659
|
-
onFocus,
|
|
660
|
-
registry,
|
|
661
|
-
formData: items = [],
|
|
662
|
-
rawErrors,
|
|
663
|
-
} = this.props;
|
|
664
|
-
const { widgets, formContext, globalUiOptions, schemaUtils } = registry;
|
|
665
|
-
const { widget = 'files', title: uiTitle, ...options } = getUiOptions<T[], S, F>(uiSchema, globalUiOptions);
|
|
666
|
-
const Widget = getWidget<T[], S, F>(schema, widget, widgets);
|
|
667
|
-
const label = uiTitle ?? schema.title ?? name;
|
|
668
|
-
const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema, globalUiOptions);
|
|
669
|
-
return (
|
|
670
|
-
<Widget
|
|
671
|
-
options={options}
|
|
672
|
-
id={idSchema.$id}
|
|
673
|
-
name={name}
|
|
674
|
-
multiple
|
|
675
|
-
onChange={this.onSelectChange}
|
|
676
|
-
onBlur={onBlur}
|
|
677
|
-
onFocus={onFocus}
|
|
678
|
-
schema={schema}
|
|
679
|
-
uiSchema={uiSchema}
|
|
680
|
-
value={items}
|
|
681
|
-
disabled={disabled}
|
|
682
|
-
readonly={readonly}
|
|
683
|
-
required={required}
|
|
684
|
-
registry={registry}
|
|
685
|
-
formContext={formContext}
|
|
686
|
-
autofocus={autofocus}
|
|
687
|
-
rawErrors={rawErrors}
|
|
688
|
-
label={label}
|
|
689
|
-
hideLabel={!displayLabel}
|
|
690
|
-
/>
|
|
691
|
-
);
|
|
1047
|
+
const arrayProps = {
|
|
1048
|
+
handleAddItem,
|
|
1049
|
+
handleCopyItem,
|
|
1050
|
+
handleRemoveItem,
|
|
1051
|
+
handleReorderItems,
|
|
1052
|
+
keyedFormData,
|
|
1053
|
+
onChange: handleChange,
|
|
1054
|
+
};
|
|
1055
|
+
if (schemaUtils.isMultiSelect(schema)) {
|
|
1056
|
+
// If array has enum or uniqueItems set to true, call renderMultiSelect() to render the default multiselect widget or a custom widget, if specified.
|
|
1057
|
+
return <ArrayAsMultiSelect<T, S, F> {...props} fieldPathId={childFieldPathId} onSelectChange={onSelectChange} />;
|
|
692
1058
|
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
*/
|
|
696
|
-
renderFixedArray() {
|
|
697
|
-
const {
|
|
698
|
-
schema,
|
|
699
|
-
uiSchema = {},
|
|
700
|
-
formData = [],
|
|
701
|
-
errorSchema,
|
|
702
|
-
idPrefix,
|
|
703
|
-
idSeparator = '_',
|
|
704
|
-
idSchema,
|
|
705
|
-
name,
|
|
706
|
-
title,
|
|
707
|
-
disabled = false,
|
|
708
|
-
readonly = false,
|
|
709
|
-
autofocus = false,
|
|
710
|
-
required = false,
|
|
711
|
-
registry,
|
|
712
|
-
onBlur,
|
|
713
|
-
onFocus,
|
|
714
|
-
rawErrors,
|
|
715
|
-
} = this.props;
|
|
716
|
-
const { keyedFormData } = this.state;
|
|
717
|
-
let { formData: items = [] } = this.props;
|
|
718
|
-
const fieldTitle = schema.title || title || name;
|
|
719
|
-
const uiOptions = getUiOptions<T[], S, F>(uiSchema);
|
|
720
|
-
const { schemaUtils, formContext } = registry;
|
|
721
|
-
const _schemaItems: S[] = isObject(schema.items) ? (schema.items as S[]) : ([] as S[]);
|
|
722
|
-
const itemSchemas = _schemaItems.map((item: S, index: number) =>
|
|
723
|
-
schemaUtils.retrieveSchema(item, formData[index] as unknown as T[]),
|
|
724
|
-
);
|
|
725
|
-
const additionalSchema = isObject(schema.additionalItems)
|
|
726
|
-
? schemaUtils.retrieveSchema(schema.additionalItems as S, formData)
|
|
727
|
-
: null;
|
|
728
|
-
|
|
729
|
-
if (!items || items.length < itemSchemas.length) {
|
|
730
|
-
// to make sure at least all fixed items are generated
|
|
731
|
-
items = items || [];
|
|
732
|
-
items = items.concat(new Array(itemSchemas.length - items.length));
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
// These are the props passed into the render function
|
|
736
|
-
const canAdd = this.canAddItem(items) && !!additionalSchema;
|
|
737
|
-
const arrayProps: ArrayFieldTemplateProps<T[], S, F> = {
|
|
738
|
-
canAdd,
|
|
739
|
-
className: 'rjsf-field rjsf-field-array rjsf-field-array-fixed-items',
|
|
740
|
-
disabled,
|
|
741
|
-
idSchema,
|
|
742
|
-
formData,
|
|
743
|
-
items: keyedFormData.map((keyedItem, index) => {
|
|
744
|
-
const { key, item } = keyedItem;
|
|
745
|
-
// While we are actually dealing with a single item of type T, the types require a T[], so cast
|
|
746
|
-
const itemCast = item as unknown as T[];
|
|
747
|
-
const additional = index >= itemSchemas.length;
|
|
748
|
-
const itemSchema =
|
|
749
|
-
(additional && isObject(schema.additionalItems)
|
|
750
|
-
? schemaUtils.retrieveSchema(schema.additionalItems as S, itemCast)
|
|
751
|
-
: itemSchemas[index]) || {};
|
|
752
|
-
const itemIdPrefix = idSchema.$id + idSeparator + index;
|
|
753
|
-
const itemIdSchema = schemaUtils.toIdSchema(itemSchema, itemIdPrefix, itemCast, idPrefix, idSeparator);
|
|
754
|
-
const itemUiSchema = additional
|
|
755
|
-
? uiSchema.additionalItems || {}
|
|
756
|
-
: Array.isArray(uiSchema.items)
|
|
757
|
-
? uiSchema.items[index]
|
|
758
|
-
: uiSchema.items || {};
|
|
759
|
-
const itemErrorSchema = errorSchema ? (errorSchema[index] as ErrorSchema<T[]>) : undefined;
|
|
760
|
-
|
|
761
|
-
return this.renderArrayFieldItem({
|
|
762
|
-
key,
|
|
763
|
-
index,
|
|
764
|
-
name: name && `${name}-${index}`,
|
|
765
|
-
title: fieldTitle ? `${fieldTitle}-${index + 1}` : undefined,
|
|
766
|
-
canAdd,
|
|
767
|
-
canRemove: additional,
|
|
768
|
-
canMoveUp: index >= itemSchemas.length + 1,
|
|
769
|
-
canMoveDown: additional && index < items.length - 1,
|
|
770
|
-
itemSchema,
|
|
771
|
-
itemData: itemCast,
|
|
772
|
-
itemUiSchema,
|
|
773
|
-
itemIdSchema,
|
|
774
|
-
itemErrorSchema,
|
|
775
|
-
autofocus: autofocus && index === 0,
|
|
776
|
-
onBlur,
|
|
777
|
-
onFocus,
|
|
778
|
-
rawErrors,
|
|
779
|
-
totalItems: keyedFormData.length,
|
|
780
|
-
});
|
|
781
|
-
}),
|
|
782
|
-
onAddClick: this.onAddClick,
|
|
783
|
-
readonly,
|
|
784
|
-
required,
|
|
785
|
-
registry,
|
|
786
|
-
schema,
|
|
787
|
-
uiSchema,
|
|
788
|
-
title: fieldTitle,
|
|
789
|
-
formContext,
|
|
790
|
-
errorSchema,
|
|
791
|
-
rawErrors,
|
|
792
|
-
};
|
|
793
|
-
|
|
794
|
-
const Template = getTemplate<'ArrayFieldTemplate', T[], S, F>('ArrayFieldTemplate', registry, uiOptions);
|
|
795
|
-
return <Template {...arrayProps} />;
|
|
1059
|
+
if (isCustomWidget<T[], S, F>(uiSchema)) {
|
|
1060
|
+
return <ArrayAsCustomWidget<T, S, F> {...props} fieldPathId={childFieldPathId} onSelectChange={onSelectChange} />;
|
|
796
1061
|
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
*/
|
|
803
|
-
renderArrayFieldItem(props: {
|
|
804
|
-
key: string;
|
|
805
|
-
index: number;
|
|
806
|
-
name: string;
|
|
807
|
-
title: string | undefined;
|
|
808
|
-
canAdd: boolean;
|
|
809
|
-
canRemove?: boolean;
|
|
810
|
-
canMoveUp: boolean;
|
|
811
|
-
canMoveDown: boolean;
|
|
812
|
-
itemSchema: S;
|
|
813
|
-
itemData: T[];
|
|
814
|
-
itemUiSchema: UiSchema<T[], S, F>;
|
|
815
|
-
itemIdSchema: IdSchema<T[]>;
|
|
816
|
-
itemErrorSchema?: ErrorSchema<T[]>;
|
|
817
|
-
autofocus?: boolean;
|
|
818
|
-
onBlur: FieldProps<T[], S, F>['onBlur'];
|
|
819
|
-
onFocus: FieldProps<T[], S, F>['onFocus'];
|
|
820
|
-
rawErrors?: string[];
|
|
821
|
-
totalItems: number;
|
|
822
|
-
}) {
|
|
823
|
-
const {
|
|
824
|
-
key,
|
|
825
|
-
index,
|
|
826
|
-
name,
|
|
827
|
-
canAdd,
|
|
828
|
-
canRemove = true,
|
|
829
|
-
canMoveUp,
|
|
830
|
-
canMoveDown,
|
|
831
|
-
itemSchema,
|
|
832
|
-
itemData,
|
|
833
|
-
itemUiSchema,
|
|
834
|
-
itemIdSchema,
|
|
835
|
-
itemErrorSchema,
|
|
836
|
-
autofocus,
|
|
837
|
-
onBlur,
|
|
838
|
-
onFocus,
|
|
839
|
-
rawErrors,
|
|
840
|
-
totalItems,
|
|
841
|
-
title,
|
|
842
|
-
} = props;
|
|
843
|
-
const { disabled, hideError, idPrefix, idSeparator, readonly, uiSchema, registry, formContext } = this.props;
|
|
844
|
-
const {
|
|
845
|
-
fields: { ArraySchemaField, SchemaField },
|
|
846
|
-
globalUiOptions,
|
|
847
|
-
} = registry;
|
|
848
|
-
const ItemSchemaField = ArraySchemaField || SchemaField;
|
|
849
|
-
const { orderable = true, removable = true, copyable = false } = getUiOptions<T[], S, F>(uiSchema, globalUiOptions);
|
|
850
|
-
const has: { [key: string]: boolean } = {
|
|
851
|
-
moveUp: orderable && canMoveUp,
|
|
852
|
-
moveDown: orderable && canMoveDown,
|
|
853
|
-
copy: copyable && canAdd,
|
|
854
|
-
remove: removable && canRemove,
|
|
855
|
-
toolbar: false,
|
|
856
|
-
};
|
|
857
|
-
has.toolbar = Object.keys(has).some((key: keyof typeof has) => has[key]);
|
|
858
|
-
|
|
859
|
-
return {
|
|
860
|
-
children: (
|
|
861
|
-
<ItemSchemaField
|
|
862
|
-
name={name}
|
|
863
|
-
title={title}
|
|
864
|
-
index={index}
|
|
865
|
-
schema={itemSchema}
|
|
866
|
-
uiSchema={itemUiSchema}
|
|
867
|
-
formData={itemData}
|
|
868
|
-
formContext={formContext}
|
|
869
|
-
errorSchema={itemErrorSchema}
|
|
870
|
-
idPrefix={idPrefix}
|
|
871
|
-
idSeparator={idSeparator}
|
|
872
|
-
idSchema={itemIdSchema}
|
|
873
|
-
required={this.isItemRequired(itemSchema)}
|
|
874
|
-
onChange={this.onChangeForIndex(index)}
|
|
875
|
-
onBlur={onBlur}
|
|
876
|
-
onFocus={onFocus}
|
|
877
|
-
registry={registry}
|
|
878
|
-
disabled={disabled}
|
|
879
|
-
readonly={readonly}
|
|
880
|
-
hideError={hideError}
|
|
881
|
-
autofocus={autofocus}
|
|
882
|
-
rawErrors={rawErrors}
|
|
883
|
-
/>
|
|
884
|
-
),
|
|
885
|
-
buttonsProps: {
|
|
886
|
-
idSchema: itemIdSchema,
|
|
887
|
-
disabled: disabled,
|
|
888
|
-
readonly: readonly,
|
|
889
|
-
canAdd,
|
|
890
|
-
hasCopy: has.copy,
|
|
891
|
-
hasMoveUp: has.moveUp,
|
|
892
|
-
hasMoveDown: has.moveDown,
|
|
893
|
-
hasRemove: has.remove,
|
|
894
|
-
index: index,
|
|
895
|
-
totalItems: totalItems,
|
|
896
|
-
onAddIndexClick: this.onAddIndexClick,
|
|
897
|
-
onCopyIndexClick: this.onCopyIndexClick,
|
|
898
|
-
onDropIndexClick: this.onDropIndexClick,
|
|
899
|
-
onReorderClick: this.onReorderClick,
|
|
900
|
-
registry: registry,
|
|
901
|
-
schema: itemSchema,
|
|
902
|
-
uiSchema: itemUiSchema,
|
|
903
|
-
},
|
|
904
|
-
className: 'rjsf-array-item',
|
|
905
|
-
disabled,
|
|
906
|
-
hasToolbar: has.toolbar,
|
|
907
|
-
index,
|
|
908
|
-
totalItems,
|
|
909
|
-
key,
|
|
910
|
-
readonly,
|
|
911
|
-
registry,
|
|
912
|
-
schema: itemSchema,
|
|
913
|
-
uiSchema: itemUiSchema,
|
|
914
|
-
};
|
|
1062
|
+
if (isFixedItems(schema)) {
|
|
1063
|
+
return <FixedArray<T, S, F> {...props} {...arrayProps} />;
|
|
1064
|
+
}
|
|
1065
|
+
if (schemaUtils.isFilesArray(schema, uiSchema)) {
|
|
1066
|
+
return <ArrayAsFiles {...props} fieldPathId={childFieldPathId} onSelectChange={onSelectChange} />;
|
|
915
1067
|
}
|
|
1068
|
+
return <NormalArray<T, S, F> {...props} {...arrayProps} />;
|
|
916
1069
|
}
|
|
917
|
-
|
|
918
|
-
/** `ArrayField` is `React.ComponentType<FieldProps<T[], S, F>>` (necessarily) but the `registry` requires things to be a
|
|
919
|
-
* `Field` which is defined as `React.ComponentType<FieldProps<T, S, F>>`, so cast it to make `registry` happy.
|
|
920
|
-
*/
|
|
921
|
-
export default ArrayField;
|