@rjsf/core 4.2.3 → 5.0.0-beta.4
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 +4 -2
- package/dist/core.cjs.development.js +2952 -4606
- package/dist/core.cjs.development.js.map +1 -1
- package/dist/core.cjs.production.min.js +1 -1
- package/dist/core.cjs.production.min.js.map +1 -1
- package/dist/core.esm.js +2900 -4564
- package/dist/core.esm.js.map +1 -1
- package/dist/core.umd.development.js +4054 -0
- package/dist/core.umd.development.js.map +1 -0
- package/dist/core.umd.production.min.js +2 -0
- package/dist/core.umd.production.min.js.map +1 -0
- package/dist/index.d.ts +295 -70
- package/package.json +41 -71
- package/dist/cjs/components/AddButton.js +0 -39
- package/dist/cjs/components/ErrorList.js +0 -41
- package/dist/cjs/components/Form.js +0 -588
- package/dist/cjs/components/IconButton.js +0 -37
- package/dist/cjs/components/fields/ArrayField.js +0 -977
- package/dist/cjs/components/fields/BooleanField.js +0 -107
- package/dist/cjs/components/fields/DescriptionField.js +0 -50
- package/dist/cjs/components/fields/MultiSchemaField.js +0 -275
- package/dist/cjs/components/fields/NullField.js +0 -66
- package/dist/cjs/components/fields/NumberField.js +0 -156
- package/dist/cjs/components/fields/ObjectField.js +0 -366
- package/dist/cjs/components/fields/SchemaField.js +0 -546
- package/dist/cjs/components/fields/StringField.js +0 -98
- package/dist/cjs/components/fields/TitleField.js +0 -42
- package/dist/cjs/components/fields/UnsupportedField.js +0 -46
- package/dist/cjs/components/fields/index.js +0 -47
- package/dist/cjs/components/widgets/AltDateTimeWidget.js +0 -48
- package/dist/cjs/components/widgets/AltDateWidget.js +0 -301
- package/dist/cjs/components/widgets/BaseInput.js +0 -151
- package/dist/cjs/components/widgets/CheckboxWidget.js +0 -85
- package/dist/cjs/components/widgets/CheckboxesWidget.js +0 -121
- package/dist/cjs/components/widgets/ColorWidget.js +0 -45
- package/dist/cjs/components/widgets/DateTimeWidget.js +0 -43
- package/dist/cjs/components/widgets/DateWidget.js +0 -39
- package/dist/cjs/components/widgets/EmailWidget.js +0 -34
- package/dist/cjs/components/widgets/FileWidget.js +0 -215
- package/dist/cjs/components/widgets/HiddenWidget.js +0 -36
- package/dist/cjs/components/widgets/PasswordWidget.js +0 -34
- package/dist/cjs/components/widgets/RadioWidget.js +0 -107
- package/dist/cjs/components/widgets/RangeWidget.js +0 -46
- package/dist/cjs/components/widgets/SelectWidget.js +0 -151
- package/dist/cjs/components/widgets/SubmitButton.js +0 -35
- package/dist/cjs/components/widgets/TextWidget.js +0 -31
- package/dist/cjs/components/widgets/TextareaWidget.js +0 -81
- package/dist/cjs/components/widgets/URLWidget.js +0 -34
- package/dist/cjs/components/widgets/UpDownWidget.js +0 -36
- package/dist/cjs/components/widgets/index.js +0 -73
- package/dist/cjs/defaultRegistry.js +0 -23
- package/dist/cjs/index.js +0 -37
- package/dist/cjs/types.js +0 -47
- package/dist/cjs/utils.js +0 -1407
- package/dist/cjs/validate.js +0 -354
- package/dist/cjs/withTheme.js +0 -53
- package/dist/components/AddButton.d.ts +0 -5
- package/dist/components/ErrorList.d.ts +0 -1
- package/dist/components/Form.d.ts +0 -48
- package/dist/components/IconButton.d.ts +0 -1
- package/dist/components/fields/ArrayField.d.ts +0 -51
- package/dist/components/fields/BooleanField.d.ts +0 -45
- package/dist/components/fields/DescriptionField.d.ts +0 -9
- package/dist/components/fields/MultiSchemaField.d.ts +0 -40
- package/dist/components/fields/NullField.d.ts +0 -6
- package/dist/components/fields/NumberField.d.ts +0 -63
- package/dist/components/fields/ObjectField.d.ts +0 -22
- package/dist/components/fields/SchemaField.d.ts +0 -39
- package/dist/components/fields/StringField.d.ts +0 -45
- package/dist/components/fields/TitleField.d.ts +0 -10
- package/dist/components/fields/UnsupportedField.d.ts +0 -14
- package/dist/components/fields/index.d.ts +0 -26
- package/dist/components/widgets/AltDateTimeWidget.d.ts +0 -22
- package/dist/components/widgets/AltDateWidget.d.ts +0 -22
- package/dist/components/widgets/BaseInput.d.ts +0 -23
- package/dist/components/widgets/CheckboxWidget.d.ts +0 -18
- package/dist/components/widgets/CheckboxesWidget.d.ts +0 -26
- package/dist/components/widgets/ColorWidget.d.ts +0 -15
- package/dist/components/widgets/DateTimeWidget.d.ts +0 -8
- package/dist/components/widgets/DateWidget.d.ts +0 -8
- package/dist/components/widgets/EmailWidget.d.ts +0 -8
- package/dist/components/widgets/FileWidget.d.ts +0 -19
- package/dist/components/widgets/HiddenWidget.d.ts +0 -12
- package/dist/components/widgets/PasswordWidget.d.ts +0 -8
- package/dist/components/widgets/RadioWidget.d.ts +0 -22
- package/dist/components/widgets/RangeWidget.d.ts +0 -8
- package/dist/components/widgets/SelectWidget.d.ts +0 -24
- package/dist/components/widgets/SubmitButton.d.ts +0 -3
- package/dist/components/widgets/TextWidget.d.ts +0 -9
- package/dist/components/widgets/TextareaWidget.d.ts +0 -25
- package/dist/components/widgets/URLWidget.d.ts +0 -8
- package/dist/components/widgets/UpDownWidget.d.ts +0 -8
- package/dist/components/widgets/index.d.ts +0 -43
- package/dist/defaultRegistry.d.ts +0 -41
- package/dist/es/components/AddButton.js +0 -28
- package/dist/es/components/ErrorList.js +0 -31
- package/dist/es/components/Form.js +0 -572
- package/dist/es/components/IconButton.js +0 -27
- package/dist/es/components/fields/ArrayField.js +0 -959
- package/dist/es/components/fields/BooleanField.js +0 -93
- package/dist/es/components/fields/DescriptionField.js +0 -39
- package/dist/es/components/fields/MultiSchemaField.js +0 -261
- package/dist/es/components/fields/NullField.js +0 -55
- package/dist/es/components/fields/NumberField.js +0 -141
- package/dist/es/components/fields/ObjectField.js +0 -351
- package/dist/es/components/fields/SchemaField.js +0 -528
- package/dist/es/components/fields/StringField.js +0 -84
- package/dist/es/components/fields/TitleField.js +0 -30
- package/dist/es/components/fields/UnsupportedField.js +0 -35
- package/dist/es/components/fields/index.js +0 -26
- package/dist/es/components/widgets/AltDateTimeWidget.js +0 -36
- package/dist/es/components/widgets/AltDateWidget.js +0 -287
- package/dist/es/components/widgets/BaseInput.js +0 -140
- package/dist/es/components/widgets/CheckboxWidget.js +0 -73
- package/dist/es/components/widgets/CheckboxesWidget.js +0 -108
- package/dist/es/components/widgets/ColorWidget.js +0 -34
- package/dist/es/components/widgets/DateTimeWidget.js +0 -31
- package/dist/es/components/widgets/DateWidget.js +0 -28
- package/dist/es/components/widgets/EmailWidget.js +0 -23
- package/dist/es/components/widgets/FileWidget.js +0 -201
- package/dist/es/components/widgets/HiddenWidget.js +0 -25
- package/dist/es/components/widgets/PasswordWidget.js +0 -23
- package/dist/es/components/widgets/RadioWidget.js +0 -94
- package/dist/es/components/widgets/RangeWidget.js +0 -34
- package/dist/es/components/widgets/SelectWidget.js +0 -138
- package/dist/es/components/widgets/SubmitButton.js +0 -24
- package/dist/es/components/widgets/TextWidget.js +0 -20
- package/dist/es/components/widgets/TextareaWidget.js +0 -70
- package/dist/es/components/widgets/URLWidget.js +0 -23
- package/dist/es/components/widgets/UpDownWidget.js +0 -24
- package/dist/es/components/widgets/index.js +0 -43
- package/dist/es/defaultRegistry.js +0 -12
- package/dist/es/index.js +0 -16
- package/dist/es/types.js +0 -34
- package/dist/es/utils.js +0 -1309
- package/dist/es/validate.js +0 -338
- package/dist/es/withTheme.js +0 -39
- package/dist/react-jsonschema-form.js +0 -26
- package/dist/react-jsonschema-form.js.map +0 -1
- package/dist/types.d.ts +0 -50
- package/dist/utils.d.ts +0 -81
- package/dist/validate.d.ts +0 -21
- package/dist/withTheme.d.ts +0 -10
- package/index.d.ts +0 -500
- package/lib/components/AddButton.js +0 -28
- package/lib/components/ErrorList.js +0 -31
- package/lib/components/Form.js +0 -572
- package/lib/components/IconButton.js +0 -27
- package/lib/components/fields/ArrayField.js +0 -959
- package/lib/components/fields/BooleanField.js +0 -93
- package/lib/components/fields/DescriptionField.js +0 -39
- package/lib/components/fields/MultiSchemaField.js +0 -261
- package/lib/components/fields/NullField.js +0 -55
- package/lib/components/fields/NumberField.js +0 -141
- package/lib/components/fields/ObjectField.js +0 -351
- package/lib/components/fields/SchemaField.js +0 -528
- package/lib/components/fields/StringField.js +0 -84
- package/lib/components/fields/TitleField.js +0 -30
- package/lib/components/fields/UnsupportedField.js +0 -35
- package/lib/components/fields/index.js +0 -26
- package/lib/components/widgets/AltDateTimeWidget.js +0 -36
- package/lib/components/widgets/AltDateWidget.js +0 -287
- package/lib/components/widgets/BaseInput.js +0 -140
- package/lib/components/widgets/CheckboxWidget.js +0 -73
- package/lib/components/widgets/CheckboxesWidget.js +0 -108
- package/lib/components/widgets/ColorWidget.js +0 -34
- package/lib/components/widgets/DateTimeWidget.js +0 -31
- package/lib/components/widgets/DateWidget.js +0 -28
- package/lib/components/widgets/EmailWidget.js +0 -23
- package/lib/components/widgets/FileWidget.js +0 -201
- package/lib/components/widgets/HiddenWidget.js +0 -25
- package/lib/components/widgets/PasswordWidget.js +0 -23
- package/lib/components/widgets/RadioWidget.js +0 -94
- package/lib/components/widgets/RangeWidget.js +0 -34
- package/lib/components/widgets/SelectWidget.js +0 -138
- package/lib/components/widgets/SubmitButton.js +0 -24
- package/lib/components/widgets/TextWidget.js +0 -20
- package/lib/components/widgets/TextareaWidget.js +0 -70
- package/lib/components/widgets/URLWidget.js +0 -23
- package/lib/components/widgets/UpDownWidget.js +0 -24
- package/lib/components/widgets/index.js +0 -43
- package/lib/defaultRegistry.js +0 -12
- package/lib/index.js +0 -16
- package/lib/types.js +0 -34
- package/lib/utils.js +0 -1309
- package/lib/validate.js +0 -338
- package/lib/withTheme.js +0 -39
|
@@ -0,0 +1,4054 @@
|
|
|
1
|
+
(function (global, factory) {
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('@rjsf/utils'), require('lodash-es/pick'), require('lodash-es/get'), require('lodash-es/isEmpty'), require('lodash-es/isObject'), require('lodash-es/set'), require('nanoid'), require('lodash-es/unset'), require('lodash-es/has'), require('lodash-es/omit')) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['exports', 'react', '@rjsf/utils', 'lodash-es/pick', 'lodash-es/get', 'lodash-es/isEmpty', 'lodash-es/isObject', 'lodash-es/set', 'nanoid', 'lodash-es/unset', 'lodash-es/has', 'lodash-es/omit'], factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["@rjsf/core"] = {}, global.React, global.utils, global._pick, global.get, global._isEmpty, global.isObject, global.set, global.nanoid, global.unset, global.has, global.omit));
|
|
5
|
+
})(this, (function (exports, React, utils, _pick, get, _isEmpty, isObject, set, nanoid, unset, has, omit) { 'use strict';
|
|
6
|
+
|
|
7
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
8
|
+
|
|
9
|
+
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
|
|
10
|
+
var _pick__default = /*#__PURE__*/_interopDefaultLegacy(_pick);
|
|
11
|
+
var get__default = /*#__PURE__*/_interopDefaultLegacy(get);
|
|
12
|
+
var _isEmpty__default = /*#__PURE__*/_interopDefaultLegacy(_isEmpty);
|
|
13
|
+
var isObject__default = /*#__PURE__*/_interopDefaultLegacy(isObject);
|
|
14
|
+
var set__default = /*#__PURE__*/_interopDefaultLegacy(set);
|
|
15
|
+
var unset__default = /*#__PURE__*/_interopDefaultLegacy(unset);
|
|
16
|
+
var has__default = /*#__PURE__*/_interopDefaultLegacy(has);
|
|
17
|
+
var omit__default = /*#__PURE__*/_interopDefaultLegacy(omit);
|
|
18
|
+
|
|
19
|
+
/** Used to generate a unique ID for an element in a row */
|
|
20
|
+
|
|
21
|
+
function generateRowId() {
|
|
22
|
+
return nanoid.nanoid();
|
|
23
|
+
}
|
|
24
|
+
/** Converts the `formData` into `KeyedFormDataType` data, using the `generateRowId()` function to create the key
|
|
25
|
+
*
|
|
26
|
+
* @param formData - The data for the form
|
|
27
|
+
* @returns - The `formData` converted into a `KeyedFormDataType` element
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
function generateKeyedFormData(formData) {
|
|
32
|
+
return !Array.isArray(formData) ? [] : formData.map(item => {
|
|
33
|
+
return {
|
|
34
|
+
key: generateRowId(),
|
|
35
|
+
item
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/** Converts `KeyedFormDataType` data into the inner `formData`
|
|
40
|
+
*
|
|
41
|
+
* @param keyedFormData - The `KeyedFormDataType` to be converted
|
|
42
|
+
* @returns - The inner `formData` item(s) in the `keyedFormData`
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
function keyedToPlainFormData(keyedFormData) {
|
|
47
|
+
if (Array.isArray(keyedFormData)) {
|
|
48
|
+
return keyedFormData.map(keyedItem => keyedItem.item);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
/** The `ArrayField` component is used to render a field in the schema that is of type `array`. It supports both normal
|
|
54
|
+
* and fixed array, allowing user to add and remove elements from the array data.
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class ArrayField extends React.Component {
|
|
59
|
+
/** Constructs an `ArrayField` from the `props`, generating the initial keyed data from the `formData`
|
|
60
|
+
*
|
|
61
|
+
* @param props - The `FieldProps` for this template
|
|
62
|
+
*/
|
|
63
|
+
constructor(props) {
|
|
64
|
+
super(props);
|
|
65
|
+
|
|
66
|
+
this._getNewFormDataRow = () => {
|
|
67
|
+
const {
|
|
68
|
+
schema,
|
|
69
|
+
registry
|
|
70
|
+
} = this.props;
|
|
71
|
+
const {
|
|
72
|
+
schemaUtils
|
|
73
|
+
} = registry;
|
|
74
|
+
let itemSchema = schema.items;
|
|
75
|
+
|
|
76
|
+
if (utils.isFixedItems(schema) && utils.allowAdditionalItems(schema)) {
|
|
77
|
+
itemSchema = schema.additionalItems;
|
|
78
|
+
} // Cast this as a T to work around schema utils being for T[] caused by the FieldProps<T[], F> call on the class
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
return schemaUtils.getDefaultFormState(itemSchema);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
this.onAddClick = event => {
|
|
85
|
+
if (event) {
|
|
86
|
+
event.preventDefault();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const {
|
|
90
|
+
onChange
|
|
91
|
+
} = this.props;
|
|
92
|
+
const {
|
|
93
|
+
keyedFormData
|
|
94
|
+
} = this.state;
|
|
95
|
+
const newKeyedFormDataRow = {
|
|
96
|
+
key: generateRowId(),
|
|
97
|
+
item: this._getNewFormDataRow()
|
|
98
|
+
};
|
|
99
|
+
const newKeyedFormData = [...keyedFormData, newKeyedFormDataRow];
|
|
100
|
+
this.setState({
|
|
101
|
+
keyedFormData: newKeyedFormData,
|
|
102
|
+
updatedKeyedFormData: true
|
|
103
|
+
}, () => onChange(keyedToPlainFormData(newKeyedFormData)));
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
this.onAddIndexClick = index => {
|
|
107
|
+
return event => {
|
|
108
|
+
if (event) {
|
|
109
|
+
event.preventDefault();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const {
|
|
113
|
+
onChange
|
|
114
|
+
} = this.props;
|
|
115
|
+
const {
|
|
116
|
+
keyedFormData
|
|
117
|
+
} = this.state;
|
|
118
|
+
const newKeyedFormDataRow = {
|
|
119
|
+
key: generateRowId(),
|
|
120
|
+
item: this._getNewFormDataRow()
|
|
121
|
+
};
|
|
122
|
+
const newKeyedFormData = [...keyedFormData];
|
|
123
|
+
newKeyedFormData.splice(index, 0, newKeyedFormDataRow);
|
|
124
|
+
this.setState({
|
|
125
|
+
keyedFormData: newKeyedFormData,
|
|
126
|
+
updatedKeyedFormData: true
|
|
127
|
+
}, () => onChange(keyedToPlainFormData(newKeyedFormData)));
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
this.onDropIndexClick = index => {
|
|
132
|
+
return event => {
|
|
133
|
+
if (event) {
|
|
134
|
+
event.preventDefault();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const {
|
|
138
|
+
onChange,
|
|
139
|
+
errorSchema
|
|
140
|
+
} = this.props;
|
|
141
|
+
const {
|
|
142
|
+
keyedFormData
|
|
143
|
+
} = this.state; // refs #195: revalidate to ensure properly reindexing errors
|
|
144
|
+
|
|
145
|
+
let newErrorSchema;
|
|
146
|
+
|
|
147
|
+
if (errorSchema) {
|
|
148
|
+
newErrorSchema = {};
|
|
149
|
+
|
|
150
|
+
for (const idx in errorSchema) {
|
|
151
|
+
const i = parseInt(idx);
|
|
152
|
+
|
|
153
|
+
if (i < index) {
|
|
154
|
+
set__default["default"](newErrorSchema, [i], errorSchema[idx]);
|
|
155
|
+
} else if (i > index) {
|
|
156
|
+
set__default["default"](newErrorSchema, [i - 1], errorSchema[idx]);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const newKeyedFormData = keyedFormData.filter((_, i) => i !== index);
|
|
162
|
+
this.setState({
|
|
163
|
+
keyedFormData: newKeyedFormData,
|
|
164
|
+
updatedKeyedFormData: true
|
|
165
|
+
}, () => onChange(keyedToPlainFormData(newKeyedFormData), newErrorSchema));
|
|
166
|
+
};
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
this.onReorderClick = (index, newIndex) => {
|
|
170
|
+
return event => {
|
|
171
|
+
if (event) {
|
|
172
|
+
event.preventDefault();
|
|
173
|
+
event.currentTarget.blur();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const {
|
|
177
|
+
onChange,
|
|
178
|
+
errorSchema
|
|
179
|
+
} = this.props;
|
|
180
|
+
let newErrorSchema;
|
|
181
|
+
|
|
182
|
+
if (this.props.errorSchema) {
|
|
183
|
+
newErrorSchema = {};
|
|
184
|
+
|
|
185
|
+
for (const idx in errorSchema) {
|
|
186
|
+
const i = parseInt(idx);
|
|
187
|
+
|
|
188
|
+
if (i == index) {
|
|
189
|
+
set__default["default"](newErrorSchema, [newIndex], errorSchema[index]);
|
|
190
|
+
} else if (i == newIndex) {
|
|
191
|
+
set__default["default"](newErrorSchema, [index], errorSchema[newIndex]);
|
|
192
|
+
} else {
|
|
193
|
+
set__default["default"](newErrorSchema, [idx], errorSchema[i]);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const {
|
|
199
|
+
keyedFormData
|
|
200
|
+
} = this.state;
|
|
201
|
+
|
|
202
|
+
function reOrderArray() {
|
|
203
|
+
// Copy item
|
|
204
|
+
const _newKeyedFormData = keyedFormData.slice(); // Moves item from index to newIndex
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
_newKeyedFormData.splice(index, 1);
|
|
208
|
+
|
|
209
|
+
_newKeyedFormData.splice(newIndex, 0, keyedFormData[index]);
|
|
210
|
+
|
|
211
|
+
return _newKeyedFormData;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const newKeyedFormData = reOrderArray();
|
|
215
|
+
this.setState({
|
|
216
|
+
keyedFormData: newKeyedFormData
|
|
217
|
+
}, () => onChange(keyedToPlainFormData(newKeyedFormData), newErrorSchema));
|
|
218
|
+
};
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
this.onChangeForIndex = index => {
|
|
222
|
+
return (value, newErrorSchema) => {
|
|
223
|
+
const {
|
|
224
|
+
formData,
|
|
225
|
+
onChange,
|
|
226
|
+
errorSchema
|
|
227
|
+
} = this.props;
|
|
228
|
+
const arrayData = Array.isArray(formData) ? formData : [];
|
|
229
|
+
const newFormData = arrayData.map((item, i) => {
|
|
230
|
+
// We need to treat undefined items as nulls to have validation.
|
|
231
|
+
// See https://github.com/tdegrunt/jsonschema/issues/206
|
|
232
|
+
const jsonValue = typeof value === "undefined" ? null : value;
|
|
233
|
+
return index === i ? jsonValue : item;
|
|
234
|
+
});
|
|
235
|
+
onChange(newFormData, errorSchema && errorSchema && { ...errorSchema,
|
|
236
|
+
[index]: newErrorSchema
|
|
237
|
+
});
|
|
238
|
+
};
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
this.onSelectChange = value => {
|
|
242
|
+
const {
|
|
243
|
+
onChange
|
|
244
|
+
} = this.props;
|
|
245
|
+
onChange(value);
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const {
|
|
249
|
+
formData: _formData = []
|
|
250
|
+
} = props;
|
|
251
|
+
|
|
252
|
+
const _keyedFormData = generateKeyedFormData(_formData);
|
|
253
|
+
|
|
254
|
+
this.state = {
|
|
255
|
+
keyedFormData: _keyedFormData,
|
|
256
|
+
updatedKeyedFormData: false
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
/** React lifecycle method that is called when the props are about to change allowing the state to be updated. It
|
|
260
|
+
* regenerates the keyed form data and returns it
|
|
261
|
+
*
|
|
262
|
+
* @param nextProps - The next set of props data
|
|
263
|
+
* @param prevState - The previous set of state data
|
|
264
|
+
*/
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
static getDerivedStateFromProps(nextProps, prevState) {
|
|
268
|
+
// Don't call getDerivedStateFromProps if keyed formdata was just updated.
|
|
269
|
+
if (prevState.updatedKeyedFormData) {
|
|
270
|
+
return {
|
|
271
|
+
updatedKeyedFormData: false
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const nextFormData = Array.isArray(nextProps.formData) ? nextProps.formData : [];
|
|
276
|
+
const previousKeyedFormData = prevState.keyedFormData || [];
|
|
277
|
+
const newKeyedFormData = nextFormData.length === previousKeyedFormData.length ? previousKeyedFormData.map((previousKeyedFormDatum, index) => {
|
|
278
|
+
return {
|
|
279
|
+
key: previousKeyedFormDatum.key,
|
|
280
|
+
item: nextFormData[index]
|
|
281
|
+
};
|
|
282
|
+
}) : generateKeyedFormData(nextFormData);
|
|
283
|
+
return {
|
|
284
|
+
keyedFormData: newKeyedFormData
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
/** Returns the appropriate title for an item by getting first the title from the schema.items, then falling back to
|
|
288
|
+
* the description from the schema.items, and finally the string "Item"
|
|
289
|
+
*/
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
get itemTitle() {
|
|
293
|
+
const {
|
|
294
|
+
schema
|
|
295
|
+
} = this.props;
|
|
296
|
+
return get__default["default"](schema, [utils.ITEMS_KEY, "title"], get__default["default"](schema, [utils.ITEMS_KEY, "description"], "Item"));
|
|
297
|
+
}
|
|
298
|
+
/** Determines whether the item described in the schema is always required, which is determined by whether any item
|
|
299
|
+
* may be null.
|
|
300
|
+
*
|
|
301
|
+
* @param itemSchema - The schema for the item
|
|
302
|
+
* @return - True if the item schema type does not contain the "null" type
|
|
303
|
+
*/
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
isItemRequired(itemSchema) {
|
|
307
|
+
if (Array.isArray(itemSchema.type)) {
|
|
308
|
+
// While we don't yet support composite/nullable jsonschema types, it's
|
|
309
|
+
// future-proof to check for requirement against these.
|
|
310
|
+
return !itemSchema.type.includes("null");
|
|
311
|
+
} // All non-null array item types are inherently required by design
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
return itemSchema.type !== "null";
|
|
315
|
+
}
|
|
316
|
+
/** Determines whether more items can be added to the array. If the uiSchema indicates the array doesn't allow adding
|
|
317
|
+
* then false is returned. Otherwise, if the schema indicates that there are a maximum number of items and the
|
|
318
|
+
* `formData` matches that value, then false is returned, otherwise true is returned.
|
|
319
|
+
*
|
|
320
|
+
* @param formItems - The list of items in the form
|
|
321
|
+
* @returns - True if the item is addable otherwise false
|
|
322
|
+
*/
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
canAddItem(formItems) {
|
|
326
|
+
const {
|
|
327
|
+
schema,
|
|
328
|
+
uiSchema
|
|
329
|
+
} = this.props;
|
|
330
|
+
let {
|
|
331
|
+
addable
|
|
332
|
+
} = utils.getUiOptions(uiSchema);
|
|
333
|
+
|
|
334
|
+
if (addable !== false) {
|
|
335
|
+
// if ui:options.addable was not explicitly set to false, we can add
|
|
336
|
+
// another item if we have not exceeded maxItems yet
|
|
337
|
+
if (schema.maxItems !== undefined) {
|
|
338
|
+
addable = formItems.length < schema.maxItems;
|
|
339
|
+
} else {
|
|
340
|
+
addable = true;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return addable;
|
|
345
|
+
}
|
|
346
|
+
/** Returns the default form information for an item based on the schema for that item. Deals with the possibility
|
|
347
|
+
* that the schema is fixed and allows additional items.
|
|
348
|
+
*/
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
/** Renders the `ArrayField` depending on the specific needs of the schema and uischema elements
|
|
352
|
+
*/
|
|
353
|
+
render() {
|
|
354
|
+
const {
|
|
355
|
+
schema,
|
|
356
|
+
uiSchema,
|
|
357
|
+
idSchema,
|
|
358
|
+
registry
|
|
359
|
+
} = this.props;
|
|
360
|
+
const {
|
|
361
|
+
schemaUtils
|
|
362
|
+
} = registry;
|
|
363
|
+
|
|
364
|
+
if (!(utils.ITEMS_KEY in schema)) {
|
|
365
|
+
const uiOptions = utils.getUiOptions(uiSchema);
|
|
366
|
+
const UnsupportedFieldTemplate = utils.getTemplate("UnsupportedFieldTemplate", registry, uiOptions);
|
|
367
|
+
return /*#__PURE__*/React__default["default"].createElement(UnsupportedFieldTemplate, {
|
|
368
|
+
schema: schema,
|
|
369
|
+
idSchema: idSchema,
|
|
370
|
+
reason: "Missing items definition",
|
|
371
|
+
registry: registry
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (schemaUtils.isMultiSelect(schema)) {
|
|
376
|
+
// If array has enum or uniqueItems set to true, call renderMultiSelect() to render the default multiselect widget or a custom widget, if specified.
|
|
377
|
+
return this.renderMultiSelect();
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (utils.isCustomWidget(uiSchema)) {
|
|
381
|
+
return this.renderCustomWidget();
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (utils.isFixedItems(schema)) {
|
|
385
|
+
return this.renderFixedArray();
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (schemaUtils.isFilesArray(schema, uiSchema)) {
|
|
389
|
+
return this.renderFiles();
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return this.renderNormalArray();
|
|
393
|
+
}
|
|
394
|
+
/** Renders a normal array without any limitations of length
|
|
395
|
+
*/
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
renderNormalArray() {
|
|
399
|
+
const {
|
|
400
|
+
schema,
|
|
401
|
+
uiSchema = {},
|
|
402
|
+
errorSchema,
|
|
403
|
+
idSchema,
|
|
404
|
+
name,
|
|
405
|
+
disabled = false,
|
|
406
|
+
readonly = false,
|
|
407
|
+
autofocus = false,
|
|
408
|
+
required = false,
|
|
409
|
+
registry,
|
|
410
|
+
onBlur,
|
|
411
|
+
onFocus,
|
|
412
|
+
idPrefix,
|
|
413
|
+
idSeparator = "_",
|
|
414
|
+
rawErrors
|
|
415
|
+
} = this.props;
|
|
416
|
+
const {
|
|
417
|
+
keyedFormData
|
|
418
|
+
} = this.state;
|
|
419
|
+
const title = schema.title === undefined ? name : schema.title;
|
|
420
|
+
const {
|
|
421
|
+
schemaUtils,
|
|
422
|
+
formContext
|
|
423
|
+
} = registry;
|
|
424
|
+
const uiOptions = utils.getUiOptions(uiSchema);
|
|
425
|
+
|
|
426
|
+
const _schemaItems = isObject__default["default"](schema.items) ? schema.items : {};
|
|
427
|
+
|
|
428
|
+
const itemsSchema = schemaUtils.retrieveSchema(_schemaItems);
|
|
429
|
+
const formData = keyedToPlainFormData(this.state.keyedFormData);
|
|
430
|
+
const arrayProps = {
|
|
431
|
+
canAdd: this.canAddItem(formData),
|
|
432
|
+
items: keyedFormData.map((keyedItem, index) => {
|
|
433
|
+
const {
|
|
434
|
+
key,
|
|
435
|
+
item
|
|
436
|
+
} = keyedItem; // While we are actually dealing with a single item of type T, the types require a T[], so cast
|
|
437
|
+
|
|
438
|
+
const itemCast = item;
|
|
439
|
+
const itemSchema = schemaUtils.retrieveSchema(_schemaItems, itemCast);
|
|
440
|
+
const itemErrorSchema = errorSchema ? errorSchema[index] : undefined;
|
|
441
|
+
const itemIdPrefix = idSchema.$id + idSeparator + index;
|
|
442
|
+
const itemIdSchema = schemaUtils.toIdSchema(itemSchema, itemIdPrefix, itemCast, idPrefix, idSeparator);
|
|
443
|
+
return this.renderArrayFieldItem({
|
|
444
|
+
key,
|
|
445
|
+
index,
|
|
446
|
+
name: name && name + "-" + index,
|
|
447
|
+
canMoveUp: index > 0,
|
|
448
|
+
canMoveDown: index < formData.length - 1,
|
|
449
|
+
itemSchema: itemSchema,
|
|
450
|
+
itemIdSchema,
|
|
451
|
+
itemErrorSchema,
|
|
452
|
+
itemData: itemCast,
|
|
453
|
+
itemUiSchema: uiSchema.items,
|
|
454
|
+
autofocus: autofocus && index === 0,
|
|
455
|
+
onBlur,
|
|
456
|
+
onFocus,
|
|
457
|
+
rawErrors
|
|
458
|
+
});
|
|
459
|
+
}),
|
|
460
|
+
className: "field field-array field-array-of-" + itemsSchema.type,
|
|
461
|
+
disabled,
|
|
462
|
+
idSchema,
|
|
463
|
+
uiSchema,
|
|
464
|
+
onAddClick: this.onAddClick,
|
|
465
|
+
readonly,
|
|
466
|
+
required,
|
|
467
|
+
schema,
|
|
468
|
+
title,
|
|
469
|
+
formContext,
|
|
470
|
+
formData,
|
|
471
|
+
rawErrors,
|
|
472
|
+
registry
|
|
473
|
+
};
|
|
474
|
+
const Template = utils.getTemplate("ArrayFieldTemplate", registry, uiOptions);
|
|
475
|
+
return /*#__PURE__*/React__default["default"].createElement(Template, { ...arrayProps
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
/** Renders an array using the custom widget provided by the user in the `uiSchema`
|
|
479
|
+
*/
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
renderCustomWidget() {
|
|
483
|
+
const {
|
|
484
|
+
schema,
|
|
485
|
+
idSchema,
|
|
486
|
+
uiSchema,
|
|
487
|
+
disabled = false,
|
|
488
|
+
readonly = false,
|
|
489
|
+
autofocus = false,
|
|
490
|
+
required = false,
|
|
491
|
+
hideError,
|
|
492
|
+
placeholder,
|
|
493
|
+
onBlur,
|
|
494
|
+
onFocus,
|
|
495
|
+
formData: items = [],
|
|
496
|
+
registry,
|
|
497
|
+
rawErrors,
|
|
498
|
+
name
|
|
499
|
+
} = this.props;
|
|
500
|
+
const {
|
|
501
|
+
widgets,
|
|
502
|
+
formContext
|
|
503
|
+
} = registry;
|
|
504
|
+
const title = schema.title || name;
|
|
505
|
+
const {
|
|
506
|
+
widget,
|
|
507
|
+
...options
|
|
508
|
+
} = utils.getUiOptions(uiSchema);
|
|
509
|
+
const Widget = utils.getWidget(schema, widget, widgets);
|
|
510
|
+
return /*#__PURE__*/React__default["default"].createElement(Widget, {
|
|
511
|
+
id: idSchema && idSchema.$id,
|
|
512
|
+
multiple: true,
|
|
513
|
+
onChange: this.onSelectChange,
|
|
514
|
+
onBlur: onBlur,
|
|
515
|
+
onFocus: onFocus,
|
|
516
|
+
options: options,
|
|
517
|
+
schema: schema,
|
|
518
|
+
uiSchema: uiSchema,
|
|
519
|
+
registry: registry,
|
|
520
|
+
value: items,
|
|
521
|
+
disabled: disabled,
|
|
522
|
+
readonly: readonly,
|
|
523
|
+
hideError: hideError,
|
|
524
|
+
required: required,
|
|
525
|
+
label: title,
|
|
526
|
+
placeholder: placeholder,
|
|
527
|
+
formContext: formContext,
|
|
528
|
+
autofocus: autofocus,
|
|
529
|
+
rawErrors: rawErrors
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
/** Renders an array as a set of checkboxes
|
|
533
|
+
*/
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
renderMultiSelect() {
|
|
537
|
+
const {
|
|
538
|
+
schema,
|
|
539
|
+
idSchema,
|
|
540
|
+
uiSchema,
|
|
541
|
+
formData: items = [],
|
|
542
|
+
disabled = false,
|
|
543
|
+
readonly = false,
|
|
544
|
+
autofocus = false,
|
|
545
|
+
required = false,
|
|
546
|
+
placeholder,
|
|
547
|
+
onBlur,
|
|
548
|
+
onFocus,
|
|
549
|
+
registry,
|
|
550
|
+
rawErrors,
|
|
551
|
+
name
|
|
552
|
+
} = this.props;
|
|
553
|
+
const {
|
|
554
|
+
widgets,
|
|
555
|
+
schemaUtils,
|
|
556
|
+
formContext
|
|
557
|
+
} = registry;
|
|
558
|
+
const itemsSchema = schemaUtils.retrieveSchema(schema.items, items);
|
|
559
|
+
const title = schema.title || name;
|
|
560
|
+
const enumOptions = utils.optionsList(itemsSchema);
|
|
561
|
+
const {
|
|
562
|
+
widget = "select",
|
|
563
|
+
...options
|
|
564
|
+
} = utils.getUiOptions(uiSchema);
|
|
565
|
+
const Widget = utils.getWidget(schema, widget, widgets);
|
|
566
|
+
return /*#__PURE__*/React__default["default"].createElement(Widget, {
|
|
567
|
+
id: idSchema && idSchema.$id,
|
|
568
|
+
multiple: true,
|
|
569
|
+
onChange: this.onSelectChange,
|
|
570
|
+
onBlur: onBlur,
|
|
571
|
+
onFocus: onFocus,
|
|
572
|
+
options: { ...options,
|
|
573
|
+
enumOptions
|
|
574
|
+
},
|
|
575
|
+
schema: schema,
|
|
576
|
+
uiSchema: uiSchema,
|
|
577
|
+
registry: registry,
|
|
578
|
+
value: items,
|
|
579
|
+
disabled: disabled,
|
|
580
|
+
readonly: readonly,
|
|
581
|
+
required: required,
|
|
582
|
+
label: title,
|
|
583
|
+
placeholder: placeholder,
|
|
584
|
+
formContext: formContext,
|
|
585
|
+
autofocus: autofocus,
|
|
586
|
+
rawErrors: rawErrors
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
/** Renders an array of files using the `FileWidget`
|
|
590
|
+
*/
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
renderFiles() {
|
|
594
|
+
const {
|
|
595
|
+
schema,
|
|
596
|
+
uiSchema,
|
|
597
|
+
idSchema,
|
|
598
|
+
name,
|
|
599
|
+
disabled = false,
|
|
600
|
+
readonly = false,
|
|
601
|
+
autofocus = false,
|
|
602
|
+
required = false,
|
|
603
|
+
onBlur,
|
|
604
|
+
onFocus,
|
|
605
|
+
registry,
|
|
606
|
+
formData: items = [],
|
|
607
|
+
rawErrors
|
|
608
|
+
} = this.props;
|
|
609
|
+
const title = schema.title || name;
|
|
610
|
+
const {
|
|
611
|
+
widgets,
|
|
612
|
+
formContext
|
|
613
|
+
} = registry;
|
|
614
|
+
const {
|
|
615
|
+
widget = "files",
|
|
616
|
+
...options
|
|
617
|
+
} = utils.getUiOptions(uiSchema);
|
|
618
|
+
const Widget = utils.getWidget(schema, widget, widgets);
|
|
619
|
+
return /*#__PURE__*/React__default["default"].createElement(Widget, {
|
|
620
|
+
options: options,
|
|
621
|
+
id: idSchema && idSchema.$id,
|
|
622
|
+
multiple: true,
|
|
623
|
+
onChange: this.onSelectChange,
|
|
624
|
+
onBlur: onBlur,
|
|
625
|
+
onFocus: onFocus,
|
|
626
|
+
schema: schema,
|
|
627
|
+
uiSchema: uiSchema,
|
|
628
|
+
title: title,
|
|
629
|
+
value: items,
|
|
630
|
+
disabled: disabled,
|
|
631
|
+
readonly: readonly,
|
|
632
|
+
required: required,
|
|
633
|
+
registry: registry,
|
|
634
|
+
formContext: formContext,
|
|
635
|
+
autofocus: autofocus,
|
|
636
|
+
rawErrors: rawErrors,
|
|
637
|
+
label: ""
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
/** Renders an array that has a maximum limit of items
|
|
641
|
+
*/
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
renderFixedArray() {
|
|
645
|
+
const {
|
|
646
|
+
schema,
|
|
647
|
+
uiSchema = {},
|
|
648
|
+
formData = [],
|
|
649
|
+
errorSchema,
|
|
650
|
+
idPrefix,
|
|
651
|
+
idSeparator = "_",
|
|
652
|
+
idSchema,
|
|
653
|
+
name,
|
|
654
|
+
disabled = false,
|
|
655
|
+
readonly = false,
|
|
656
|
+
autofocus = false,
|
|
657
|
+
required = false,
|
|
658
|
+
registry,
|
|
659
|
+
onBlur,
|
|
660
|
+
onFocus,
|
|
661
|
+
rawErrors
|
|
662
|
+
} = this.props;
|
|
663
|
+
const {
|
|
664
|
+
keyedFormData
|
|
665
|
+
} = this.state;
|
|
666
|
+
let {
|
|
667
|
+
formData: items = []
|
|
668
|
+
} = this.props;
|
|
669
|
+
const title = schema.title || name;
|
|
670
|
+
const uiOptions = utils.getUiOptions(uiSchema);
|
|
671
|
+
const {
|
|
672
|
+
schemaUtils,
|
|
673
|
+
formContext
|
|
674
|
+
} = registry;
|
|
675
|
+
|
|
676
|
+
const _schemaItems = isObject__default["default"](schema.items) ? schema.items : [];
|
|
677
|
+
|
|
678
|
+
const itemSchemas = _schemaItems.map((item, index) => schemaUtils.retrieveSchema(item, formData[index]));
|
|
679
|
+
|
|
680
|
+
const additionalSchema = isObject__default["default"](schema.additionalItems) ? schemaUtils.retrieveSchema(schema.additionalItems, formData) : null;
|
|
681
|
+
|
|
682
|
+
if (!items || items.length < itemSchemas.length) {
|
|
683
|
+
// to make sure at least all fixed items are generated
|
|
684
|
+
items = items || [];
|
|
685
|
+
items = items.concat(new Array(itemSchemas.length - items.length));
|
|
686
|
+
} // These are the props passed into the render function
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
const arrayProps = {
|
|
690
|
+
canAdd: this.canAddItem(items) && !!additionalSchema,
|
|
691
|
+
className: "field field-array field-array-fixed-items",
|
|
692
|
+
disabled,
|
|
693
|
+
idSchema,
|
|
694
|
+
formData,
|
|
695
|
+
items: keyedFormData.map((keyedItem, index) => {
|
|
696
|
+
const {
|
|
697
|
+
key,
|
|
698
|
+
item
|
|
699
|
+
} = keyedItem; // While we are actually dealing with a single item of type T, the types require a T[], so cast
|
|
700
|
+
|
|
701
|
+
const itemCast = item;
|
|
702
|
+
const additional = index >= itemSchemas.length;
|
|
703
|
+
const itemSchema = additional && isObject__default["default"](schema.additionalItems) ? schemaUtils.retrieveSchema(schema.additionalItems, itemCast) : itemSchemas[index];
|
|
704
|
+
const itemIdPrefix = idSchema.$id + idSeparator + index;
|
|
705
|
+
const itemIdSchema = schemaUtils.toIdSchema(itemSchema, itemIdPrefix, itemCast, idPrefix, idSeparator);
|
|
706
|
+
const itemUiSchema = additional ? uiSchema.additionalItems || {} : Array.isArray(uiSchema.items) ? uiSchema.items[index] : uiSchema.items || {};
|
|
707
|
+
const itemErrorSchema = errorSchema ? errorSchema[index] : undefined;
|
|
708
|
+
return this.renderArrayFieldItem({
|
|
709
|
+
key,
|
|
710
|
+
index,
|
|
711
|
+
name: name && name + "-" + index,
|
|
712
|
+
canRemove: additional,
|
|
713
|
+
canMoveUp: index >= itemSchemas.length + 1,
|
|
714
|
+
canMoveDown: additional && index < items.length - 1,
|
|
715
|
+
itemSchema,
|
|
716
|
+
itemData: itemCast,
|
|
717
|
+
itemUiSchema,
|
|
718
|
+
itemIdSchema,
|
|
719
|
+
itemErrorSchema,
|
|
720
|
+
autofocus: autofocus && index === 0,
|
|
721
|
+
onBlur,
|
|
722
|
+
onFocus,
|
|
723
|
+
rawErrors
|
|
724
|
+
});
|
|
725
|
+
}),
|
|
726
|
+
onAddClick: this.onAddClick,
|
|
727
|
+
readonly,
|
|
728
|
+
required,
|
|
729
|
+
registry,
|
|
730
|
+
schema,
|
|
731
|
+
uiSchema,
|
|
732
|
+
title,
|
|
733
|
+
formContext,
|
|
734
|
+
rawErrors
|
|
735
|
+
};
|
|
736
|
+
const Template = utils.getTemplate("ArrayFieldTemplate", registry, uiOptions);
|
|
737
|
+
return /*#__PURE__*/React__default["default"].createElement(Template, { ...arrayProps
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
/** Renders the individual array item using a `SchemaField` along with the additional properties required to be send
|
|
741
|
+
* back to the `ArrayFieldItemTemplate`.
|
|
742
|
+
*
|
|
743
|
+
* @param props - The props for the individual array item to be rendered
|
|
744
|
+
*/
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
renderArrayFieldItem(props) {
|
|
748
|
+
const {
|
|
749
|
+
key,
|
|
750
|
+
index,
|
|
751
|
+
name,
|
|
752
|
+
canRemove = true,
|
|
753
|
+
canMoveUp = true,
|
|
754
|
+
canMoveDown = true,
|
|
755
|
+
itemSchema,
|
|
756
|
+
itemData,
|
|
757
|
+
itemUiSchema,
|
|
758
|
+
itemIdSchema,
|
|
759
|
+
itemErrorSchema,
|
|
760
|
+
autofocus,
|
|
761
|
+
onBlur,
|
|
762
|
+
onFocus,
|
|
763
|
+
rawErrors
|
|
764
|
+
} = props;
|
|
765
|
+
const {
|
|
766
|
+
disabled,
|
|
767
|
+
hideError,
|
|
768
|
+
idPrefix,
|
|
769
|
+
idSeparator,
|
|
770
|
+
readonly,
|
|
771
|
+
uiSchema,
|
|
772
|
+
registry,
|
|
773
|
+
formContext
|
|
774
|
+
} = this.props;
|
|
775
|
+
const {
|
|
776
|
+
fields: {
|
|
777
|
+
SchemaField
|
|
778
|
+
}
|
|
779
|
+
} = registry;
|
|
780
|
+
const {
|
|
781
|
+
orderable = true,
|
|
782
|
+
removable = true
|
|
783
|
+
} = utils.getUiOptions(uiSchema);
|
|
784
|
+
const has = {
|
|
785
|
+
moveUp: orderable && canMoveUp,
|
|
786
|
+
moveDown: orderable && canMoveDown,
|
|
787
|
+
remove: removable && canRemove,
|
|
788
|
+
toolbar: false
|
|
789
|
+
};
|
|
790
|
+
has.toolbar = Object.keys(has).some(key => has[key]);
|
|
791
|
+
return {
|
|
792
|
+
children: /*#__PURE__*/React__default["default"].createElement(SchemaField, {
|
|
793
|
+
name: name,
|
|
794
|
+
index: index,
|
|
795
|
+
schema: itemSchema,
|
|
796
|
+
uiSchema: itemUiSchema,
|
|
797
|
+
formData: itemData,
|
|
798
|
+
formContext: formContext,
|
|
799
|
+
errorSchema: itemErrorSchema,
|
|
800
|
+
idPrefix: idPrefix,
|
|
801
|
+
idSeparator: idSeparator,
|
|
802
|
+
idSchema: itemIdSchema,
|
|
803
|
+
required: this.isItemRequired(itemSchema),
|
|
804
|
+
onChange: this.onChangeForIndex(index),
|
|
805
|
+
onBlur: onBlur,
|
|
806
|
+
onFocus: onFocus,
|
|
807
|
+
registry: registry,
|
|
808
|
+
disabled: disabled,
|
|
809
|
+
readonly: readonly,
|
|
810
|
+
hideError: hideError,
|
|
811
|
+
autofocus: autofocus,
|
|
812
|
+
rawErrors: rawErrors
|
|
813
|
+
}),
|
|
814
|
+
className: "array-item",
|
|
815
|
+
disabled,
|
|
816
|
+
hasToolbar: has.toolbar,
|
|
817
|
+
hasMoveUp: has.moveUp,
|
|
818
|
+
hasMoveDown: has.moveDown,
|
|
819
|
+
hasRemove: has.remove,
|
|
820
|
+
index,
|
|
821
|
+
key,
|
|
822
|
+
onAddIndexClick: this.onAddIndexClick,
|
|
823
|
+
onDropIndexClick: this.onDropIndexClick,
|
|
824
|
+
onReorderClick: this.onReorderClick,
|
|
825
|
+
readonly,
|
|
826
|
+
registry
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/** The `BooleanField` component is used to render a field in the schema is boolean. It constructs `enumOptions` for the
|
|
833
|
+
* two boolean values based on the various alternatives in the schema.
|
|
834
|
+
*
|
|
835
|
+
* @param props - The `FieldProps` for this template
|
|
836
|
+
*/
|
|
837
|
+
|
|
838
|
+
function BooleanField(props) {
|
|
839
|
+
const {
|
|
840
|
+
schema,
|
|
841
|
+
name,
|
|
842
|
+
uiSchema,
|
|
843
|
+
idSchema,
|
|
844
|
+
formData,
|
|
845
|
+
registry,
|
|
846
|
+
required,
|
|
847
|
+
disabled,
|
|
848
|
+
readonly,
|
|
849
|
+
autofocus,
|
|
850
|
+
onChange,
|
|
851
|
+
onFocus,
|
|
852
|
+
onBlur,
|
|
853
|
+
rawErrors
|
|
854
|
+
} = props;
|
|
855
|
+
const {
|
|
856
|
+
title
|
|
857
|
+
} = schema;
|
|
858
|
+
const {
|
|
859
|
+
widgets,
|
|
860
|
+
formContext
|
|
861
|
+
} = registry;
|
|
862
|
+
const {
|
|
863
|
+
widget = "checkbox",
|
|
864
|
+
...options
|
|
865
|
+
} = utils.getUiOptions(uiSchema);
|
|
866
|
+
const Widget = utils.getWidget(schema, widget, widgets);
|
|
867
|
+
let enumOptions;
|
|
868
|
+
|
|
869
|
+
if (Array.isArray(schema.oneOf)) {
|
|
870
|
+
enumOptions = utils.optionsList({
|
|
871
|
+
oneOf: schema.oneOf.map(option => {
|
|
872
|
+
if (isObject__default["default"](option)) {
|
|
873
|
+
return { ...option,
|
|
874
|
+
title: option.title || (option.const === true ? "Yes" : "No")
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
return undefined;
|
|
879
|
+
}).filter(o => o) // cast away the error that typescript can't grok is fixed
|
|
880
|
+
|
|
881
|
+
});
|
|
882
|
+
} else {
|
|
883
|
+
var _schema$enum;
|
|
884
|
+
|
|
885
|
+
// We deprecated enumNames in v5. It's intentionally omitted from RSJFSchema type, so we need to cast here.
|
|
886
|
+
const schemaWithEnumNames = schema;
|
|
887
|
+
schema.enum = (_schema$enum = schema.enum) != null ? _schema$enum : [true, false];
|
|
888
|
+
|
|
889
|
+
if (!schemaWithEnumNames.enumNames && schema.enum && schema.enum.length === 2 && schema.enum.every(v => typeof v === "boolean")) {
|
|
890
|
+
enumOptions = [{
|
|
891
|
+
value: schema.enum[0],
|
|
892
|
+
label: schema.enum[0] ? "Yes" : "No"
|
|
893
|
+
}, {
|
|
894
|
+
value: schema.enum[1],
|
|
895
|
+
label: schema.enum[1] ? "Yes" : "No"
|
|
896
|
+
}];
|
|
897
|
+
} else {
|
|
898
|
+
enumOptions = utils.optionsList({
|
|
899
|
+
enum: schema.enum,
|
|
900
|
+
// NOTE: enumNames is deprecated, but still supported for now.
|
|
901
|
+
enumNames: schemaWithEnumNames.enumNames
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
return /*#__PURE__*/React__default["default"].createElement(Widget, {
|
|
907
|
+
options: { ...options,
|
|
908
|
+
enumOptions
|
|
909
|
+
},
|
|
910
|
+
schema: schema,
|
|
911
|
+
uiSchema: uiSchema,
|
|
912
|
+
id: idSchema && idSchema.$id,
|
|
913
|
+
onChange: onChange,
|
|
914
|
+
onFocus: onFocus,
|
|
915
|
+
onBlur: onBlur,
|
|
916
|
+
label: title === undefined ? name : title,
|
|
917
|
+
value: formData,
|
|
918
|
+
required: required,
|
|
919
|
+
disabled: disabled,
|
|
920
|
+
readonly: readonly,
|
|
921
|
+
registry: registry,
|
|
922
|
+
formContext: formContext,
|
|
923
|
+
autofocus: autofocus,
|
|
924
|
+
rawErrors: rawErrors
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/** The `AnyOfField` component is used to render a field in the schema that is an `anyOf`, `allOf` or `oneOf`. It tracks
|
|
929
|
+
* the currently selected option and cleans up any irrelevant data in `formData`.
|
|
930
|
+
*
|
|
931
|
+
* @param props - The `FieldProps` for this template
|
|
932
|
+
*/
|
|
933
|
+
|
|
934
|
+
class AnyOfField extends React.Component {
|
|
935
|
+
/** Constructs an `AnyOfField` with the given `props` to initialize the initially selected option in state
|
|
936
|
+
*
|
|
937
|
+
* @param props - The `FieldProps` for this template
|
|
938
|
+
*/
|
|
939
|
+
constructor(props) {
|
|
940
|
+
super(props);
|
|
941
|
+
|
|
942
|
+
this.onOptionChange = option => {
|
|
943
|
+
const selectedOption = parseInt(option, 10);
|
|
944
|
+
const {
|
|
945
|
+
formData,
|
|
946
|
+
onChange,
|
|
947
|
+
options,
|
|
948
|
+
registry
|
|
949
|
+
} = this.props;
|
|
950
|
+
const {
|
|
951
|
+
schemaUtils
|
|
952
|
+
} = registry;
|
|
953
|
+
const newOption = schemaUtils.retrieveSchema(options[selectedOption], formData); // If the new option is of type object and the current data is an object,
|
|
954
|
+
// discard properties added using the old option.
|
|
955
|
+
|
|
956
|
+
let newFormData = undefined;
|
|
957
|
+
|
|
958
|
+
if (utils.guessType(formData) === "object" && (newOption.type === "object" || newOption.properties)) {
|
|
959
|
+
newFormData = Object.assign({}, formData);
|
|
960
|
+
const optionsToDiscard = options.slice();
|
|
961
|
+
optionsToDiscard.splice(selectedOption, 1); // Discard any data added using other options
|
|
962
|
+
|
|
963
|
+
for (const option of optionsToDiscard) {
|
|
964
|
+
if (option.properties) {
|
|
965
|
+
for (const key in option.properties) {
|
|
966
|
+
if (key in newFormData) {
|
|
967
|
+
unset__default["default"](newFormData, key);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
} // Call getDefaultFormState to make sure defaults are populated on change.
|
|
973
|
+
|
|
974
|
+
|
|
975
|
+
onChange(schemaUtils.getDefaultFormState(options[selectedOption], newFormData));
|
|
976
|
+
this.setState({
|
|
977
|
+
selectedOption: parseInt(option, 10)
|
|
978
|
+
});
|
|
979
|
+
};
|
|
980
|
+
|
|
981
|
+
const {
|
|
982
|
+
formData: _formData,
|
|
983
|
+
options: _options
|
|
984
|
+
} = this.props;
|
|
985
|
+
this.state = {
|
|
986
|
+
selectedOption: this.getMatchingOption(0, _formData, _options)
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
/** React lifecycle methos that is called when the props and/or state for this component is updated. It recomputes the
|
|
990
|
+
* currently selected option based on the overall `formData`
|
|
991
|
+
*
|
|
992
|
+
* @param prevProps - The previous `FieldProps` for this template
|
|
993
|
+
* @param prevState - The previous `AnyOfFieldState` for this template
|
|
994
|
+
*/
|
|
995
|
+
|
|
996
|
+
|
|
997
|
+
componentDidUpdate(prevProps, prevState) {
|
|
998
|
+
const {
|
|
999
|
+
formData,
|
|
1000
|
+
options,
|
|
1001
|
+
idSchema
|
|
1002
|
+
} = this.props;
|
|
1003
|
+
const {
|
|
1004
|
+
selectedOption
|
|
1005
|
+
} = this.state;
|
|
1006
|
+
|
|
1007
|
+
if (!utils.deepEquals(formData, prevProps.formData) && idSchema.$id === prevProps.idSchema.$id) {
|
|
1008
|
+
const matchingOption = this.getMatchingOption(selectedOption, formData, options);
|
|
1009
|
+
|
|
1010
|
+
if (!prevState || matchingOption === selectedOption) {
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
this.setState({
|
|
1015
|
+
selectedOption: matchingOption
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
/** Determines the best matching option for the given `formData` and `options`.
|
|
1020
|
+
*
|
|
1021
|
+
* @param formData - The new formData
|
|
1022
|
+
* @param options - The list of options to choose from
|
|
1023
|
+
* @return - The index of the `option` that best matches the `formData`
|
|
1024
|
+
*/
|
|
1025
|
+
|
|
1026
|
+
|
|
1027
|
+
getMatchingOption(selectedOption, formData, options) {
|
|
1028
|
+
const {
|
|
1029
|
+
schemaUtils
|
|
1030
|
+
} = this.props.registry;
|
|
1031
|
+
const option = schemaUtils.getMatchingOption(formData, options);
|
|
1032
|
+
|
|
1033
|
+
if (option !== 0) {
|
|
1034
|
+
return option;
|
|
1035
|
+
} // If the form data matches none of the options, use the currently selected
|
|
1036
|
+
// option, assuming it's available; otherwise use the first option
|
|
1037
|
+
|
|
1038
|
+
|
|
1039
|
+
return selectedOption || 0;
|
|
1040
|
+
}
|
|
1041
|
+
/** Callback handler to remember what the currently selected option is. In addition to that the `formData` is updated
|
|
1042
|
+
* to remove properties that are not part of the newly selected option schema, and then the updated data is passed to
|
|
1043
|
+
* the `onChange` handler.
|
|
1044
|
+
*
|
|
1045
|
+
* @param option -
|
|
1046
|
+
*/
|
|
1047
|
+
|
|
1048
|
+
|
|
1049
|
+
/** Renders the `AnyOfField` selector along with a `SchemaField` for the value of the `formData`
|
|
1050
|
+
*/
|
|
1051
|
+
render() {
|
|
1052
|
+
const {
|
|
1053
|
+
name,
|
|
1054
|
+
baseType,
|
|
1055
|
+
disabled = false,
|
|
1056
|
+
readonly = false,
|
|
1057
|
+
hideError = false,
|
|
1058
|
+
errorSchema = {},
|
|
1059
|
+
formData,
|
|
1060
|
+
formContext,
|
|
1061
|
+
idPrefix,
|
|
1062
|
+
idSeparator,
|
|
1063
|
+
idSchema,
|
|
1064
|
+
onBlur,
|
|
1065
|
+
onChange,
|
|
1066
|
+
onFocus,
|
|
1067
|
+
options,
|
|
1068
|
+
registry,
|
|
1069
|
+
uiSchema,
|
|
1070
|
+
schema
|
|
1071
|
+
} = this.props;
|
|
1072
|
+
const {
|
|
1073
|
+
widgets,
|
|
1074
|
+
fields
|
|
1075
|
+
} = registry;
|
|
1076
|
+
const {
|
|
1077
|
+
SchemaField: _SchemaField
|
|
1078
|
+
} = fields;
|
|
1079
|
+
const {
|
|
1080
|
+
selectedOption
|
|
1081
|
+
} = this.state;
|
|
1082
|
+
const {
|
|
1083
|
+
widget = "select",
|
|
1084
|
+
...uiOptions
|
|
1085
|
+
} = utils.getUiOptions(uiSchema);
|
|
1086
|
+
const Widget = utils.getWidget({
|
|
1087
|
+
type: "number"
|
|
1088
|
+
}, widget, widgets);
|
|
1089
|
+
const option = options[selectedOption] || null;
|
|
1090
|
+
let optionSchema;
|
|
1091
|
+
|
|
1092
|
+
if (option) {
|
|
1093
|
+
// If the subschema doesn't declare a type, infer the type from the
|
|
1094
|
+
// parent schema
|
|
1095
|
+
optionSchema = option.type ? option : Object.assign({}, option, {
|
|
1096
|
+
type: baseType
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
const enumOptions = options.map((option, index) => ({
|
|
1101
|
+
label: option.title || "Option " + (index + 1),
|
|
1102
|
+
value: index
|
|
1103
|
+
}));
|
|
1104
|
+
return /*#__PURE__*/React__default["default"].createElement("div", {
|
|
1105
|
+
className: "panel panel-default panel-body"
|
|
1106
|
+
}, /*#__PURE__*/React__default["default"].createElement("div", {
|
|
1107
|
+
className: "form-group"
|
|
1108
|
+
}, /*#__PURE__*/React__default["default"].createElement(Widget, {
|
|
1109
|
+
id: "" + idSchema.$id + (schema.oneOf ? "__oneof_select" : "__anyof_select"),
|
|
1110
|
+
schema: {
|
|
1111
|
+
type: "number",
|
|
1112
|
+
default: 0
|
|
1113
|
+
},
|
|
1114
|
+
onChange: this.onOptionChange,
|
|
1115
|
+
onBlur: onBlur,
|
|
1116
|
+
onFocus: onFocus,
|
|
1117
|
+
value: selectedOption,
|
|
1118
|
+
options: {
|
|
1119
|
+
enumOptions
|
|
1120
|
+
},
|
|
1121
|
+
registry: registry,
|
|
1122
|
+
formContext: formContext,
|
|
1123
|
+
...uiOptions,
|
|
1124
|
+
label: ""
|
|
1125
|
+
})), option !== null && /*#__PURE__*/React__default["default"].createElement(_SchemaField, {
|
|
1126
|
+
name: name,
|
|
1127
|
+
schema: optionSchema,
|
|
1128
|
+
uiSchema: uiSchema,
|
|
1129
|
+
errorSchema: errorSchema,
|
|
1130
|
+
idSchema: idSchema,
|
|
1131
|
+
idPrefix: idPrefix,
|
|
1132
|
+
idSeparator: idSeparator,
|
|
1133
|
+
formData: formData,
|
|
1134
|
+
formContext: formContext,
|
|
1135
|
+
onChange: onChange,
|
|
1136
|
+
onBlur: onBlur,
|
|
1137
|
+
onFocus: onFocus,
|
|
1138
|
+
registry: registry,
|
|
1139
|
+
disabled: disabled,
|
|
1140
|
+
readonly: readonly,
|
|
1141
|
+
hideError: hideError
|
|
1142
|
+
}));
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
// digits followed by any number of 0 characters up until the end of the line.
|
|
1148
|
+
// Ensuring that there is at least one prefixed character is important so that
|
|
1149
|
+
// you don't incorrectly match against "0".
|
|
1150
|
+
|
|
1151
|
+
const trailingCharMatcherWithPrefix = /\.([0-9]*0)*$/; // This is used for trimming the trailing 0 and . characters without affecting
|
|
1152
|
+
// the rest of the string. Its possible to use one RegEx with groups for this
|
|
1153
|
+
// functionality, but it is fairly complex compared to simply defining two
|
|
1154
|
+
// different matchers.
|
|
1155
|
+
|
|
1156
|
+
const trailingCharMatcher = /[0.]0*$/;
|
|
1157
|
+
/**
|
|
1158
|
+
* The NumberField class has some special handling for dealing with trailing
|
|
1159
|
+
* decimal points and/or zeroes. This logic is designed to allow trailing values
|
|
1160
|
+
* to be visible in the input element, but not be represented in the
|
|
1161
|
+
* corresponding form data.
|
|
1162
|
+
*
|
|
1163
|
+
* The algorithm is as follows:
|
|
1164
|
+
*
|
|
1165
|
+
* 1. When the input value changes the value is cached in the component state
|
|
1166
|
+
*
|
|
1167
|
+
* 2. The value is then normalized, removing trailing decimal points and zeros,
|
|
1168
|
+
* then passed to the "onChange" callback
|
|
1169
|
+
*
|
|
1170
|
+
* 3. When the component is rendered, the formData value is checked against the
|
|
1171
|
+
* value cached in the state. If it matches the cached value, the cached
|
|
1172
|
+
* value is passed to the input instead of the formData value
|
|
1173
|
+
*/
|
|
1174
|
+
|
|
1175
|
+
function NumberField(props) {
|
|
1176
|
+
const {
|
|
1177
|
+
registry,
|
|
1178
|
+
onChange,
|
|
1179
|
+
formData,
|
|
1180
|
+
value: initialValue
|
|
1181
|
+
} = props;
|
|
1182
|
+
const [lastValue, setLastValue] = React.useState(initialValue);
|
|
1183
|
+
const {
|
|
1184
|
+
StringField
|
|
1185
|
+
} = registry.fields;
|
|
1186
|
+
let value = formData;
|
|
1187
|
+
/** Handle the change from the `StringField` to properly convert to a number
|
|
1188
|
+
*
|
|
1189
|
+
* @param value - The current value for the change occurring
|
|
1190
|
+
*/
|
|
1191
|
+
|
|
1192
|
+
const handleChange = React.useCallback(value => {
|
|
1193
|
+
// Cache the original value in component state
|
|
1194
|
+
setLastValue(value); // Normalize decimals that don't start with a zero character in advance so
|
|
1195
|
+
// that the rest of the normalization logic is simpler
|
|
1196
|
+
|
|
1197
|
+
if (("" + value).charAt(0) === ".") {
|
|
1198
|
+
value = "0" + value;
|
|
1199
|
+
} // Check that the value is a string (this can happen if the widget used is a
|
|
1200
|
+
// <select>, due to an enum declaration etc) then, if the value ends in a
|
|
1201
|
+
// trailing decimal point or multiple zeroes, strip the trailing values
|
|
1202
|
+
|
|
1203
|
+
|
|
1204
|
+
const processed = typeof value === "string" && value.match(trailingCharMatcherWithPrefix) ? utils.asNumber(value.replace(trailingCharMatcher, "")) : utils.asNumber(value);
|
|
1205
|
+
onChange(processed);
|
|
1206
|
+
}, [onChange]);
|
|
1207
|
+
|
|
1208
|
+
if (typeof lastValue === "string" && typeof value === "number") {
|
|
1209
|
+
// Construct a regular expression that checks for a string that consists
|
|
1210
|
+
// of the formData value suffixed with zero or one '.' characters and zero
|
|
1211
|
+
// or more '0' characters
|
|
1212
|
+
const re = new RegExp(("" + value).replace(".", "\\.") + "\\.?0*$"); // If the cached "lastValue" is a match, use that instead of the formData
|
|
1213
|
+
// value to prevent the input value from changing in the UI
|
|
1214
|
+
|
|
1215
|
+
if (lastValue.match(re)) {
|
|
1216
|
+
value = lastValue;
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
return /*#__PURE__*/React__default["default"].createElement(StringField, { ...props,
|
|
1221
|
+
formData: value,
|
|
1222
|
+
onChange: handleChange
|
|
1223
|
+
});
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
/** The `ObjectField` component is used to render a field in the schema that is of type `object`. It tracks whether an
|
|
1227
|
+
* additional property key was modified and what it was modified to
|
|
1228
|
+
*
|
|
1229
|
+
* @param props - The `FieldProps` for this template
|
|
1230
|
+
*/
|
|
1231
|
+
|
|
1232
|
+
class ObjectField extends React.Component {
|
|
1233
|
+
constructor() {
|
|
1234
|
+
var _this;
|
|
1235
|
+
|
|
1236
|
+
super(...arguments);
|
|
1237
|
+
_this = this;
|
|
1238
|
+
this.state = {
|
|
1239
|
+
wasPropertyKeyModified: false,
|
|
1240
|
+
additionalProperties: {}
|
|
1241
|
+
};
|
|
1242
|
+
|
|
1243
|
+
this.onPropertyChange = function (name, addedByAdditionalProperties) {
|
|
1244
|
+
if (addedByAdditionalProperties === void 0) {
|
|
1245
|
+
addedByAdditionalProperties = false;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
return (value, newErrorSchema) => {
|
|
1249
|
+
const {
|
|
1250
|
+
formData,
|
|
1251
|
+
onChange,
|
|
1252
|
+
errorSchema
|
|
1253
|
+
} = _this.props;
|
|
1254
|
+
|
|
1255
|
+
if (value === undefined && addedByAdditionalProperties) {
|
|
1256
|
+
// Don't set value = undefined for fields added by
|
|
1257
|
+
// additionalProperties. Doing so removes them from the
|
|
1258
|
+
// formData, which causes them to completely disappear
|
|
1259
|
+
// (including the input field for the property name). Unlike
|
|
1260
|
+
// fields which are "mandated" by the schema, these fields can
|
|
1261
|
+
// be set to undefined by clicking a "delete field" button, so
|
|
1262
|
+
// set empty values to the empty string.
|
|
1263
|
+
value = "";
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
const newFormData = { ...formData,
|
|
1267
|
+
[name]: value
|
|
1268
|
+
};
|
|
1269
|
+
onChange(newFormData, errorSchema && errorSchema && { ...errorSchema,
|
|
1270
|
+
[name]: newErrorSchema
|
|
1271
|
+
});
|
|
1272
|
+
};
|
|
1273
|
+
};
|
|
1274
|
+
|
|
1275
|
+
this.onDropPropertyClick = key => {
|
|
1276
|
+
return event => {
|
|
1277
|
+
event.preventDefault();
|
|
1278
|
+
const {
|
|
1279
|
+
onChange,
|
|
1280
|
+
formData
|
|
1281
|
+
} = this.props;
|
|
1282
|
+
const copiedFormData = { ...formData
|
|
1283
|
+
};
|
|
1284
|
+
unset__default["default"](copiedFormData, key);
|
|
1285
|
+
onChange(copiedFormData);
|
|
1286
|
+
};
|
|
1287
|
+
};
|
|
1288
|
+
|
|
1289
|
+
this.getAvailableKey = (preferredKey, formData) => {
|
|
1290
|
+
const {
|
|
1291
|
+
uiSchema
|
|
1292
|
+
} = this.props;
|
|
1293
|
+
const {
|
|
1294
|
+
duplicateKeySuffixSeparator = "-"
|
|
1295
|
+
} = utils.getUiOptions(uiSchema);
|
|
1296
|
+
let index = 0;
|
|
1297
|
+
let newKey = preferredKey;
|
|
1298
|
+
|
|
1299
|
+
while (newKey in formData) {
|
|
1300
|
+
newKey = "" + preferredKey + duplicateKeySuffixSeparator + ++index;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
return newKey;
|
|
1304
|
+
};
|
|
1305
|
+
|
|
1306
|
+
this.onKeyChange = oldValue => {
|
|
1307
|
+
return (value, newErrorSchema) => {
|
|
1308
|
+
if (oldValue === value) {
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
const {
|
|
1313
|
+
formData,
|
|
1314
|
+
onChange,
|
|
1315
|
+
errorSchema
|
|
1316
|
+
} = this.props;
|
|
1317
|
+
value = this.getAvailableKey(value, formData);
|
|
1318
|
+
const newFormData = { ...formData
|
|
1319
|
+
};
|
|
1320
|
+
const newKeys = {
|
|
1321
|
+
[oldValue]: value
|
|
1322
|
+
};
|
|
1323
|
+
const keyValues = Object.keys(newFormData).map(key => {
|
|
1324
|
+
const newKey = newKeys[key] || key;
|
|
1325
|
+
return {
|
|
1326
|
+
[newKey]: newFormData[key]
|
|
1327
|
+
};
|
|
1328
|
+
});
|
|
1329
|
+
const renamedObj = Object.assign({}, ...keyValues);
|
|
1330
|
+
this.setState({
|
|
1331
|
+
wasPropertyKeyModified: true
|
|
1332
|
+
});
|
|
1333
|
+
onChange(renamedObj, errorSchema && errorSchema && { ...errorSchema,
|
|
1334
|
+
[value]: newErrorSchema
|
|
1335
|
+
});
|
|
1336
|
+
};
|
|
1337
|
+
};
|
|
1338
|
+
|
|
1339
|
+
this.handleAddClick = schema => () => {
|
|
1340
|
+
if (!isObject__default["default"](schema.additionalProperties)) {
|
|
1341
|
+
return;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
const {
|
|
1345
|
+
formData,
|
|
1346
|
+
onChange,
|
|
1347
|
+
registry
|
|
1348
|
+
} = this.props;
|
|
1349
|
+
let type = schema.additionalProperties.type;
|
|
1350
|
+
const newFormData = { ...formData
|
|
1351
|
+
};
|
|
1352
|
+
|
|
1353
|
+
if (utils.REF_KEY in schema.additionalProperties) {
|
|
1354
|
+
const {
|
|
1355
|
+
schemaUtils
|
|
1356
|
+
} = registry;
|
|
1357
|
+
const refSchema = schemaUtils.retrieveSchema({
|
|
1358
|
+
$ref: schema.additionalProperties[utils.REF_KEY]
|
|
1359
|
+
}, formData);
|
|
1360
|
+
type = refSchema.type;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
const newKey = this.getAvailableKey("newKey", newFormData); // Cast this to make the `set` work properly
|
|
1364
|
+
|
|
1365
|
+
set__default["default"](newFormData, newKey, this.getDefaultValue(type));
|
|
1366
|
+
onChange(newFormData);
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
/** Returns a flag indicating whether the `name` field is required in the object schema
|
|
1371
|
+
*
|
|
1372
|
+
* @param name - The name of the field to check for required-ness
|
|
1373
|
+
* @returns - True if the field `name` is required, false otherwise
|
|
1374
|
+
*/
|
|
1375
|
+
isRequired(name) {
|
|
1376
|
+
const {
|
|
1377
|
+
schema
|
|
1378
|
+
} = this.props;
|
|
1379
|
+
return Array.isArray(schema.required) && schema.required.indexOf(name) !== -1;
|
|
1380
|
+
}
|
|
1381
|
+
/** Returns the `onPropertyChange` handler for the `name` field. Handles the special case where a user is attempting
|
|
1382
|
+
* to clear the data for a field added as an additional property. Calls the `onChange()` handler with the updated
|
|
1383
|
+
* formData.
|
|
1384
|
+
*
|
|
1385
|
+
* @param name - The name of the property
|
|
1386
|
+
* @param addedByAdditionalProperties - Flag indicating whether this property is an additional property
|
|
1387
|
+
* @returns - The onPropertyChange callback for the `name` property
|
|
1388
|
+
*/
|
|
1389
|
+
|
|
1390
|
+
|
|
1391
|
+
/** Returns a default value to be used for a new additional schema property of the given `type`
|
|
1392
|
+
*
|
|
1393
|
+
* @param type - The type of the new additional schema property
|
|
1394
|
+
*/
|
|
1395
|
+
getDefaultValue(type) {
|
|
1396
|
+
switch (type) {
|
|
1397
|
+
case "string":
|
|
1398
|
+
return "New Value";
|
|
1399
|
+
|
|
1400
|
+
case "array":
|
|
1401
|
+
return [];
|
|
1402
|
+
|
|
1403
|
+
case "boolean":
|
|
1404
|
+
return false;
|
|
1405
|
+
|
|
1406
|
+
case "null":
|
|
1407
|
+
return null;
|
|
1408
|
+
|
|
1409
|
+
case "number":
|
|
1410
|
+
return 0;
|
|
1411
|
+
|
|
1412
|
+
case "object":
|
|
1413
|
+
return {};
|
|
1414
|
+
|
|
1415
|
+
default:
|
|
1416
|
+
// We don't have a datatype for some reason (perhaps additionalProperties was true)
|
|
1417
|
+
return "New Value";
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
/** Handles the adding of a new additional property on the given `schema`. Calls the `onChange` callback once the new
|
|
1421
|
+
* default data for that field has been added to the formData.
|
|
1422
|
+
*
|
|
1423
|
+
* @param schema - The schema element to which the new property is being added
|
|
1424
|
+
*/
|
|
1425
|
+
|
|
1426
|
+
|
|
1427
|
+
/** Renders the `ObjectField` from the given props
|
|
1428
|
+
*/
|
|
1429
|
+
render() {
|
|
1430
|
+
const {
|
|
1431
|
+
schema: rawSchema,
|
|
1432
|
+
uiSchema = {},
|
|
1433
|
+
formData,
|
|
1434
|
+
errorSchema,
|
|
1435
|
+
idSchema,
|
|
1436
|
+
name,
|
|
1437
|
+
required = false,
|
|
1438
|
+
disabled = false,
|
|
1439
|
+
readonly = false,
|
|
1440
|
+
hideError,
|
|
1441
|
+
idPrefix,
|
|
1442
|
+
idSeparator,
|
|
1443
|
+
onBlur,
|
|
1444
|
+
onFocus,
|
|
1445
|
+
registry
|
|
1446
|
+
} = this.props;
|
|
1447
|
+
const {
|
|
1448
|
+
fields,
|
|
1449
|
+
formContext,
|
|
1450
|
+
schemaUtils
|
|
1451
|
+
} = registry;
|
|
1452
|
+
const {
|
|
1453
|
+
SchemaField
|
|
1454
|
+
} = fields;
|
|
1455
|
+
const schema = schemaUtils.retrieveSchema(rawSchema, formData);
|
|
1456
|
+
const uiOptions = utils.getUiOptions(uiSchema);
|
|
1457
|
+
const {
|
|
1458
|
+
properties: schemaProperties = {}
|
|
1459
|
+
} = schema;
|
|
1460
|
+
const title = schema.title === undefined ? name : schema.title;
|
|
1461
|
+
const description = uiOptions.description || schema.description;
|
|
1462
|
+
let orderedProperties;
|
|
1463
|
+
|
|
1464
|
+
try {
|
|
1465
|
+
const properties = Object.keys(schemaProperties);
|
|
1466
|
+
orderedProperties = utils.orderProperties(properties, uiOptions.order);
|
|
1467
|
+
} catch (err) {
|
|
1468
|
+
return /*#__PURE__*/React__default["default"].createElement("div", null, /*#__PURE__*/React__default["default"].createElement("p", {
|
|
1469
|
+
className: "config-error",
|
|
1470
|
+
style: {
|
|
1471
|
+
color: "red"
|
|
1472
|
+
}
|
|
1473
|
+
}, "Invalid ", name || "root", " object field configuration:", /*#__PURE__*/React__default["default"].createElement("em", null, err.message), "."), /*#__PURE__*/React__default["default"].createElement("pre", null, JSON.stringify(schema)));
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
const Template = utils.getTemplate("ObjectFieldTemplate", registry, uiOptions);
|
|
1477
|
+
const templateProps = {
|
|
1478
|
+
title: uiOptions.title || title,
|
|
1479
|
+
description,
|
|
1480
|
+
properties: orderedProperties.map(name => {
|
|
1481
|
+
const addedByAdditionalProperties = has__default["default"](schema, [utils.PROPERTIES_KEY, name, utils.ADDITIONAL_PROPERTY_FLAG]);
|
|
1482
|
+
const fieldUiSchema = addedByAdditionalProperties ? uiSchema.additionalProperties : uiSchema[name];
|
|
1483
|
+
const hidden = utils.getUiOptions(fieldUiSchema).widget === "hidden";
|
|
1484
|
+
const fieldIdSchema = get__default["default"](idSchema, [name], {});
|
|
1485
|
+
return {
|
|
1486
|
+
content: /*#__PURE__*/React__default["default"].createElement(SchemaField, {
|
|
1487
|
+
key: name,
|
|
1488
|
+
name: name,
|
|
1489
|
+
required: this.isRequired(name),
|
|
1490
|
+
schema: get__default["default"](schema, [utils.PROPERTIES_KEY, name], {}),
|
|
1491
|
+
uiSchema: fieldUiSchema,
|
|
1492
|
+
errorSchema: get__default["default"](errorSchema, name),
|
|
1493
|
+
idSchema: fieldIdSchema,
|
|
1494
|
+
idPrefix: idPrefix,
|
|
1495
|
+
idSeparator: idSeparator,
|
|
1496
|
+
formData: get__default["default"](formData, name),
|
|
1497
|
+
formContext: formContext,
|
|
1498
|
+
wasPropertyKeyModified: this.state.wasPropertyKeyModified,
|
|
1499
|
+
onKeyChange: this.onKeyChange(name),
|
|
1500
|
+
onChange: this.onPropertyChange(name, addedByAdditionalProperties),
|
|
1501
|
+
onBlur: onBlur,
|
|
1502
|
+
onFocus: onFocus,
|
|
1503
|
+
registry: registry,
|
|
1504
|
+
disabled: disabled,
|
|
1505
|
+
readonly: readonly,
|
|
1506
|
+
hideError: hideError,
|
|
1507
|
+
onDropPropertyClick: this.onDropPropertyClick
|
|
1508
|
+
}),
|
|
1509
|
+
name,
|
|
1510
|
+
readonly,
|
|
1511
|
+
disabled,
|
|
1512
|
+
required,
|
|
1513
|
+
hidden
|
|
1514
|
+
};
|
|
1515
|
+
}),
|
|
1516
|
+
readonly,
|
|
1517
|
+
disabled,
|
|
1518
|
+
required,
|
|
1519
|
+
idSchema,
|
|
1520
|
+
uiSchema,
|
|
1521
|
+
schema,
|
|
1522
|
+
formData,
|
|
1523
|
+
formContext,
|
|
1524
|
+
registry
|
|
1525
|
+
};
|
|
1526
|
+
return /*#__PURE__*/React__default["default"].createElement(Template, { ...templateProps,
|
|
1527
|
+
onAddClick: this.handleAddClick
|
|
1528
|
+
});
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
/** The map of component type to FieldName */
|
|
1534
|
+
|
|
1535
|
+
const COMPONENT_TYPES = {
|
|
1536
|
+
array: "ArrayField",
|
|
1537
|
+
boolean: "BooleanField",
|
|
1538
|
+
integer: "NumberField",
|
|
1539
|
+
number: "NumberField",
|
|
1540
|
+
object: "ObjectField",
|
|
1541
|
+
string: "StringField",
|
|
1542
|
+
null: "NullField"
|
|
1543
|
+
};
|
|
1544
|
+
/** Computes and returns which `Field` implementation to return in order to render the field represented by the
|
|
1545
|
+
* `schema`. The `uiOptions` are used to alter what potential `Field` implementation is actually returned. If no
|
|
1546
|
+
* appropriate `Field` implementation can be found then a wrapper around `UnsupportedFieldTemplate` is used.
|
|
1547
|
+
*
|
|
1548
|
+
* @param schema - The schema from which to obtain the type
|
|
1549
|
+
* @param uiOptions - The UI Options that may affect the component decision
|
|
1550
|
+
* @param idSchema - The id that is passed to the `UnsupportedFieldTemplate`
|
|
1551
|
+
* @param registry - The registry from which fields and templates are obtained
|
|
1552
|
+
* @returns - The `Field` component that is used to render the actual field data
|
|
1553
|
+
*/
|
|
1554
|
+
|
|
1555
|
+
function getFieldComponent(schema, uiOptions, idSchema, registry) {
|
|
1556
|
+
const field = uiOptions.field;
|
|
1557
|
+
const {
|
|
1558
|
+
fields
|
|
1559
|
+
} = registry;
|
|
1560
|
+
|
|
1561
|
+
if (typeof field === "function") {
|
|
1562
|
+
return field;
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
if (typeof field === "string" && field in fields) {
|
|
1566
|
+
return fields[field];
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
const schemaType = utils.getSchemaType(schema);
|
|
1570
|
+
const type = Array.isArray(schemaType) ? schemaType[0] : schemaType || "";
|
|
1571
|
+
const componentName = COMPONENT_TYPES[type]; // If the type is not defined and the schema uses 'anyOf' or 'oneOf', don't
|
|
1572
|
+
// render a field and let the MultiSchemaField component handle the form display
|
|
1573
|
+
|
|
1574
|
+
if (!componentName && (schema.anyOf || schema.oneOf)) {
|
|
1575
|
+
return () => null;
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
return componentName in fields ? fields[componentName] : () => {
|
|
1579
|
+
const UnsupportedFieldTemplate = utils.getTemplate("UnsupportedFieldTemplate", registry, uiOptions);
|
|
1580
|
+
return /*#__PURE__*/React__default["default"].createElement(UnsupportedFieldTemplate, {
|
|
1581
|
+
schema: schema,
|
|
1582
|
+
idSchema: idSchema,
|
|
1583
|
+
reason: "Unknown field type " + schema.type,
|
|
1584
|
+
registry: registry
|
|
1585
|
+
});
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
/** The `Help` component renders any help desired for a field
|
|
1589
|
+
*
|
|
1590
|
+
* @param props - The id and help information to be rendered
|
|
1591
|
+
*/
|
|
1592
|
+
|
|
1593
|
+
|
|
1594
|
+
function Help(props) {
|
|
1595
|
+
const {
|
|
1596
|
+
id,
|
|
1597
|
+
help
|
|
1598
|
+
} = props;
|
|
1599
|
+
|
|
1600
|
+
if (!help) {
|
|
1601
|
+
return null;
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
if (typeof help === "string") {
|
|
1605
|
+
return /*#__PURE__*/React__default["default"].createElement("p", {
|
|
1606
|
+
id: id,
|
|
1607
|
+
className: "help-block"
|
|
1608
|
+
}, help);
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
return /*#__PURE__*/React__default["default"].createElement("div", {
|
|
1612
|
+
id: id,
|
|
1613
|
+
className: "help-block"
|
|
1614
|
+
}, help);
|
|
1615
|
+
}
|
|
1616
|
+
/** The `ErrorList` component renders the errors local to the particular field
|
|
1617
|
+
*
|
|
1618
|
+
* @param props - The list of errors to show
|
|
1619
|
+
*/
|
|
1620
|
+
|
|
1621
|
+
|
|
1622
|
+
function ErrorList$1(props) {
|
|
1623
|
+
const {
|
|
1624
|
+
errors = []
|
|
1625
|
+
} = props;
|
|
1626
|
+
|
|
1627
|
+
if (errors.length === 0) {
|
|
1628
|
+
return null;
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
return /*#__PURE__*/React__default["default"].createElement("div", null, /*#__PURE__*/React__default["default"].createElement("ul", {
|
|
1632
|
+
className: "error-detail bs-callout bs-callout-info"
|
|
1633
|
+
}, errors.filter(elem => !!elem).map((error, index) => {
|
|
1634
|
+
return /*#__PURE__*/React__default["default"].createElement("li", {
|
|
1635
|
+
className: "text-danger",
|
|
1636
|
+
key: index
|
|
1637
|
+
}, error);
|
|
1638
|
+
})));
|
|
1639
|
+
}
|
|
1640
|
+
/** The `SchemaFieldRender` component is the work-horse of react-jsonschema-form, determining what kind of real field to
|
|
1641
|
+
* render based on the `schema`, `uiSchema` and all the other props. It also deals with rendering the `anyOf` and
|
|
1642
|
+
* `oneOf` fields.
|
|
1643
|
+
*
|
|
1644
|
+
* @param props - The `FieldProps` for this component
|
|
1645
|
+
*/
|
|
1646
|
+
|
|
1647
|
+
|
|
1648
|
+
function SchemaFieldRender(props) {
|
|
1649
|
+
const {
|
|
1650
|
+
schema: _schema,
|
|
1651
|
+
idSchema: _idSchema,
|
|
1652
|
+
uiSchema,
|
|
1653
|
+
formData,
|
|
1654
|
+
errorSchema,
|
|
1655
|
+
idPrefix,
|
|
1656
|
+
idSeparator,
|
|
1657
|
+
name,
|
|
1658
|
+
onChange,
|
|
1659
|
+
onKeyChange,
|
|
1660
|
+
onDropPropertyClick,
|
|
1661
|
+
required,
|
|
1662
|
+
registry,
|
|
1663
|
+
wasPropertyKeyModified = false
|
|
1664
|
+
} = props;
|
|
1665
|
+
const {
|
|
1666
|
+
formContext,
|
|
1667
|
+
schemaUtils
|
|
1668
|
+
} = registry;
|
|
1669
|
+
const uiOptions = utils.getUiOptions(uiSchema);
|
|
1670
|
+
const FieldTemplate = utils.getTemplate("FieldTemplate", registry, uiOptions);
|
|
1671
|
+
const DescriptionFieldTemplate = utils.getTemplate("DescriptionFieldTemplate", registry, uiOptions);
|
|
1672
|
+
const schema = schemaUtils.retrieveSchema(_schema, formData);
|
|
1673
|
+
const idSchema = utils.mergeObjects(schemaUtils.toIdSchema(schema, _idSchema.$id, formData, idPrefix, idSeparator), _idSchema);
|
|
1674
|
+
const FieldComponent = getFieldComponent(schema, uiOptions, idSchema, registry);
|
|
1675
|
+
const disabled = Boolean(props.disabled || uiOptions.disabled);
|
|
1676
|
+
const readonly = Boolean(props.readonly || uiOptions.readonly || props.schema.readOnly || schema.readOnly);
|
|
1677
|
+
const uiSchemaHideError = uiOptions.hideError; // Set hideError to the value provided in the uiSchema, otherwise stick with the prop to propagate to children
|
|
1678
|
+
|
|
1679
|
+
const hideError = uiSchemaHideError === undefined ? props.hideError : Boolean(uiSchemaHideError);
|
|
1680
|
+
const autofocus = Boolean(props.autofocus || uiOptions.autofocus);
|
|
1681
|
+
|
|
1682
|
+
if (Object.keys(schema).length === 0) {
|
|
1683
|
+
return null;
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema);
|
|
1687
|
+
const {
|
|
1688
|
+
__errors,
|
|
1689
|
+
...fieldErrorSchema
|
|
1690
|
+
} = errorSchema || {}; // See #439: uiSchema: Don't pass consumed class names to child components
|
|
1691
|
+
|
|
1692
|
+
const fieldUiSchema = omit__default["default"](uiSchema, ["ui:classNames", "classNames"]);
|
|
1693
|
+
|
|
1694
|
+
if ("ui:options" in fieldUiSchema) {
|
|
1695
|
+
fieldUiSchema["ui:options"] = omit__default["default"](fieldUiSchema["ui:options"], ["classNames"]);
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
const field = /*#__PURE__*/React__default["default"].createElement(FieldComponent, { ...props,
|
|
1699
|
+
idSchema: idSchema,
|
|
1700
|
+
schema: schema,
|
|
1701
|
+
uiSchema: fieldUiSchema,
|
|
1702
|
+
disabled: disabled,
|
|
1703
|
+
readonly: readonly,
|
|
1704
|
+
hideError: hideError,
|
|
1705
|
+
autofocus: autofocus,
|
|
1706
|
+
errorSchema: fieldErrorSchema,
|
|
1707
|
+
formContext: formContext,
|
|
1708
|
+
rawErrors: __errors
|
|
1709
|
+
});
|
|
1710
|
+
const id = idSchema[utils.ID_KEY]; // If this schema has a title defined, but the user has set a new key/label, retain their input.
|
|
1711
|
+
|
|
1712
|
+
let label;
|
|
1713
|
+
|
|
1714
|
+
if (wasPropertyKeyModified) {
|
|
1715
|
+
label = name;
|
|
1716
|
+
} else {
|
|
1717
|
+
label = uiOptions.title || props.schema.title || schema.title || name;
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
const description = uiOptions.description || props.schema.description || schema.description || "";
|
|
1721
|
+
const errors = __errors;
|
|
1722
|
+
const help = uiOptions.help;
|
|
1723
|
+
const hidden = uiOptions.widget === "hidden";
|
|
1724
|
+
const classNames = ["form-group", "field", "field-" + schema.type];
|
|
1725
|
+
|
|
1726
|
+
if (!hideError && errors && errors.length > 0) {
|
|
1727
|
+
classNames.push("field-error has-error has-danger");
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
if (uiSchema !== null && uiSchema !== void 0 && uiSchema.classNames) {
|
|
1731
|
+
{
|
|
1732
|
+
console.warn("'uiSchema.classNames' is deprecated and may be removed in a major release; Use 'ui:classNames' instead.");
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
classNames.push(uiSchema.classNames);
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
if (uiOptions.classNames) {
|
|
1739
|
+
classNames.push(uiOptions.classNames);
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
const fieldProps = {
|
|
1743
|
+
description: /*#__PURE__*/React__default["default"].createElement(DescriptionFieldTemplate, {
|
|
1744
|
+
id: id + "__description",
|
|
1745
|
+
description: description,
|
|
1746
|
+
registry: registry
|
|
1747
|
+
}),
|
|
1748
|
+
rawDescription: description,
|
|
1749
|
+
help: /*#__PURE__*/React__default["default"].createElement(Help, {
|
|
1750
|
+
id: id + "__help",
|
|
1751
|
+
help: help
|
|
1752
|
+
}),
|
|
1753
|
+
rawHelp: typeof help === "string" ? help : undefined,
|
|
1754
|
+
errors: hideError ? undefined : /*#__PURE__*/React__default["default"].createElement(ErrorList$1, {
|
|
1755
|
+
errors: errors
|
|
1756
|
+
}),
|
|
1757
|
+
rawErrors: hideError ? undefined : errors,
|
|
1758
|
+
id,
|
|
1759
|
+
label,
|
|
1760
|
+
hidden,
|
|
1761
|
+
onChange,
|
|
1762
|
+
onKeyChange,
|
|
1763
|
+
onDropPropertyClick,
|
|
1764
|
+
required,
|
|
1765
|
+
disabled,
|
|
1766
|
+
readonly,
|
|
1767
|
+
hideError,
|
|
1768
|
+
displayLabel,
|
|
1769
|
+
classNames: classNames.join(" ").trim(),
|
|
1770
|
+
formContext,
|
|
1771
|
+
formData,
|
|
1772
|
+
schema,
|
|
1773
|
+
uiSchema,
|
|
1774
|
+
registry
|
|
1775
|
+
};
|
|
1776
|
+
const _AnyOfField = registry.fields.AnyOfField;
|
|
1777
|
+
const _OneOfField = registry.fields.OneOfField;
|
|
1778
|
+
return /*#__PURE__*/React__default["default"].createElement(FieldTemplate, { ...fieldProps
|
|
1779
|
+
}, /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, field, schema.anyOf && !(uiSchema !== null && uiSchema !== void 0 && uiSchema["ui:field"]) && !schemaUtils.isSelect(schema) && /*#__PURE__*/React__default["default"].createElement(_AnyOfField, {
|
|
1780
|
+
name: name,
|
|
1781
|
+
disabled: disabled,
|
|
1782
|
+
readonly: readonly,
|
|
1783
|
+
hideError: hideError,
|
|
1784
|
+
errorSchema: errorSchema,
|
|
1785
|
+
formData: formData,
|
|
1786
|
+
formContext: formContext,
|
|
1787
|
+
idPrefix: idPrefix,
|
|
1788
|
+
idSchema: idSchema,
|
|
1789
|
+
idSeparator: idSeparator,
|
|
1790
|
+
onBlur: props.onBlur,
|
|
1791
|
+
onChange: props.onChange,
|
|
1792
|
+
onFocus: props.onFocus,
|
|
1793
|
+
options: schema.anyOf.map(_schema => schemaUtils.retrieveSchema(isObject__default["default"](_schema) ? _schema : {}, formData)),
|
|
1794
|
+
baseType: schema.type,
|
|
1795
|
+
registry: registry,
|
|
1796
|
+
schema: schema,
|
|
1797
|
+
uiSchema: uiSchema
|
|
1798
|
+
}), schema.oneOf && !(uiSchema !== null && uiSchema !== void 0 && uiSchema["ui:field"]) && !schemaUtils.isSelect(schema) && /*#__PURE__*/React__default["default"].createElement(_OneOfField, {
|
|
1799
|
+
name: name,
|
|
1800
|
+
disabled: disabled,
|
|
1801
|
+
readonly: readonly,
|
|
1802
|
+
hideError: hideError,
|
|
1803
|
+
errorSchema: errorSchema,
|
|
1804
|
+
formData: formData,
|
|
1805
|
+
formContext: formContext,
|
|
1806
|
+
idPrefix: idPrefix,
|
|
1807
|
+
idSchema: idSchema,
|
|
1808
|
+
idSeparator: idSeparator,
|
|
1809
|
+
onBlur: props.onBlur,
|
|
1810
|
+
onChange: props.onChange,
|
|
1811
|
+
onFocus: props.onFocus,
|
|
1812
|
+
options: schema.oneOf.map(_schema => schemaUtils.retrieveSchema(isObject__default["default"](_schema) ? _schema : {}, formData)),
|
|
1813
|
+
baseType: schema.type,
|
|
1814
|
+
registry: registry,
|
|
1815
|
+
schema: schema,
|
|
1816
|
+
uiSchema: uiSchema
|
|
1817
|
+
})));
|
|
1818
|
+
}
|
|
1819
|
+
/** The `SchemaField` component determines whether it is necessary to rerender the component based on any props changes
|
|
1820
|
+
* and if so, calls the `SchemaFieldRender` component with the props.
|
|
1821
|
+
*/
|
|
1822
|
+
|
|
1823
|
+
|
|
1824
|
+
class SchemaField extends React__default["default"].Component {
|
|
1825
|
+
shouldComponentUpdate(nextProps) {
|
|
1826
|
+
return !utils.deepEquals(this.props, nextProps);
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
render() {
|
|
1830
|
+
return /*#__PURE__*/React__default["default"].createElement(SchemaFieldRender, { ...this.props
|
|
1831
|
+
});
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
/** The `StringField` component is used to render a schema field that represents a string type
|
|
1837
|
+
*
|
|
1838
|
+
* @param props - The `FieldProps` for this template
|
|
1839
|
+
*/
|
|
1840
|
+
|
|
1841
|
+
function StringField(props) {
|
|
1842
|
+
const {
|
|
1843
|
+
schema,
|
|
1844
|
+
name,
|
|
1845
|
+
uiSchema,
|
|
1846
|
+
idSchema,
|
|
1847
|
+
formData,
|
|
1848
|
+
required,
|
|
1849
|
+
disabled = false,
|
|
1850
|
+
readonly = false,
|
|
1851
|
+
autofocus = false,
|
|
1852
|
+
onChange,
|
|
1853
|
+
onBlur,
|
|
1854
|
+
onFocus,
|
|
1855
|
+
registry,
|
|
1856
|
+
rawErrors
|
|
1857
|
+
} = props;
|
|
1858
|
+
const {
|
|
1859
|
+
title,
|
|
1860
|
+
format
|
|
1861
|
+
} = schema;
|
|
1862
|
+
const {
|
|
1863
|
+
widgets,
|
|
1864
|
+
formContext,
|
|
1865
|
+
schemaUtils
|
|
1866
|
+
} = registry;
|
|
1867
|
+
const enumOptions = schemaUtils.isSelect(schema) ? utils.optionsList(schema) : undefined;
|
|
1868
|
+
let defaultWidget = enumOptions ? "select" : "text";
|
|
1869
|
+
|
|
1870
|
+
if (format && utils.hasWidget(schema, format, widgets)) {
|
|
1871
|
+
defaultWidget = format;
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
const {
|
|
1875
|
+
widget = defaultWidget,
|
|
1876
|
+
placeholder = "",
|
|
1877
|
+
...options
|
|
1878
|
+
} = utils.getUiOptions(uiSchema);
|
|
1879
|
+
const Widget = utils.getWidget(schema, widget, widgets);
|
|
1880
|
+
return /*#__PURE__*/React__default["default"].createElement(Widget, {
|
|
1881
|
+
options: { ...options,
|
|
1882
|
+
enumOptions
|
|
1883
|
+
},
|
|
1884
|
+
schema: schema,
|
|
1885
|
+
uiSchema: uiSchema,
|
|
1886
|
+
id: idSchema && idSchema.$id,
|
|
1887
|
+
label: title === undefined ? name : title,
|
|
1888
|
+
value: formData,
|
|
1889
|
+
onChange: onChange,
|
|
1890
|
+
onBlur: onBlur,
|
|
1891
|
+
onFocus: onFocus,
|
|
1892
|
+
required: required,
|
|
1893
|
+
disabled: disabled,
|
|
1894
|
+
readonly: readonly,
|
|
1895
|
+
formContext: formContext,
|
|
1896
|
+
autofocus: autofocus,
|
|
1897
|
+
registry: registry,
|
|
1898
|
+
placeholder: placeholder,
|
|
1899
|
+
rawErrors: rawErrors
|
|
1900
|
+
});
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
/** The `NullField` component is used to render a field in the schema is null. It also ensures that the `formData` is
|
|
1904
|
+
* also set to null if it has no value.
|
|
1905
|
+
*
|
|
1906
|
+
* @param props - The `FieldProps` for this template
|
|
1907
|
+
*/
|
|
1908
|
+
|
|
1909
|
+
function NullField(props) {
|
|
1910
|
+
const {
|
|
1911
|
+
formData,
|
|
1912
|
+
onChange
|
|
1913
|
+
} = props;
|
|
1914
|
+
React.useEffect(() => {
|
|
1915
|
+
if (formData === undefined) {
|
|
1916
|
+
onChange(null);
|
|
1917
|
+
}
|
|
1918
|
+
}, [formData, onChange]);
|
|
1919
|
+
return null;
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
const fields = {
|
|
1923
|
+
AnyOfField: AnyOfField,
|
|
1924
|
+
ArrayField,
|
|
1925
|
+
BooleanField,
|
|
1926
|
+
NumberField,
|
|
1927
|
+
ObjectField,
|
|
1928
|
+
OneOfField: AnyOfField,
|
|
1929
|
+
SchemaField,
|
|
1930
|
+
StringField,
|
|
1931
|
+
NullField
|
|
1932
|
+
};
|
|
1933
|
+
|
|
1934
|
+
/** The `ArrayFieldDescriptionTemplate` component renders a `DescriptionFieldTemplate` with an `id` derived from
|
|
1935
|
+
* the `idSchema`.
|
|
1936
|
+
*
|
|
1937
|
+
* @param props - The `ArrayFieldDescriptionProps` for the component
|
|
1938
|
+
*/
|
|
1939
|
+
|
|
1940
|
+
function ArrayFieldDescriptionTemplate(props) {
|
|
1941
|
+
const {
|
|
1942
|
+
idSchema,
|
|
1943
|
+
description,
|
|
1944
|
+
registry,
|
|
1945
|
+
uiSchema
|
|
1946
|
+
} = props;
|
|
1947
|
+
|
|
1948
|
+
if (!description) {
|
|
1949
|
+
return null;
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
const options = utils.getUiOptions(uiSchema);
|
|
1953
|
+
const DescriptionFieldTemplate = utils.getTemplate("DescriptionFieldTemplate", registry, options);
|
|
1954
|
+
const id = idSchema.$id + "__description";
|
|
1955
|
+
return /*#__PURE__*/React__default["default"].createElement(DescriptionFieldTemplate, {
|
|
1956
|
+
id: id,
|
|
1957
|
+
description: description,
|
|
1958
|
+
registry: registry
|
|
1959
|
+
});
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
/** The `ArrayFieldItemTemplate` component is the template used to render an items of an array.
|
|
1963
|
+
*
|
|
1964
|
+
* @param props - The `ArrayFieldTemplateItemType` props for the component
|
|
1965
|
+
*/
|
|
1966
|
+
|
|
1967
|
+
function ArrayFieldItemTemplate(props) {
|
|
1968
|
+
const {
|
|
1969
|
+
children,
|
|
1970
|
+
className,
|
|
1971
|
+
disabled,
|
|
1972
|
+
hasToolbar,
|
|
1973
|
+
hasMoveDown,
|
|
1974
|
+
hasMoveUp,
|
|
1975
|
+
hasRemove,
|
|
1976
|
+
index,
|
|
1977
|
+
onDropIndexClick,
|
|
1978
|
+
onReorderClick,
|
|
1979
|
+
readonly,
|
|
1980
|
+
registry
|
|
1981
|
+
} = props;
|
|
1982
|
+
const {
|
|
1983
|
+
MoveDownButton,
|
|
1984
|
+
MoveUpButton,
|
|
1985
|
+
RemoveButton
|
|
1986
|
+
} = registry.templates.ButtonTemplates;
|
|
1987
|
+
const btnStyle = {
|
|
1988
|
+
flex: 1,
|
|
1989
|
+
paddingLeft: 6,
|
|
1990
|
+
paddingRight: 6,
|
|
1991
|
+
fontWeight: "bold"
|
|
1992
|
+
};
|
|
1993
|
+
return /*#__PURE__*/React__default["default"].createElement("div", {
|
|
1994
|
+
className: className
|
|
1995
|
+
}, /*#__PURE__*/React__default["default"].createElement("div", {
|
|
1996
|
+
className: hasToolbar ? "col-xs-9" : "col-xs-12"
|
|
1997
|
+
}, children), hasToolbar && /*#__PURE__*/React__default["default"].createElement("div", {
|
|
1998
|
+
className: "col-xs-3 array-item-toolbox"
|
|
1999
|
+
}, /*#__PURE__*/React__default["default"].createElement("div", {
|
|
2000
|
+
className: "btn-group",
|
|
2001
|
+
style: {
|
|
2002
|
+
display: "flex",
|
|
2003
|
+
justifyContent: "space-around"
|
|
2004
|
+
}
|
|
2005
|
+
}, (hasMoveUp || hasMoveDown) && /*#__PURE__*/React__default["default"].createElement(MoveUpButton, {
|
|
2006
|
+
style: btnStyle,
|
|
2007
|
+
disabled: disabled || readonly || !hasMoveUp,
|
|
2008
|
+
onClick: onReorderClick(index, index - 1)
|
|
2009
|
+
}), (hasMoveUp || hasMoveDown) && /*#__PURE__*/React__default["default"].createElement(MoveDownButton, {
|
|
2010
|
+
style: btnStyle,
|
|
2011
|
+
disabled: disabled || readonly || !hasMoveDown,
|
|
2012
|
+
onClick: onReorderClick(index, index + 1)
|
|
2013
|
+
}), hasRemove && /*#__PURE__*/React__default["default"].createElement(RemoveButton, {
|
|
2014
|
+
style: btnStyle,
|
|
2015
|
+
disabled: disabled || readonly,
|
|
2016
|
+
onClick: onDropIndexClick(index)
|
|
2017
|
+
}))));
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
/** The `ArrayFieldTemplate` component is the template used to render all items in an array.
|
|
2021
|
+
*
|
|
2022
|
+
* @param props - The `ArrayFieldTemplateItemType` props for the component
|
|
2023
|
+
*/
|
|
2024
|
+
|
|
2025
|
+
function ArrayFieldTemplate(props) {
|
|
2026
|
+
const {
|
|
2027
|
+
canAdd,
|
|
2028
|
+
className,
|
|
2029
|
+
disabled,
|
|
2030
|
+
idSchema,
|
|
2031
|
+
uiSchema,
|
|
2032
|
+
items,
|
|
2033
|
+
onAddClick,
|
|
2034
|
+
readonly,
|
|
2035
|
+
registry,
|
|
2036
|
+
required,
|
|
2037
|
+
schema,
|
|
2038
|
+
title
|
|
2039
|
+
} = props;
|
|
2040
|
+
const uiOptions = utils.getUiOptions(uiSchema);
|
|
2041
|
+
const ArrayFieldDescriptionTemplate = utils.getTemplate("ArrayFieldDescriptionTemplate", registry, uiOptions);
|
|
2042
|
+
const ArrayFieldItemTemplate = utils.getTemplate("ArrayFieldItemTemplate", registry, uiOptions);
|
|
2043
|
+
const ArrayFieldTitleTemplate = utils.getTemplate("ArrayFieldTitleTemplate", registry, uiOptions); // Button templates are not overridden in the uiSchema
|
|
2044
|
+
|
|
2045
|
+
const {
|
|
2046
|
+
ButtonTemplates: {
|
|
2047
|
+
AddButton
|
|
2048
|
+
}
|
|
2049
|
+
} = registry.templates;
|
|
2050
|
+
return /*#__PURE__*/React__default["default"].createElement("fieldset", {
|
|
2051
|
+
className: className,
|
|
2052
|
+
id: idSchema.$id
|
|
2053
|
+
}, /*#__PURE__*/React__default["default"].createElement(ArrayFieldTitleTemplate, {
|
|
2054
|
+
idSchema: idSchema,
|
|
2055
|
+
title: uiOptions.title || title,
|
|
2056
|
+
required: required,
|
|
2057
|
+
uiSchema: uiSchema,
|
|
2058
|
+
registry: registry
|
|
2059
|
+
}), (uiOptions.description || schema.description) && /*#__PURE__*/React__default["default"].createElement(ArrayFieldDescriptionTemplate, {
|
|
2060
|
+
idSchema: idSchema,
|
|
2061
|
+
description: uiOptions.description || schema.description,
|
|
2062
|
+
uiSchema: uiSchema,
|
|
2063
|
+
registry: registry
|
|
2064
|
+
}), /*#__PURE__*/React__default["default"].createElement("div", {
|
|
2065
|
+
className: "row array-item-list"
|
|
2066
|
+
}, items && items.map(_ref => {
|
|
2067
|
+
let {
|
|
2068
|
+
key,
|
|
2069
|
+
...itemProps
|
|
2070
|
+
} = _ref;
|
|
2071
|
+
return /*#__PURE__*/React__default["default"].createElement(ArrayFieldItemTemplate, {
|
|
2072
|
+
key: key,
|
|
2073
|
+
...itemProps
|
|
2074
|
+
});
|
|
2075
|
+
})), canAdd && /*#__PURE__*/React__default["default"].createElement(AddButton, {
|
|
2076
|
+
className: "array-item-add",
|
|
2077
|
+
onClick: onAddClick,
|
|
2078
|
+
disabled: disabled || readonly
|
|
2079
|
+
}));
|
|
2080
|
+
}
|
|
2081
|
+
|
|
2082
|
+
/** The `ArrayFieldTitleTemplate` component renders a `TitleFieldTemplate` with an `id` derived from
|
|
2083
|
+
* the `idSchema`.
|
|
2084
|
+
*
|
|
2085
|
+
* @param props - The `ArrayFieldTitleProps` for the component
|
|
2086
|
+
*/
|
|
2087
|
+
|
|
2088
|
+
function ArrayFieldTitleTemplate(props) {
|
|
2089
|
+
const {
|
|
2090
|
+
idSchema,
|
|
2091
|
+
title,
|
|
2092
|
+
uiSchema,
|
|
2093
|
+
required,
|
|
2094
|
+
registry
|
|
2095
|
+
} = props;
|
|
2096
|
+
|
|
2097
|
+
if (!title) {
|
|
2098
|
+
return null;
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
const options = utils.getUiOptions(uiSchema);
|
|
2102
|
+
const TitleFieldTemplate = utils.getTemplate("TitleFieldTemplate", registry, options);
|
|
2103
|
+
const id = idSchema.$id + "__title";
|
|
2104
|
+
return /*#__PURE__*/React__default["default"].createElement(TitleFieldTemplate, {
|
|
2105
|
+
id: id,
|
|
2106
|
+
title: title,
|
|
2107
|
+
required: required,
|
|
2108
|
+
uiSchema: uiSchema,
|
|
2109
|
+
registry: registry
|
|
2110
|
+
});
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2113
|
+
/** The `BaseInputTemplate` is the template to use to render the basic `<input>` component for the `core` theme.
|
|
2114
|
+
* It is used as the template for rendering many of the <input> based widgets that differ by `type` and callbacks only.
|
|
2115
|
+
* It can be customized/overridden for other themes or individual implementations as needed.
|
|
2116
|
+
*
|
|
2117
|
+
* @param props - The `WidgetProps` for this template
|
|
2118
|
+
*/
|
|
2119
|
+
|
|
2120
|
+
function BaseInputTemplate(props) {
|
|
2121
|
+
const {
|
|
2122
|
+
id,
|
|
2123
|
+
value,
|
|
2124
|
+
readonly,
|
|
2125
|
+
disabled,
|
|
2126
|
+
autofocus,
|
|
2127
|
+
onBlur,
|
|
2128
|
+
onFocus,
|
|
2129
|
+
onChange,
|
|
2130
|
+
options,
|
|
2131
|
+
schema,
|
|
2132
|
+
uiSchema,
|
|
2133
|
+
formContext,
|
|
2134
|
+
registry,
|
|
2135
|
+
rawErrors,
|
|
2136
|
+
type,
|
|
2137
|
+
...rest
|
|
2138
|
+
} = props; // Note: since React 15.2.0 we can't forward unknown element attributes, so we
|
|
2139
|
+
// exclude the "options" and "schema" ones here.
|
|
2140
|
+
|
|
2141
|
+
if (!id) {
|
|
2142
|
+
console.log("No id for", props);
|
|
2143
|
+
throw new Error("no id for props " + JSON.stringify(props));
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
const inputProps = { ...rest,
|
|
2147
|
+
...utils.getInputProps(schema, type, options)
|
|
2148
|
+
};
|
|
2149
|
+
let inputValue;
|
|
2150
|
+
|
|
2151
|
+
if (inputProps.type === "number" || inputProps.type === "integer") {
|
|
2152
|
+
inputValue = value || value === 0 ? value : "";
|
|
2153
|
+
} else {
|
|
2154
|
+
inputValue = value == null ? "" : value;
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
const _onChange = React.useCallback(_ref => {
|
|
2158
|
+
let {
|
|
2159
|
+
target: {
|
|
2160
|
+
value
|
|
2161
|
+
}
|
|
2162
|
+
} = _ref;
|
|
2163
|
+
return onChange(value === "" ? options.emptyValue : value);
|
|
2164
|
+
}, [onChange, options]);
|
|
2165
|
+
|
|
2166
|
+
const _onBlur = React.useCallback(_ref2 => {
|
|
2167
|
+
let {
|
|
2168
|
+
target: {
|
|
2169
|
+
value
|
|
2170
|
+
}
|
|
2171
|
+
} = _ref2;
|
|
2172
|
+
return onBlur(id, value);
|
|
2173
|
+
}, [onBlur, id]);
|
|
2174
|
+
|
|
2175
|
+
const _onFocus = React.useCallback(_ref3 => {
|
|
2176
|
+
let {
|
|
2177
|
+
target: {
|
|
2178
|
+
value
|
|
2179
|
+
}
|
|
2180
|
+
} = _ref3;
|
|
2181
|
+
return onFocus(id, value);
|
|
2182
|
+
}, [onFocus, id]);
|
|
2183
|
+
|
|
2184
|
+
return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement("input", {
|
|
2185
|
+
key: id,
|
|
2186
|
+
id: id,
|
|
2187
|
+
className: "form-control",
|
|
2188
|
+
readOnly: readonly,
|
|
2189
|
+
disabled: disabled,
|
|
2190
|
+
autoFocus: autofocus,
|
|
2191
|
+
value: inputValue,
|
|
2192
|
+
...inputProps,
|
|
2193
|
+
list: schema.examples ? "examples_" + id : undefined,
|
|
2194
|
+
onChange: _onChange,
|
|
2195
|
+
onBlur: _onBlur,
|
|
2196
|
+
onFocus: _onFocus
|
|
2197
|
+
}), Array.isArray(schema.examples) && /*#__PURE__*/React__default["default"].createElement("datalist", {
|
|
2198
|
+
key: "datalist_" + id,
|
|
2199
|
+
id: "examples_" + id
|
|
2200
|
+
}, [...new Set(schema.examples.concat(schema.default ? [schema.default] : []))].map(example => /*#__PURE__*/React__default["default"].createElement("option", {
|
|
2201
|
+
key: example,
|
|
2202
|
+
value: example
|
|
2203
|
+
}))));
|
|
2204
|
+
}
|
|
2205
|
+
|
|
2206
|
+
/** The `SubmitButton` renders a button that represent the `Submit` action on a form
|
|
2207
|
+
*/
|
|
2208
|
+
|
|
2209
|
+
function SubmitButton(_ref) {
|
|
2210
|
+
let {
|
|
2211
|
+
uiSchema
|
|
2212
|
+
} = _ref;
|
|
2213
|
+
const {
|
|
2214
|
+
submitText,
|
|
2215
|
+
norender,
|
|
2216
|
+
props: submitButtonProps = {}
|
|
2217
|
+
} = utils.getSubmitButtonOptions(uiSchema);
|
|
2218
|
+
|
|
2219
|
+
if (norender) {
|
|
2220
|
+
return null;
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
return /*#__PURE__*/React__default["default"].createElement("div", null, /*#__PURE__*/React__default["default"].createElement("button", {
|
|
2224
|
+
type: "submit",
|
|
2225
|
+
...submitButtonProps,
|
|
2226
|
+
className: "btn btn-info " + submitButtonProps.className
|
|
2227
|
+
}, submitText));
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
function IconButton(props) {
|
|
2231
|
+
const {
|
|
2232
|
+
iconType = "default",
|
|
2233
|
+
icon,
|
|
2234
|
+
className,
|
|
2235
|
+
...otherProps
|
|
2236
|
+
} = props;
|
|
2237
|
+
return /*#__PURE__*/React__default["default"].createElement("button", {
|
|
2238
|
+
type: "button",
|
|
2239
|
+
className: "btn btn-" + iconType + " " + className,
|
|
2240
|
+
...otherProps
|
|
2241
|
+
}, /*#__PURE__*/React__default["default"].createElement("i", {
|
|
2242
|
+
className: "glyphicon glyphicon-" + icon
|
|
2243
|
+
}));
|
|
2244
|
+
}
|
|
2245
|
+
function MoveDownButton(props) {
|
|
2246
|
+
return /*#__PURE__*/React__default["default"].createElement(IconButton, {
|
|
2247
|
+
title: "Move down",
|
|
2248
|
+
className: "array-item-move-down",
|
|
2249
|
+
...props,
|
|
2250
|
+
icon: "arrow-down"
|
|
2251
|
+
});
|
|
2252
|
+
}
|
|
2253
|
+
function MoveUpButton(props) {
|
|
2254
|
+
return /*#__PURE__*/React__default["default"].createElement(IconButton, {
|
|
2255
|
+
title: "Move up",
|
|
2256
|
+
className: "array-item-move-up",
|
|
2257
|
+
...props,
|
|
2258
|
+
icon: "arrow-up"
|
|
2259
|
+
});
|
|
2260
|
+
}
|
|
2261
|
+
function RemoveButton(props) {
|
|
2262
|
+
return /*#__PURE__*/React__default["default"].createElement(IconButton, {
|
|
2263
|
+
title: "Remove",
|
|
2264
|
+
className: "array-item-remove",
|
|
2265
|
+
...props,
|
|
2266
|
+
iconType: "danger",
|
|
2267
|
+
icon: "remove"
|
|
2268
|
+
});
|
|
2269
|
+
}
|
|
2270
|
+
|
|
2271
|
+
/** The `AddButton` renders a button that represent the `Add` action on a form
|
|
2272
|
+
*/
|
|
2273
|
+
|
|
2274
|
+
function AddButton(_ref) {
|
|
2275
|
+
let {
|
|
2276
|
+
className,
|
|
2277
|
+
onClick,
|
|
2278
|
+
disabled
|
|
2279
|
+
} = _ref;
|
|
2280
|
+
return /*#__PURE__*/React__default["default"].createElement("div", {
|
|
2281
|
+
className: "row"
|
|
2282
|
+
}, /*#__PURE__*/React__default["default"].createElement("p", {
|
|
2283
|
+
className: "col-xs-3 col-xs-offset-9 text-right " + className
|
|
2284
|
+
}, /*#__PURE__*/React__default["default"].createElement(IconButton, {
|
|
2285
|
+
iconType: "info",
|
|
2286
|
+
icon: "plus",
|
|
2287
|
+
className: "btn-add col-xs-12",
|
|
2288
|
+
title: "Add",
|
|
2289
|
+
onClick: onClick,
|
|
2290
|
+
disabled: disabled
|
|
2291
|
+
})));
|
|
2292
|
+
}
|
|
2293
|
+
|
|
2294
|
+
const buttonTemplates = {
|
|
2295
|
+
SubmitButton,
|
|
2296
|
+
AddButton,
|
|
2297
|
+
MoveDownButton,
|
|
2298
|
+
MoveUpButton,
|
|
2299
|
+
RemoveButton
|
|
2300
|
+
};
|
|
2301
|
+
|
|
2302
|
+
/** The `DescriptionField` is the template to use to render the description of a field
|
|
2303
|
+
*
|
|
2304
|
+
* @param props - The `DescriptionFieldProps` for this component
|
|
2305
|
+
*/
|
|
2306
|
+
|
|
2307
|
+
function DescriptionField(props) {
|
|
2308
|
+
const {
|
|
2309
|
+
id,
|
|
2310
|
+
description
|
|
2311
|
+
} = props;
|
|
2312
|
+
|
|
2313
|
+
if (!description) {
|
|
2314
|
+
return null;
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
if (typeof description === "string") {
|
|
2318
|
+
return /*#__PURE__*/React__default["default"].createElement("p", {
|
|
2319
|
+
id: id,
|
|
2320
|
+
className: "field-description"
|
|
2321
|
+
}, description);
|
|
2322
|
+
} else {
|
|
2323
|
+
return /*#__PURE__*/React__default["default"].createElement("div", {
|
|
2324
|
+
id: id,
|
|
2325
|
+
className: "field-description"
|
|
2326
|
+
}, description);
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
/** The `ErrorList` component is the template that renders the all the errors associated with the fields in the `Form`
|
|
2331
|
+
*
|
|
2332
|
+
* @param props - The `ErrorListProps` for this component
|
|
2333
|
+
*/
|
|
2334
|
+
|
|
2335
|
+
function ErrorList(_ref) {
|
|
2336
|
+
let {
|
|
2337
|
+
errors
|
|
2338
|
+
} = _ref;
|
|
2339
|
+
return /*#__PURE__*/React__default["default"].createElement("div", {
|
|
2340
|
+
className: "panel panel-danger errors"
|
|
2341
|
+
}, /*#__PURE__*/React__default["default"].createElement("div", {
|
|
2342
|
+
className: "panel-heading"
|
|
2343
|
+
}, /*#__PURE__*/React__default["default"].createElement("h3", {
|
|
2344
|
+
className: "panel-title"
|
|
2345
|
+
}, "Errors")), /*#__PURE__*/React__default["default"].createElement("ul", {
|
|
2346
|
+
className: "list-group"
|
|
2347
|
+
}, errors.map((error, i) => {
|
|
2348
|
+
return /*#__PURE__*/React__default["default"].createElement("li", {
|
|
2349
|
+
key: i,
|
|
2350
|
+
className: "list-group-item text-danger"
|
|
2351
|
+
}, error.stack);
|
|
2352
|
+
})));
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
const REQUIRED_FIELD_SYMBOL$1 = "*";
|
|
2356
|
+
/** Renders a label for a field
|
|
2357
|
+
*
|
|
2358
|
+
* @param props - The `LabelProps` for this component
|
|
2359
|
+
*/
|
|
2360
|
+
|
|
2361
|
+
function Label(props) {
|
|
2362
|
+
const {
|
|
2363
|
+
label,
|
|
2364
|
+
required,
|
|
2365
|
+
id
|
|
2366
|
+
} = props;
|
|
2367
|
+
|
|
2368
|
+
if (!label) {
|
|
2369
|
+
return null;
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2372
|
+
return /*#__PURE__*/React__default["default"].createElement("label", {
|
|
2373
|
+
className: "control-label",
|
|
2374
|
+
htmlFor: id
|
|
2375
|
+
}, label, required && /*#__PURE__*/React__default["default"].createElement("span", {
|
|
2376
|
+
className: "required"
|
|
2377
|
+
}, REQUIRED_FIELD_SYMBOL$1));
|
|
2378
|
+
}
|
|
2379
|
+
|
|
2380
|
+
/** The `WrapIfAdditional` component is used by the `FieldTemplate` to rename, or remove properties that are
|
|
2381
|
+
* part of an `additionalProperties` part of a schema.
|
|
2382
|
+
*
|
|
2383
|
+
* @param props - The `WrapIfAdditionalProps` for this component
|
|
2384
|
+
*/
|
|
2385
|
+
|
|
2386
|
+
function WrapIfAdditional(props) {
|
|
2387
|
+
const {
|
|
2388
|
+
id,
|
|
2389
|
+
classNames,
|
|
2390
|
+
disabled,
|
|
2391
|
+
label,
|
|
2392
|
+
onKeyChange,
|
|
2393
|
+
onDropPropertyClick,
|
|
2394
|
+
readonly,
|
|
2395
|
+
required,
|
|
2396
|
+
schema,
|
|
2397
|
+
children,
|
|
2398
|
+
registry
|
|
2399
|
+
} = props;
|
|
2400
|
+
const {
|
|
2401
|
+
RemoveButton
|
|
2402
|
+
} = registry.templates.ButtonTemplates;
|
|
2403
|
+
const keyLabel = label + " Key"; // i18n ?
|
|
2404
|
+
|
|
2405
|
+
const additional = (utils.ADDITIONAL_PROPERTY_FLAG in schema);
|
|
2406
|
+
|
|
2407
|
+
if (!additional) {
|
|
2408
|
+
return /*#__PURE__*/React__default["default"].createElement("div", {
|
|
2409
|
+
className: classNames
|
|
2410
|
+
}, children);
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2413
|
+
return /*#__PURE__*/React__default["default"].createElement("div", {
|
|
2414
|
+
className: classNames
|
|
2415
|
+
}, /*#__PURE__*/React__default["default"].createElement("div", {
|
|
2416
|
+
className: "row"
|
|
2417
|
+
}, /*#__PURE__*/React__default["default"].createElement("div", {
|
|
2418
|
+
className: "col-xs-5 form-additional"
|
|
2419
|
+
}, /*#__PURE__*/React__default["default"].createElement("div", {
|
|
2420
|
+
className: "form-group"
|
|
2421
|
+
}, /*#__PURE__*/React__default["default"].createElement(Label, {
|
|
2422
|
+
label: keyLabel,
|
|
2423
|
+
required: required,
|
|
2424
|
+
id: id + "-key"
|
|
2425
|
+
}), /*#__PURE__*/React__default["default"].createElement("input", {
|
|
2426
|
+
className: "form-control",
|
|
2427
|
+
type: "text",
|
|
2428
|
+
id: id + "-key",
|
|
2429
|
+
onBlur: event => onKeyChange(event.target.value),
|
|
2430
|
+
defaultValue: label
|
|
2431
|
+
}))), /*#__PURE__*/React__default["default"].createElement("div", {
|
|
2432
|
+
className: "form-additional form-group col-xs-5"
|
|
2433
|
+
}, children), /*#__PURE__*/React__default["default"].createElement("div", {
|
|
2434
|
+
className: "col-xs-2"
|
|
2435
|
+
}, /*#__PURE__*/React__default["default"].createElement(RemoveButton, {
|
|
2436
|
+
className: "array-item-remove btn-block",
|
|
2437
|
+
style: {
|
|
2438
|
+
border: "0"
|
|
2439
|
+
},
|
|
2440
|
+
disabled: disabled || readonly,
|
|
2441
|
+
onClick: onDropPropertyClick(label)
|
|
2442
|
+
}))));
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2445
|
+
/** The `FieldTemplate` component is the template used by `SchemaField` to render any field. It renders the field
|
|
2446
|
+
* content, (label, description, children, errors and help) inside of a `WrapIfAdditional` component.
|
|
2447
|
+
*
|
|
2448
|
+
* @param props - The `FieldTemplateProps` for this component
|
|
2449
|
+
*/
|
|
2450
|
+
|
|
2451
|
+
function FieldTemplate(props) {
|
|
2452
|
+
const {
|
|
2453
|
+
id,
|
|
2454
|
+
label,
|
|
2455
|
+
children,
|
|
2456
|
+
errors,
|
|
2457
|
+
help,
|
|
2458
|
+
description,
|
|
2459
|
+
hidden,
|
|
2460
|
+
required,
|
|
2461
|
+
displayLabel
|
|
2462
|
+
} = props;
|
|
2463
|
+
|
|
2464
|
+
if (hidden) {
|
|
2465
|
+
return /*#__PURE__*/React__default["default"].createElement("div", {
|
|
2466
|
+
className: "hidden"
|
|
2467
|
+
}, children);
|
|
2468
|
+
}
|
|
2469
|
+
|
|
2470
|
+
return /*#__PURE__*/React__default["default"].createElement(WrapIfAdditional, { ...props
|
|
2471
|
+
}, displayLabel && /*#__PURE__*/React__default["default"].createElement(Label, {
|
|
2472
|
+
label: label,
|
|
2473
|
+
required: required,
|
|
2474
|
+
id: id
|
|
2475
|
+
}), displayLabel && description ? description : null, children, errors, help);
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
/** The `ObjectFieldTemplate` is the template to use to render all the inner properties of an object along with the
|
|
2479
|
+
* title and description if available. If the object is expandable, then an `AddButton` is also rendered after all
|
|
2480
|
+
* the properties.
|
|
2481
|
+
*
|
|
2482
|
+
* @param props - The `ObjectFieldTemplateProps` for this component
|
|
2483
|
+
*/
|
|
2484
|
+
|
|
2485
|
+
function ObjectFieldTemplate(props) {
|
|
2486
|
+
const {
|
|
2487
|
+
description,
|
|
2488
|
+
disabled,
|
|
2489
|
+
formData,
|
|
2490
|
+
idSchema,
|
|
2491
|
+
onAddClick,
|
|
2492
|
+
properties,
|
|
2493
|
+
readonly,
|
|
2494
|
+
registry,
|
|
2495
|
+
required,
|
|
2496
|
+
schema,
|
|
2497
|
+
title,
|
|
2498
|
+
uiSchema
|
|
2499
|
+
} = props;
|
|
2500
|
+
const options = utils.getUiOptions(uiSchema);
|
|
2501
|
+
const TitleFieldTemplate = utils.getTemplate("TitleFieldTemplate", registry, options);
|
|
2502
|
+
const DescriptionFieldTemplate = utils.getTemplate("DescriptionFieldTemplate", registry, options); // Button templates are not overridden in the uiSchema
|
|
2503
|
+
|
|
2504
|
+
const {
|
|
2505
|
+
ButtonTemplates: {
|
|
2506
|
+
AddButton
|
|
2507
|
+
}
|
|
2508
|
+
} = registry.templates;
|
|
2509
|
+
return /*#__PURE__*/React__default["default"].createElement("fieldset", {
|
|
2510
|
+
id: idSchema.$id
|
|
2511
|
+
}, (options.title || title) && /*#__PURE__*/React__default["default"].createElement(TitleFieldTemplate, {
|
|
2512
|
+
id: idSchema.$id + "__title",
|
|
2513
|
+
title: options.title || title,
|
|
2514
|
+
required: required,
|
|
2515
|
+
uiSchema: uiSchema,
|
|
2516
|
+
registry: registry
|
|
2517
|
+
}), (options.description || description) && /*#__PURE__*/React__default["default"].createElement(DescriptionFieldTemplate, {
|
|
2518
|
+
id: idSchema.$id + "__description",
|
|
2519
|
+
description: options.description || description,
|
|
2520
|
+
registry: registry
|
|
2521
|
+
}), properties.map(prop => prop.content), utils.canExpand(schema, uiSchema, formData) && /*#__PURE__*/React__default["default"].createElement(AddButton, {
|
|
2522
|
+
className: "object-property-expand",
|
|
2523
|
+
onClick: onAddClick(schema),
|
|
2524
|
+
disabled: disabled || readonly
|
|
2525
|
+
}));
|
|
2526
|
+
}
|
|
2527
|
+
|
|
2528
|
+
const REQUIRED_FIELD_SYMBOL = "*";
|
|
2529
|
+
/** The `TitleField` is the template to use to render the title of a field
|
|
2530
|
+
*
|
|
2531
|
+
* @param props - The `TitleFieldProps` for this component
|
|
2532
|
+
*/
|
|
2533
|
+
|
|
2534
|
+
function TitleField(props) {
|
|
2535
|
+
const {
|
|
2536
|
+
id,
|
|
2537
|
+
title,
|
|
2538
|
+
required
|
|
2539
|
+
} = props;
|
|
2540
|
+
return /*#__PURE__*/React__default["default"].createElement("legend", {
|
|
2541
|
+
id: id
|
|
2542
|
+
}, title, required && /*#__PURE__*/React__default["default"].createElement("span", {
|
|
2543
|
+
className: "required"
|
|
2544
|
+
}, REQUIRED_FIELD_SYMBOL));
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2547
|
+
/** The `UnsupportedField` component is used to render a field in the schema is one that is not supported by
|
|
2548
|
+
* react-jsonschema-form.
|
|
2549
|
+
*
|
|
2550
|
+
* @param props - The `FieldProps` for this template
|
|
2551
|
+
*/
|
|
2552
|
+
|
|
2553
|
+
function UnsupportedField(props) {
|
|
2554
|
+
const {
|
|
2555
|
+
schema,
|
|
2556
|
+
idSchema,
|
|
2557
|
+
reason
|
|
2558
|
+
} = props;
|
|
2559
|
+
return /*#__PURE__*/React__default["default"].createElement("div", {
|
|
2560
|
+
className: "unsupported-field"
|
|
2561
|
+
}, /*#__PURE__*/React__default["default"].createElement("p", null, "Unsupported field schema", idSchema && idSchema.$id && /*#__PURE__*/React__default["default"].createElement("span", null, " for", " field ", /*#__PURE__*/React__default["default"].createElement("code", null, idSchema.$id)), reason && /*#__PURE__*/React__default["default"].createElement("em", null, ": ", reason), "."), schema && /*#__PURE__*/React__default["default"].createElement("pre", null, JSON.stringify(schema, null, 2)));
|
|
2562
|
+
}
|
|
2563
|
+
|
|
2564
|
+
const templates = {
|
|
2565
|
+
ArrayFieldDescriptionTemplate,
|
|
2566
|
+
ArrayFieldItemTemplate,
|
|
2567
|
+
ArrayFieldTemplate,
|
|
2568
|
+
ArrayFieldTitleTemplate,
|
|
2569
|
+
ButtonTemplates: buttonTemplates,
|
|
2570
|
+
BaseInputTemplate,
|
|
2571
|
+
DescriptionFieldTemplate: DescriptionField,
|
|
2572
|
+
ErrorListTemplate: ErrorList,
|
|
2573
|
+
FieldTemplate,
|
|
2574
|
+
ObjectFieldTemplate,
|
|
2575
|
+
TitleFieldTemplate: TitleField,
|
|
2576
|
+
UnsupportedFieldTemplate: UnsupportedField
|
|
2577
|
+
};
|
|
2578
|
+
|
|
2579
|
+
function rangeOptions(start, stop) {
|
|
2580
|
+
const options = [];
|
|
2581
|
+
|
|
2582
|
+
for (let i = start; i <= stop; i++) {
|
|
2583
|
+
options.push({
|
|
2584
|
+
value: i,
|
|
2585
|
+
label: utils.pad(i, 2)
|
|
2586
|
+
});
|
|
2587
|
+
}
|
|
2588
|
+
|
|
2589
|
+
return options;
|
|
2590
|
+
}
|
|
2591
|
+
|
|
2592
|
+
function readyForChange(state) {
|
|
2593
|
+
return Object.values(state).every(value => value !== -1);
|
|
2594
|
+
}
|
|
2595
|
+
|
|
2596
|
+
function dateElementProps(state, time, yearsRange) {
|
|
2597
|
+
if (yearsRange === void 0) {
|
|
2598
|
+
yearsRange = [1900, new Date().getFullYear() + 2];
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
const {
|
|
2602
|
+
year,
|
|
2603
|
+
month,
|
|
2604
|
+
day,
|
|
2605
|
+
hour,
|
|
2606
|
+
minute,
|
|
2607
|
+
second
|
|
2608
|
+
} = state;
|
|
2609
|
+
const data = [{
|
|
2610
|
+
type: "year",
|
|
2611
|
+
range: yearsRange,
|
|
2612
|
+
value: year
|
|
2613
|
+
}, {
|
|
2614
|
+
type: "month",
|
|
2615
|
+
range: [1, 12],
|
|
2616
|
+
value: month
|
|
2617
|
+
}, {
|
|
2618
|
+
type: "day",
|
|
2619
|
+
range: [1, 31],
|
|
2620
|
+
value: day
|
|
2621
|
+
}];
|
|
2622
|
+
|
|
2623
|
+
if (time) {
|
|
2624
|
+
data.push({
|
|
2625
|
+
type: "hour",
|
|
2626
|
+
range: [0, 23],
|
|
2627
|
+
value: hour
|
|
2628
|
+
}, {
|
|
2629
|
+
type: "minute",
|
|
2630
|
+
range: [0, 59],
|
|
2631
|
+
value: minute
|
|
2632
|
+
}, {
|
|
2633
|
+
type: "second",
|
|
2634
|
+
range: [0, 59],
|
|
2635
|
+
value: second
|
|
2636
|
+
});
|
|
2637
|
+
}
|
|
2638
|
+
|
|
2639
|
+
return data;
|
|
2640
|
+
}
|
|
2641
|
+
|
|
2642
|
+
function DateElement(_ref) {
|
|
2643
|
+
let {
|
|
2644
|
+
type,
|
|
2645
|
+
range,
|
|
2646
|
+
value,
|
|
2647
|
+
select,
|
|
2648
|
+
rootId,
|
|
2649
|
+
disabled,
|
|
2650
|
+
readonly,
|
|
2651
|
+
autofocus,
|
|
2652
|
+
registry,
|
|
2653
|
+
onBlur,
|
|
2654
|
+
onFocus
|
|
2655
|
+
} = _ref;
|
|
2656
|
+
const id = rootId + "_" + type;
|
|
2657
|
+
const {
|
|
2658
|
+
SelectWidget
|
|
2659
|
+
} = registry.widgets;
|
|
2660
|
+
return /*#__PURE__*/React__default["default"].createElement(SelectWidget, {
|
|
2661
|
+
schema: {
|
|
2662
|
+
type: "integer"
|
|
2663
|
+
},
|
|
2664
|
+
id: id,
|
|
2665
|
+
className: "form-control",
|
|
2666
|
+
options: {
|
|
2667
|
+
enumOptions: rangeOptions(range[0], range[1])
|
|
2668
|
+
},
|
|
2669
|
+
placeholder: type,
|
|
2670
|
+
value: value,
|
|
2671
|
+
disabled: disabled,
|
|
2672
|
+
readonly: readonly,
|
|
2673
|
+
autofocus: autofocus,
|
|
2674
|
+
onChange: value => select(type, value),
|
|
2675
|
+
onBlur: onBlur,
|
|
2676
|
+
onFocus: onFocus,
|
|
2677
|
+
registry: registry,
|
|
2678
|
+
label: ""
|
|
2679
|
+
});
|
|
2680
|
+
}
|
|
2681
|
+
/** The `AltDateWidget` is an alternative widget for rendering date properties.
|
|
2682
|
+
* @param props - The `WidgetProps` for this component
|
|
2683
|
+
*/
|
|
2684
|
+
|
|
2685
|
+
|
|
2686
|
+
function AltDateWidget(_ref2) {
|
|
2687
|
+
let {
|
|
2688
|
+
time = false,
|
|
2689
|
+
disabled = false,
|
|
2690
|
+
readonly = false,
|
|
2691
|
+
autofocus = false,
|
|
2692
|
+
options,
|
|
2693
|
+
id,
|
|
2694
|
+
registry,
|
|
2695
|
+
onBlur,
|
|
2696
|
+
onFocus,
|
|
2697
|
+
onChange,
|
|
2698
|
+
value
|
|
2699
|
+
} = _ref2;
|
|
2700
|
+
const [state, setState] = React.useReducer((state, action) => {
|
|
2701
|
+
return { ...state,
|
|
2702
|
+
...action
|
|
2703
|
+
};
|
|
2704
|
+
}, utils.parseDateString(value, time));
|
|
2705
|
+
React.useEffect(() => {
|
|
2706
|
+
if (value && value !== utils.toDateString(state, time)) {
|
|
2707
|
+
setState(utils.parseDateString(value, time));
|
|
2708
|
+
}
|
|
2709
|
+
}, [value, state, time]);
|
|
2710
|
+
React.useEffect(() => {
|
|
2711
|
+
if (readyForChange(state)) {
|
|
2712
|
+
// Only propagate to parent state if we have a complete date{time}
|
|
2713
|
+
onChange(utils.toDateString(state, time));
|
|
2714
|
+
}
|
|
2715
|
+
}, [state, time, onChange]);
|
|
2716
|
+
const handleChange = React.useCallback((property, value) => {
|
|
2717
|
+
setState({
|
|
2718
|
+
[property]: value
|
|
2719
|
+
});
|
|
2720
|
+
}, []);
|
|
2721
|
+
const handleSetNow = React.useCallback(event => {
|
|
2722
|
+
event.preventDefault();
|
|
2723
|
+
|
|
2724
|
+
if (disabled || readonly) {
|
|
2725
|
+
return;
|
|
2726
|
+
}
|
|
2727
|
+
|
|
2728
|
+
const nowDateObj = utils.parseDateString(new Date().toJSON(), time);
|
|
2729
|
+
setState(nowDateObj);
|
|
2730
|
+
}, [disabled, readonly, time]);
|
|
2731
|
+
const handleClear = React.useCallback(event => {
|
|
2732
|
+
event.preventDefault();
|
|
2733
|
+
|
|
2734
|
+
if (disabled || readonly) {
|
|
2735
|
+
return;
|
|
2736
|
+
}
|
|
2737
|
+
|
|
2738
|
+
setState(utils.parseDateString("", time));
|
|
2739
|
+
onChange(undefined);
|
|
2740
|
+
}, [disabled, readonly, time, onChange]);
|
|
2741
|
+
return /*#__PURE__*/React__default["default"].createElement("ul", {
|
|
2742
|
+
className: "list-inline"
|
|
2743
|
+
}, dateElementProps(state, time, options.yearsRange).map((elemProps, i) => /*#__PURE__*/React__default["default"].createElement("li", {
|
|
2744
|
+
key: i
|
|
2745
|
+
}, /*#__PURE__*/React__default["default"].createElement(DateElement, {
|
|
2746
|
+
rootId: id,
|
|
2747
|
+
select: handleChange,
|
|
2748
|
+
...elemProps,
|
|
2749
|
+
disabled: disabled,
|
|
2750
|
+
readonly: readonly,
|
|
2751
|
+
registry: registry,
|
|
2752
|
+
onBlur: onBlur,
|
|
2753
|
+
onFocus: onFocus,
|
|
2754
|
+
autofocus: autofocus && i === 0
|
|
2755
|
+
}))), (options.hideNowButton !== "undefined" ? !options.hideNowButton : true) && /*#__PURE__*/React__default["default"].createElement("li", null, /*#__PURE__*/React__default["default"].createElement("a", {
|
|
2756
|
+
href: "#",
|
|
2757
|
+
className: "btn btn-info btn-now",
|
|
2758
|
+
onClick: handleSetNow
|
|
2759
|
+
}, "Now")), (options.hideClearButton !== "undefined" ? !options.hideClearButton : true) && /*#__PURE__*/React__default["default"].createElement("li", null, /*#__PURE__*/React__default["default"].createElement("a", {
|
|
2760
|
+
href: "#",
|
|
2761
|
+
className: "btn btn-warning btn-clear",
|
|
2762
|
+
onClick: handleClear
|
|
2763
|
+
}, "Clear")));
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2766
|
+
/** The `AltDateTimeWidget` is an alternative widget for rendering datetime properties.
|
|
2767
|
+
* It uses the AltDateWidget for rendering, with the `time` prop set to true by default.
|
|
2768
|
+
*
|
|
2769
|
+
* @param props - The `WidgetProps` for this component
|
|
2770
|
+
*/
|
|
2771
|
+
|
|
2772
|
+
function AltDateTimeWidget(_ref) {
|
|
2773
|
+
let {
|
|
2774
|
+
time = true,
|
|
2775
|
+
...props
|
|
2776
|
+
} = _ref;
|
|
2777
|
+
const {
|
|
2778
|
+
AltDateWidget
|
|
2779
|
+
} = props.registry.widgets;
|
|
2780
|
+
return /*#__PURE__*/React__default["default"].createElement(AltDateWidget, {
|
|
2781
|
+
time: time,
|
|
2782
|
+
...props
|
|
2783
|
+
});
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2786
|
+
/** The `CheckBoxWidget` is a widget for rendering boolean properties.
|
|
2787
|
+
* It is typically used to represent a boolean.
|
|
2788
|
+
*
|
|
2789
|
+
* @param props - The `WidgetProps` for this component
|
|
2790
|
+
*/
|
|
2791
|
+
|
|
2792
|
+
function CheckboxWidget(_ref) {
|
|
2793
|
+
let {
|
|
2794
|
+
schema,
|
|
2795
|
+
options,
|
|
2796
|
+
id,
|
|
2797
|
+
value,
|
|
2798
|
+
disabled,
|
|
2799
|
+
readonly,
|
|
2800
|
+
label,
|
|
2801
|
+
autofocus = false,
|
|
2802
|
+
onBlur,
|
|
2803
|
+
onFocus,
|
|
2804
|
+
onChange,
|
|
2805
|
+
registry
|
|
2806
|
+
} = _ref;
|
|
2807
|
+
const DescriptionFieldTemplate = utils.getTemplate("DescriptionFieldTemplate", registry, options); // Because an unchecked checkbox will cause html5 validation to fail, only add
|
|
2808
|
+
// the "required" attribute if the field value must be "true", due to the
|
|
2809
|
+
// "const" or "enum" keywords
|
|
2810
|
+
|
|
2811
|
+
const required = utils.schemaRequiresTrueValue(schema);
|
|
2812
|
+
const handleChange = React.useCallback(event => onChange(event.target.checked), [onChange]);
|
|
2813
|
+
const handleBlur = React.useCallback(event => onBlur(id, event.target.checked), [onBlur, id]);
|
|
2814
|
+
const handleFocus = React.useCallback(event => onFocus(id, event.target.checked), [onFocus, id]);
|
|
2815
|
+
return /*#__PURE__*/React__default["default"].createElement("div", {
|
|
2816
|
+
className: "checkbox " + (disabled || readonly ? "disabled" : "")
|
|
2817
|
+
}, schema.description && /*#__PURE__*/React__default["default"].createElement(DescriptionFieldTemplate, {
|
|
2818
|
+
id: id + "__description",
|
|
2819
|
+
description: schema.description,
|
|
2820
|
+
registry: registry
|
|
2821
|
+
}), /*#__PURE__*/React__default["default"].createElement("label", null, /*#__PURE__*/React__default["default"].createElement("input", {
|
|
2822
|
+
type: "checkbox",
|
|
2823
|
+
id: id,
|
|
2824
|
+
checked: typeof value === "undefined" ? false : value,
|
|
2825
|
+
required: required,
|
|
2826
|
+
disabled: disabled || readonly,
|
|
2827
|
+
autoFocus: autofocus,
|
|
2828
|
+
onChange: handleChange,
|
|
2829
|
+
onBlur: handleBlur,
|
|
2830
|
+
onFocus: handleFocus
|
|
2831
|
+
}), /*#__PURE__*/React__default["default"].createElement("span", null, label)));
|
|
2832
|
+
}
|
|
2833
|
+
|
|
2834
|
+
function selectValue(value, selected, all) {
|
|
2835
|
+
const at = all.indexOf(value);
|
|
2836
|
+
const updated = selected.slice(0, at).concat(value, selected.slice(at)); // As inserting values at predefined index positions doesn't work with empty
|
|
2837
|
+
// arrays, we need to reorder the updated selection to match the initial order
|
|
2838
|
+
|
|
2839
|
+
return updated.sort((a, b) => Number(all.indexOf(a) > all.indexOf(b)));
|
|
2840
|
+
}
|
|
2841
|
+
|
|
2842
|
+
function deselectValue(value, selected) {
|
|
2843
|
+
return selected.filter(v => v !== value);
|
|
2844
|
+
}
|
|
2845
|
+
/** The `CheckboxesWidget` is a widget for rendering checkbox groups.
|
|
2846
|
+
* It is typically used to represent an array of enums.
|
|
2847
|
+
*
|
|
2848
|
+
* @param props - The `WidgetProps` for this component
|
|
2849
|
+
*/
|
|
2850
|
+
|
|
2851
|
+
|
|
2852
|
+
function CheckboxesWidget(_ref) {
|
|
2853
|
+
let {
|
|
2854
|
+
id,
|
|
2855
|
+
disabled,
|
|
2856
|
+
options: {
|
|
2857
|
+
inline = false,
|
|
2858
|
+
enumOptions,
|
|
2859
|
+
enumDisabled
|
|
2860
|
+
},
|
|
2861
|
+
value,
|
|
2862
|
+
autofocus = false,
|
|
2863
|
+
readonly,
|
|
2864
|
+
onChange
|
|
2865
|
+
} = _ref;
|
|
2866
|
+
return /*#__PURE__*/React__default["default"].createElement("div", {
|
|
2867
|
+
className: "checkboxes",
|
|
2868
|
+
id: id
|
|
2869
|
+
}, Array.isArray(enumOptions) && enumOptions.map((option, index) => {
|
|
2870
|
+
const checked = value.indexOf(option.value) !== -1;
|
|
2871
|
+
const itemDisabled = enumDisabled && enumDisabled.indexOf(option.value) != -1;
|
|
2872
|
+
const disabledCls = disabled || itemDisabled || readonly ? "disabled" : "";
|
|
2873
|
+
|
|
2874
|
+
const handleChange = event => {
|
|
2875
|
+
const all = enumOptions.map(_ref2 => {
|
|
2876
|
+
let {
|
|
2877
|
+
value
|
|
2878
|
+
} = _ref2;
|
|
2879
|
+
return value;
|
|
2880
|
+
});
|
|
2881
|
+
|
|
2882
|
+
if (event.target.checked) {
|
|
2883
|
+
onChange(selectValue(option.value, value, all));
|
|
2884
|
+
} else {
|
|
2885
|
+
onChange(deselectValue(option.value, value));
|
|
2886
|
+
}
|
|
2887
|
+
};
|
|
2888
|
+
|
|
2889
|
+
const checkbox = /*#__PURE__*/React__default["default"].createElement("span", null, /*#__PURE__*/React__default["default"].createElement("input", {
|
|
2890
|
+
type: "checkbox",
|
|
2891
|
+
id: id + "_" + index,
|
|
2892
|
+
checked: checked,
|
|
2893
|
+
disabled: disabled || itemDisabled || readonly,
|
|
2894
|
+
autoFocus: autofocus && index === 0,
|
|
2895
|
+
onChange: handleChange
|
|
2896
|
+
}), /*#__PURE__*/React__default["default"].createElement("span", null, option.label));
|
|
2897
|
+
return inline ? /*#__PURE__*/React__default["default"].createElement("label", {
|
|
2898
|
+
key: index,
|
|
2899
|
+
className: "checkbox-inline " + disabledCls
|
|
2900
|
+
}, checkbox) : /*#__PURE__*/React__default["default"].createElement("div", {
|
|
2901
|
+
key: index,
|
|
2902
|
+
className: "checkbox " + disabledCls
|
|
2903
|
+
}, /*#__PURE__*/React__default["default"].createElement("label", null, checkbox));
|
|
2904
|
+
}));
|
|
2905
|
+
}
|
|
2906
|
+
|
|
2907
|
+
/** The `ColorWidget` component uses the `BaseInputTemplate` changing the type to `color` and disables it when it is
|
|
2908
|
+
* either disabled or readonly.
|
|
2909
|
+
*
|
|
2910
|
+
* @param props - The `WidgetProps` for this component
|
|
2911
|
+
*/
|
|
2912
|
+
|
|
2913
|
+
function ColorWidget(props) {
|
|
2914
|
+
const {
|
|
2915
|
+
disabled,
|
|
2916
|
+
readonly,
|
|
2917
|
+
options,
|
|
2918
|
+
registry
|
|
2919
|
+
} = props;
|
|
2920
|
+
const BaseInputTemplate = utils.getTemplate("BaseInputTemplate", registry, options);
|
|
2921
|
+
return /*#__PURE__*/React__default["default"].createElement(BaseInputTemplate, {
|
|
2922
|
+
type: "color",
|
|
2923
|
+
...props,
|
|
2924
|
+
disabled: disabled || readonly
|
|
2925
|
+
});
|
|
2926
|
+
}
|
|
2927
|
+
|
|
2928
|
+
/** The `DateWidget` component uses the `BaseInputTemplate` changing the type to `date` and transforms
|
|
2929
|
+
* the value to undefined when it is falsy during the `onChange` handling.
|
|
2930
|
+
*
|
|
2931
|
+
* @param props - The `WidgetProps` for this component
|
|
2932
|
+
*/
|
|
2933
|
+
|
|
2934
|
+
function DateWidget(props) {
|
|
2935
|
+
const {
|
|
2936
|
+
onChange,
|
|
2937
|
+
options,
|
|
2938
|
+
registry
|
|
2939
|
+
} = props;
|
|
2940
|
+
const BaseInputTemplate = utils.getTemplate("BaseInputTemplate", registry, options);
|
|
2941
|
+
const handleChange = React.useCallback(value => onChange(value || undefined), [onChange]);
|
|
2942
|
+
return /*#__PURE__*/React__default["default"].createElement(BaseInputTemplate, {
|
|
2943
|
+
type: "date",
|
|
2944
|
+
...props,
|
|
2945
|
+
onChange: handleChange
|
|
2946
|
+
});
|
|
2947
|
+
}
|
|
2948
|
+
|
|
2949
|
+
/** The `DateTimeWidget` component uses the `BaseInputTemplate` changing the type to `datetime-local` and transforms
|
|
2950
|
+
* the value to/from utc using the appropriate utility functions.
|
|
2951
|
+
*
|
|
2952
|
+
* @param props - The `WidgetProps` for this component
|
|
2953
|
+
*/
|
|
2954
|
+
|
|
2955
|
+
function DateTimeWidget(props) {
|
|
2956
|
+
const {
|
|
2957
|
+
onChange,
|
|
2958
|
+
value,
|
|
2959
|
+
options,
|
|
2960
|
+
registry
|
|
2961
|
+
} = props;
|
|
2962
|
+
const BaseInputTemplate = utils.getTemplate("BaseInputTemplate", registry, options);
|
|
2963
|
+
return /*#__PURE__*/React__default["default"].createElement(BaseInputTemplate, {
|
|
2964
|
+
type: "datetime-local",
|
|
2965
|
+
...props,
|
|
2966
|
+
value: utils.utcToLocal(value),
|
|
2967
|
+
onChange: value => onChange(utils.localToUTC(value))
|
|
2968
|
+
});
|
|
2969
|
+
}
|
|
2970
|
+
|
|
2971
|
+
/** The `EmailWidget` component uses the `BaseInputTemplate` changing the type to `email`.
|
|
2972
|
+
*
|
|
2973
|
+
* @param props - The `WidgetProps` for this component
|
|
2974
|
+
*/
|
|
2975
|
+
|
|
2976
|
+
function EmailWidget(props) {
|
|
2977
|
+
const {
|
|
2978
|
+
options,
|
|
2979
|
+
registry
|
|
2980
|
+
} = props;
|
|
2981
|
+
const BaseInputTemplate = utils.getTemplate("BaseInputTemplate", registry, options);
|
|
2982
|
+
return /*#__PURE__*/React__default["default"].createElement(BaseInputTemplate, {
|
|
2983
|
+
type: "email",
|
|
2984
|
+
...props
|
|
2985
|
+
});
|
|
2986
|
+
}
|
|
2987
|
+
|
|
2988
|
+
function addNameToDataURL(dataURL, name) {
|
|
2989
|
+
if (dataURL === null) {
|
|
2990
|
+
return null;
|
|
2991
|
+
}
|
|
2992
|
+
|
|
2993
|
+
return dataURL.replace(";base64", ";name=" + encodeURIComponent(name) + ";base64");
|
|
2994
|
+
}
|
|
2995
|
+
|
|
2996
|
+
function processFile(file) {
|
|
2997
|
+
const {
|
|
2998
|
+
name,
|
|
2999
|
+
size,
|
|
3000
|
+
type
|
|
3001
|
+
} = file;
|
|
3002
|
+
return new Promise((resolve, reject) => {
|
|
3003
|
+
const reader = new window.FileReader();
|
|
3004
|
+
reader.onerror = reject;
|
|
3005
|
+
|
|
3006
|
+
reader.onload = event => {
|
|
3007
|
+
var _event$target;
|
|
3008
|
+
|
|
3009
|
+
if (typeof ((_event$target = event.target) === null || _event$target === void 0 ? void 0 : _event$target.result) === "string") {
|
|
3010
|
+
resolve({
|
|
3011
|
+
dataURL: addNameToDataURL(event.target.result, name),
|
|
3012
|
+
name,
|
|
3013
|
+
size,
|
|
3014
|
+
type
|
|
3015
|
+
});
|
|
3016
|
+
} else {
|
|
3017
|
+
resolve({
|
|
3018
|
+
dataURL: null,
|
|
3019
|
+
name,
|
|
3020
|
+
size,
|
|
3021
|
+
type
|
|
3022
|
+
});
|
|
3023
|
+
}
|
|
3024
|
+
};
|
|
3025
|
+
|
|
3026
|
+
reader.readAsDataURL(file);
|
|
3027
|
+
});
|
|
3028
|
+
}
|
|
3029
|
+
|
|
3030
|
+
function processFiles(files) {
|
|
3031
|
+
return Promise.all(Array.from(files).map(processFile));
|
|
3032
|
+
}
|
|
3033
|
+
|
|
3034
|
+
function FilesInfo(_ref) {
|
|
3035
|
+
let {
|
|
3036
|
+
filesInfo
|
|
3037
|
+
} = _ref;
|
|
3038
|
+
|
|
3039
|
+
if (filesInfo.length === 0) {
|
|
3040
|
+
return null;
|
|
3041
|
+
}
|
|
3042
|
+
|
|
3043
|
+
return /*#__PURE__*/React__default["default"].createElement("ul", {
|
|
3044
|
+
className: "file-info"
|
|
3045
|
+
}, filesInfo.map((fileInfo, key) => {
|
|
3046
|
+
const {
|
|
3047
|
+
name,
|
|
3048
|
+
size,
|
|
3049
|
+
type
|
|
3050
|
+
} = fileInfo;
|
|
3051
|
+
return /*#__PURE__*/React__default["default"].createElement("li", {
|
|
3052
|
+
key: key
|
|
3053
|
+
}, /*#__PURE__*/React__default["default"].createElement("strong", null, name), " (", type, ", ", size, " bytes)");
|
|
3054
|
+
}));
|
|
3055
|
+
}
|
|
3056
|
+
|
|
3057
|
+
function extractFileInfo(dataURLs) {
|
|
3058
|
+
return dataURLs.filter(dataURL => typeof dataURL !== "undefined").map(dataURL => {
|
|
3059
|
+
const {
|
|
3060
|
+
blob,
|
|
3061
|
+
name
|
|
3062
|
+
} = utils.dataURItoBlob(dataURL);
|
|
3063
|
+
return {
|
|
3064
|
+
name: name,
|
|
3065
|
+
size: blob.size,
|
|
3066
|
+
type: blob.type
|
|
3067
|
+
};
|
|
3068
|
+
});
|
|
3069
|
+
}
|
|
3070
|
+
/**
|
|
3071
|
+
* The `FileWidget` is a widget for rendering file upload fields.
|
|
3072
|
+
* It is typically used with a string property with data-url format.
|
|
3073
|
+
*/
|
|
3074
|
+
|
|
3075
|
+
|
|
3076
|
+
function FileWidget(_ref2) {
|
|
3077
|
+
let {
|
|
3078
|
+
multiple,
|
|
3079
|
+
id,
|
|
3080
|
+
readonly,
|
|
3081
|
+
disabled,
|
|
3082
|
+
onChange,
|
|
3083
|
+
value,
|
|
3084
|
+
autofocus = false,
|
|
3085
|
+
options
|
|
3086
|
+
} = _ref2;
|
|
3087
|
+
const extractedFilesInfo = React.useMemo(() => Array.isArray(value) ? extractFileInfo(value) : extractFileInfo([value]), [value]);
|
|
3088
|
+
const [filesInfo, setFilesInfo] = React.useState(extractedFilesInfo);
|
|
3089
|
+
const handleChange = React.useCallback(event => {
|
|
3090
|
+
if (!event.target.files) {
|
|
3091
|
+
return;
|
|
3092
|
+
}
|
|
3093
|
+
|
|
3094
|
+
processFiles(event.target.files).then(filesInfoEvent => {
|
|
3095
|
+
setFilesInfo(filesInfoEvent);
|
|
3096
|
+
const newValue = filesInfoEvent.map(fileInfo => fileInfo.dataURL);
|
|
3097
|
+
|
|
3098
|
+
if (multiple) {
|
|
3099
|
+
onChange(newValue);
|
|
3100
|
+
} else {
|
|
3101
|
+
onChange(newValue[0]);
|
|
3102
|
+
}
|
|
3103
|
+
});
|
|
3104
|
+
}, [multiple, onChange]);
|
|
3105
|
+
return /*#__PURE__*/React__default["default"].createElement("div", null, /*#__PURE__*/React__default["default"].createElement("p", null, /*#__PURE__*/React__default["default"].createElement("input", {
|
|
3106
|
+
id: id,
|
|
3107
|
+
type: "file",
|
|
3108
|
+
disabled: readonly || disabled,
|
|
3109
|
+
onChange: handleChange,
|
|
3110
|
+
defaultValue: "",
|
|
3111
|
+
autoFocus: autofocus,
|
|
3112
|
+
multiple: multiple,
|
|
3113
|
+
accept: options.accept ? String(options.accept) : undefined
|
|
3114
|
+
})), /*#__PURE__*/React__default["default"].createElement(FilesInfo, {
|
|
3115
|
+
filesInfo: filesInfo
|
|
3116
|
+
}));
|
|
3117
|
+
}
|
|
3118
|
+
|
|
3119
|
+
/** The `HiddenWidget` is a widget for rendering a hidden input field.
|
|
3120
|
+
* It is typically used by setting type to "hidden".
|
|
3121
|
+
*
|
|
3122
|
+
* @param props - The `WidgetProps` for this component
|
|
3123
|
+
*/
|
|
3124
|
+
|
|
3125
|
+
function HiddenWidget(_ref) {
|
|
3126
|
+
let {
|
|
3127
|
+
id,
|
|
3128
|
+
value
|
|
3129
|
+
} = _ref;
|
|
3130
|
+
return /*#__PURE__*/React__default["default"].createElement("input", {
|
|
3131
|
+
type: "hidden",
|
|
3132
|
+
id: id,
|
|
3133
|
+
value: typeof value === "undefined" ? "" : value
|
|
3134
|
+
});
|
|
3135
|
+
}
|
|
3136
|
+
|
|
3137
|
+
/** The `PasswordWidget` component uses the `BaseInputTemplate` changing the type to `password`.
|
|
3138
|
+
*
|
|
3139
|
+
* @param props - The `WidgetProps` for this component
|
|
3140
|
+
*/
|
|
3141
|
+
|
|
3142
|
+
function PasswordWidget(props) {
|
|
3143
|
+
const {
|
|
3144
|
+
options,
|
|
3145
|
+
registry
|
|
3146
|
+
} = props;
|
|
3147
|
+
const BaseInputTemplate = utils.getTemplate("BaseInputTemplate", registry, options);
|
|
3148
|
+
return /*#__PURE__*/React__default["default"].createElement(BaseInputTemplate, {
|
|
3149
|
+
type: "password",
|
|
3150
|
+
...props
|
|
3151
|
+
});
|
|
3152
|
+
}
|
|
3153
|
+
|
|
3154
|
+
/** The `RadioWidget` is a widget for rendering a radio group.
|
|
3155
|
+
* It is typically used with a string property constrained with enum options.
|
|
3156
|
+
*
|
|
3157
|
+
* @param props - The `WidgetProps` for this component
|
|
3158
|
+
*/
|
|
3159
|
+
|
|
3160
|
+
function RadioWidget(_ref) {
|
|
3161
|
+
let {
|
|
3162
|
+
options,
|
|
3163
|
+
value,
|
|
3164
|
+
required,
|
|
3165
|
+
disabled,
|
|
3166
|
+
readonly,
|
|
3167
|
+
autofocus = false,
|
|
3168
|
+
onBlur,
|
|
3169
|
+
onFocus,
|
|
3170
|
+
onChange,
|
|
3171
|
+
id
|
|
3172
|
+
} = _ref;
|
|
3173
|
+
// Generating a unique field name to identify this set of radio buttons
|
|
3174
|
+
const name = Math.random().toString();
|
|
3175
|
+
const {
|
|
3176
|
+
enumOptions,
|
|
3177
|
+
enumDisabled,
|
|
3178
|
+
inline
|
|
3179
|
+
} = options; // checked={checked} has been moved above name={name}, As mentioned in #349;
|
|
3180
|
+
// this is a temporary fix for radio button rendering bug in React, facebook/react#7630.
|
|
3181
|
+
|
|
3182
|
+
const handleBlur = React.useCallback(event => onBlur(id, event.target.value), [onBlur, id]);
|
|
3183
|
+
const handleFocus = React.useCallback(event => onFocus(id, event.target.value), [onFocus, id]);
|
|
3184
|
+
return /*#__PURE__*/React__default["default"].createElement("div", {
|
|
3185
|
+
className: "field-radio-group",
|
|
3186
|
+
id: id
|
|
3187
|
+
}, Array.isArray(enumOptions) && enumOptions.map((option, i) => {
|
|
3188
|
+
const checked = option.value === value;
|
|
3189
|
+
const itemDisabled = enumDisabled && enumDisabled.indexOf(option.value) != -1;
|
|
3190
|
+
const disabledCls = disabled || itemDisabled || readonly ? "disabled" : "";
|
|
3191
|
+
|
|
3192
|
+
const handleChange = () => onChange(option.value);
|
|
3193
|
+
|
|
3194
|
+
const radio = /*#__PURE__*/React__default["default"].createElement("span", null, /*#__PURE__*/React__default["default"].createElement("input", {
|
|
3195
|
+
type: "radio",
|
|
3196
|
+
id: id + "_" + i,
|
|
3197
|
+
checked: checked,
|
|
3198
|
+
name: name,
|
|
3199
|
+
required: required,
|
|
3200
|
+
value: option.value,
|
|
3201
|
+
disabled: disabled || itemDisabled || readonly,
|
|
3202
|
+
autoFocus: autofocus && i === 0,
|
|
3203
|
+
onChange: handleChange,
|
|
3204
|
+
onBlur: handleBlur,
|
|
3205
|
+
onFocus: handleFocus
|
|
3206
|
+
}), /*#__PURE__*/React__default["default"].createElement("span", null, option.label));
|
|
3207
|
+
return inline ? /*#__PURE__*/React__default["default"].createElement("label", {
|
|
3208
|
+
key: i,
|
|
3209
|
+
className: "radio-inline " + disabledCls
|
|
3210
|
+
}, radio) : /*#__PURE__*/React__default["default"].createElement("div", {
|
|
3211
|
+
key: i,
|
|
3212
|
+
className: "radio " + disabledCls
|
|
3213
|
+
}, /*#__PURE__*/React__default["default"].createElement("label", null, radio));
|
|
3214
|
+
}));
|
|
3215
|
+
}
|
|
3216
|
+
|
|
3217
|
+
/** The `RangeWidget` component uses the `BaseInputTemplate` changing the type to `range` and wrapping the result
|
|
3218
|
+
* in a div, with the value along side it.
|
|
3219
|
+
*
|
|
3220
|
+
* @param props - The `WidgetProps` for this component
|
|
3221
|
+
*/
|
|
3222
|
+
|
|
3223
|
+
function RangeWidget(props) {
|
|
3224
|
+
const {
|
|
3225
|
+
value,
|
|
3226
|
+
registry: {
|
|
3227
|
+
templates: {
|
|
3228
|
+
BaseInputTemplate
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
3231
|
+
} = props;
|
|
3232
|
+
return /*#__PURE__*/React__default["default"].createElement("div", {
|
|
3233
|
+
className: "field-range-wrapper"
|
|
3234
|
+
}, /*#__PURE__*/React__default["default"].createElement(BaseInputTemplate, {
|
|
3235
|
+
type: "range",
|
|
3236
|
+
...props
|
|
3237
|
+
}), /*#__PURE__*/React__default["default"].createElement("span", {
|
|
3238
|
+
className: "range-view"
|
|
3239
|
+
}, value));
|
|
3240
|
+
}
|
|
3241
|
+
|
|
3242
|
+
function getValue(event, multiple) {
|
|
3243
|
+
if (multiple) {
|
|
3244
|
+
return Array.from(event.target.options).slice().filter(o => o.selected).map(o => o.value);
|
|
3245
|
+
}
|
|
3246
|
+
|
|
3247
|
+
return event.target.value;
|
|
3248
|
+
}
|
|
3249
|
+
/** The `SelectWidget` is a widget for rendering dropdowns.
|
|
3250
|
+
* It is typically used with string properties constrained with enum options.
|
|
3251
|
+
*
|
|
3252
|
+
* @param props - The `WidgetProps` for this component
|
|
3253
|
+
*/
|
|
3254
|
+
|
|
3255
|
+
|
|
3256
|
+
function SelectWidget(_ref) {
|
|
3257
|
+
let {
|
|
3258
|
+
schema,
|
|
3259
|
+
id,
|
|
3260
|
+
options,
|
|
3261
|
+
value,
|
|
3262
|
+
required,
|
|
3263
|
+
disabled,
|
|
3264
|
+
readonly,
|
|
3265
|
+
multiple = false,
|
|
3266
|
+
autofocus = false,
|
|
3267
|
+
onChange,
|
|
3268
|
+
onBlur,
|
|
3269
|
+
onFocus,
|
|
3270
|
+
placeholder
|
|
3271
|
+
} = _ref;
|
|
3272
|
+
const {
|
|
3273
|
+
enumOptions,
|
|
3274
|
+
enumDisabled
|
|
3275
|
+
} = options;
|
|
3276
|
+
const emptyValue = multiple ? [] : "";
|
|
3277
|
+
const handleFocus = React.useCallback(event => {
|
|
3278
|
+
const newValue = getValue(event, multiple);
|
|
3279
|
+
return onFocus(id, utils.processSelectValue(schema, newValue, options));
|
|
3280
|
+
}, [onFocus, id, schema, multiple, options]);
|
|
3281
|
+
const handleBlur = React.useCallback(event => {
|
|
3282
|
+
const newValue = getValue(event, multiple);
|
|
3283
|
+
return onBlur(id, utils.processSelectValue(schema, newValue, options));
|
|
3284
|
+
}, [onBlur, id, schema, multiple, options]);
|
|
3285
|
+
const handleChange = React.useCallback(event => {
|
|
3286
|
+
const newValue = getValue(event, multiple);
|
|
3287
|
+
return onChange(utils.processSelectValue(schema, newValue, options));
|
|
3288
|
+
}, [onChange, schema, multiple, options]);
|
|
3289
|
+
return /*#__PURE__*/React__default["default"].createElement("select", {
|
|
3290
|
+
id: id,
|
|
3291
|
+
multiple: multiple,
|
|
3292
|
+
className: "form-control",
|
|
3293
|
+
value: typeof value === "undefined" ? emptyValue : value,
|
|
3294
|
+
required: required,
|
|
3295
|
+
disabled: disabled || readonly,
|
|
3296
|
+
autoFocus: autofocus,
|
|
3297
|
+
onBlur: handleBlur,
|
|
3298
|
+
onFocus: handleFocus,
|
|
3299
|
+
onChange: handleChange
|
|
3300
|
+
}, !multiple && schema.default === undefined && /*#__PURE__*/React__default["default"].createElement("option", {
|
|
3301
|
+
value: ""
|
|
3302
|
+
}, placeholder), Array.isArray(enumOptions) && enumOptions.map((_ref2, i) => {
|
|
3303
|
+
let {
|
|
3304
|
+
value,
|
|
3305
|
+
label
|
|
3306
|
+
} = _ref2;
|
|
3307
|
+
const disabled = enumDisabled && enumDisabled.indexOf(value) != -1;
|
|
3308
|
+
return /*#__PURE__*/React__default["default"].createElement("option", {
|
|
3309
|
+
key: i,
|
|
3310
|
+
value: value,
|
|
3311
|
+
disabled: disabled
|
|
3312
|
+
}, label);
|
|
3313
|
+
}));
|
|
3314
|
+
}
|
|
3315
|
+
|
|
3316
|
+
/** The `TextareaWidget` is a widget for rendering input fields as textarea.
|
|
3317
|
+
*
|
|
3318
|
+
* @param props - The `WidgetProps` for this component
|
|
3319
|
+
*/
|
|
3320
|
+
|
|
3321
|
+
function TextareaWidget(_ref) {
|
|
3322
|
+
let {
|
|
3323
|
+
id,
|
|
3324
|
+
options = {},
|
|
3325
|
+
placeholder,
|
|
3326
|
+
value,
|
|
3327
|
+
required,
|
|
3328
|
+
disabled,
|
|
3329
|
+
readonly,
|
|
3330
|
+
autofocus = false,
|
|
3331
|
+
onChange,
|
|
3332
|
+
onBlur,
|
|
3333
|
+
onFocus
|
|
3334
|
+
} = _ref;
|
|
3335
|
+
const handleChange = React.useCallback(_ref2 => {
|
|
3336
|
+
let {
|
|
3337
|
+
target: {
|
|
3338
|
+
value
|
|
3339
|
+
}
|
|
3340
|
+
} = _ref2;
|
|
3341
|
+
return onChange(value === "" ? options.emptyValue : value);
|
|
3342
|
+
}, [onChange, options.emptyValue]);
|
|
3343
|
+
const handleBlur = React.useCallback(_ref3 => {
|
|
3344
|
+
let {
|
|
3345
|
+
target: {
|
|
3346
|
+
value
|
|
3347
|
+
}
|
|
3348
|
+
} = _ref3;
|
|
3349
|
+
return onBlur(id, value);
|
|
3350
|
+
}, [onBlur, id]);
|
|
3351
|
+
const handleFocus = React.useCallback(_ref4 => {
|
|
3352
|
+
let {
|
|
3353
|
+
target: {
|
|
3354
|
+
value
|
|
3355
|
+
}
|
|
3356
|
+
} = _ref4;
|
|
3357
|
+
return onFocus(id, value);
|
|
3358
|
+
}, [id, onFocus]);
|
|
3359
|
+
return /*#__PURE__*/React__default["default"].createElement("textarea", {
|
|
3360
|
+
id: id,
|
|
3361
|
+
className: "form-control",
|
|
3362
|
+
value: value ? value : "",
|
|
3363
|
+
placeholder: placeholder,
|
|
3364
|
+
required: required,
|
|
3365
|
+
disabled: disabled,
|
|
3366
|
+
readOnly: readonly,
|
|
3367
|
+
autoFocus: autofocus,
|
|
3368
|
+
rows: options.rows,
|
|
3369
|
+
onBlur: handleBlur,
|
|
3370
|
+
onFocus: handleFocus,
|
|
3371
|
+
onChange: handleChange
|
|
3372
|
+
});
|
|
3373
|
+
}
|
|
3374
|
+
|
|
3375
|
+
TextareaWidget.defaultProps = {
|
|
3376
|
+
autofocus: false,
|
|
3377
|
+
options: {}
|
|
3378
|
+
};
|
|
3379
|
+
|
|
3380
|
+
/** The `TextWidget` component uses the `BaseInputTemplate`.
|
|
3381
|
+
*
|
|
3382
|
+
* @param props - The `WidgetProps` for this component
|
|
3383
|
+
*/
|
|
3384
|
+
|
|
3385
|
+
function TextWidget(props) {
|
|
3386
|
+
const {
|
|
3387
|
+
options,
|
|
3388
|
+
registry
|
|
3389
|
+
} = props;
|
|
3390
|
+
const BaseInputTemplate = utils.getTemplate("BaseInputTemplate", registry, options);
|
|
3391
|
+
return /*#__PURE__*/React__default["default"].createElement(BaseInputTemplate, { ...props
|
|
3392
|
+
});
|
|
3393
|
+
}
|
|
3394
|
+
|
|
3395
|
+
/** The `URLWidget` component uses the `BaseInputTemplate` changing the type to `url`.
|
|
3396
|
+
*
|
|
3397
|
+
* @param props - The `WidgetProps` for this component
|
|
3398
|
+
*/
|
|
3399
|
+
|
|
3400
|
+
function URLWidget(props) {
|
|
3401
|
+
const {
|
|
3402
|
+
options,
|
|
3403
|
+
registry
|
|
3404
|
+
} = props;
|
|
3405
|
+
const BaseInputTemplate = utils.getTemplate("BaseInputTemplate", registry, options);
|
|
3406
|
+
return /*#__PURE__*/React__default["default"].createElement(BaseInputTemplate, {
|
|
3407
|
+
type: "url",
|
|
3408
|
+
...props
|
|
3409
|
+
});
|
|
3410
|
+
}
|
|
3411
|
+
|
|
3412
|
+
/** The `UpDownWidget` component uses the `BaseInputTemplate` changing the type to `number`.
|
|
3413
|
+
*
|
|
3414
|
+
* @param props - The `WidgetProps` for this component
|
|
3415
|
+
*/
|
|
3416
|
+
|
|
3417
|
+
function UpDownWidget(props) {
|
|
3418
|
+
const {
|
|
3419
|
+
options,
|
|
3420
|
+
registry
|
|
3421
|
+
} = props;
|
|
3422
|
+
const BaseInputTemplate = utils.getTemplate("BaseInputTemplate", registry, options);
|
|
3423
|
+
return /*#__PURE__*/React__default["default"].createElement(BaseInputTemplate, {
|
|
3424
|
+
type: "number",
|
|
3425
|
+
...props
|
|
3426
|
+
});
|
|
3427
|
+
}
|
|
3428
|
+
|
|
3429
|
+
const widgets = {
|
|
3430
|
+
PasswordWidget,
|
|
3431
|
+
RadioWidget,
|
|
3432
|
+
UpDownWidget,
|
|
3433
|
+
RangeWidget,
|
|
3434
|
+
SelectWidget,
|
|
3435
|
+
TextWidget,
|
|
3436
|
+
DateWidget,
|
|
3437
|
+
DateTimeWidget,
|
|
3438
|
+
AltDateWidget,
|
|
3439
|
+
AltDateTimeWidget,
|
|
3440
|
+
EmailWidget,
|
|
3441
|
+
URLWidget,
|
|
3442
|
+
TextareaWidget,
|
|
3443
|
+
HiddenWidget,
|
|
3444
|
+
ColorWidget,
|
|
3445
|
+
FileWidget,
|
|
3446
|
+
CheckboxWidget,
|
|
3447
|
+
CheckboxesWidget
|
|
3448
|
+
};
|
|
3449
|
+
|
|
3450
|
+
/** The default registry consists of all the fields, templates and widgets provided in the core implementation,
|
|
3451
|
+
* plus an empty `rootSchema` and `formContext. We omit schemaUtils here because it cannot be defaulted without a
|
|
3452
|
+
* rootSchema and validator. It will be added into the computed registry later in the Form.
|
|
3453
|
+
*/
|
|
3454
|
+
|
|
3455
|
+
function getDefaultRegistry() {
|
|
3456
|
+
return {
|
|
3457
|
+
fields,
|
|
3458
|
+
templates,
|
|
3459
|
+
widgets,
|
|
3460
|
+
rootSchema: {},
|
|
3461
|
+
formContext: {}
|
|
3462
|
+
};
|
|
3463
|
+
}
|
|
3464
|
+
|
|
3465
|
+
/** The `Form` component renders the outer form and all the fields defined in the `schema` */
|
|
3466
|
+
|
|
3467
|
+
class Form extends React.Component {
|
|
3468
|
+
/** The ref used to hold the `form` element, this needs to be `any` because `tagName` or `_internalFormWrapper` can
|
|
3469
|
+
* provide any possible type here
|
|
3470
|
+
*/
|
|
3471
|
+
|
|
3472
|
+
/** Constructs the `Form` from the `props`. Will setup the initial state from the props. It will also call the
|
|
3473
|
+
* `onChange` handler if the initially provided `formData` is modified to add missing default values as part of the
|
|
3474
|
+
* state construction.
|
|
3475
|
+
*
|
|
3476
|
+
* @param props - The initial props for the `Form`
|
|
3477
|
+
*/
|
|
3478
|
+
constructor(props) {
|
|
3479
|
+
super(props);
|
|
3480
|
+
this.formElement = void 0;
|
|
3481
|
+
|
|
3482
|
+
this.getUsedFormData = (formData, fields) => {
|
|
3483
|
+
// For the case of a single input form
|
|
3484
|
+
if (fields.length === 0 && typeof formData !== "object") {
|
|
3485
|
+
return formData;
|
|
3486
|
+
}
|
|
3487
|
+
|
|
3488
|
+
const data = _pick__default["default"](formData, fields);
|
|
3489
|
+
|
|
3490
|
+
if (Array.isArray(formData)) {
|
|
3491
|
+
return Object.keys(data).map(key => data[key]);
|
|
3492
|
+
}
|
|
3493
|
+
|
|
3494
|
+
return data;
|
|
3495
|
+
};
|
|
3496
|
+
|
|
3497
|
+
this.getFieldNames = (pathSchema, formData) => {
|
|
3498
|
+
const getAllPaths = function (_obj, acc, paths) {
|
|
3499
|
+
if (acc === void 0) {
|
|
3500
|
+
acc = [];
|
|
3501
|
+
}
|
|
3502
|
+
|
|
3503
|
+
if (paths === void 0) {
|
|
3504
|
+
paths = [""];
|
|
3505
|
+
}
|
|
3506
|
+
|
|
3507
|
+
Object.keys(_obj).forEach(key => {
|
|
3508
|
+
if (typeof _obj[key] === "object") {
|
|
3509
|
+
const newPaths = paths.map(path => path + "." + key); // If an object is marked with additionalProperties, all its keys are valid
|
|
3510
|
+
|
|
3511
|
+
if (_obj[key][utils.RJSF_ADDITONAL_PROPERTIES_FLAG] && _obj[key][utils.NAME_KEY] !== "") {
|
|
3512
|
+
acc.push(_obj[key][utils.NAME_KEY]);
|
|
3513
|
+
} else {
|
|
3514
|
+
getAllPaths(_obj[key], acc, newPaths);
|
|
3515
|
+
}
|
|
3516
|
+
} else if (key === utils.NAME_KEY && _obj[key] !== "") {
|
|
3517
|
+
paths.forEach(path => {
|
|
3518
|
+
path = path.replace(/^\./, "");
|
|
3519
|
+
|
|
3520
|
+
const formValue = get__default["default"](formData, path); // adds path to fieldNames if it points to a value
|
|
3521
|
+
// or an empty object/array
|
|
3522
|
+
|
|
3523
|
+
|
|
3524
|
+
if (typeof formValue !== "object" || _isEmpty__default["default"](formValue)) {
|
|
3525
|
+
acc.push(path);
|
|
3526
|
+
}
|
|
3527
|
+
});
|
|
3528
|
+
}
|
|
3529
|
+
});
|
|
3530
|
+
return acc;
|
|
3531
|
+
};
|
|
3532
|
+
|
|
3533
|
+
return getAllPaths(pathSchema);
|
|
3534
|
+
};
|
|
3535
|
+
|
|
3536
|
+
this.onChange = (formData, newErrorSchema) => {
|
|
3537
|
+
const {
|
|
3538
|
+
extraErrors,
|
|
3539
|
+
omitExtraData,
|
|
3540
|
+
liveOmit,
|
|
3541
|
+
noValidate,
|
|
3542
|
+
liveValidate,
|
|
3543
|
+
onChange
|
|
3544
|
+
} = this.props;
|
|
3545
|
+
const {
|
|
3546
|
+
schemaUtils,
|
|
3547
|
+
schema
|
|
3548
|
+
} = this.state;
|
|
3549
|
+
|
|
3550
|
+
if (utils.isObject(formData) || Array.isArray(formData)) {
|
|
3551
|
+
const newState = this.getStateFromProps(this.props, formData);
|
|
3552
|
+
formData = newState.formData;
|
|
3553
|
+
}
|
|
3554
|
+
|
|
3555
|
+
const mustValidate = !noValidate && liveValidate;
|
|
3556
|
+
let state = {
|
|
3557
|
+
formData,
|
|
3558
|
+
schema
|
|
3559
|
+
};
|
|
3560
|
+
let newFormData = formData;
|
|
3561
|
+
|
|
3562
|
+
if (omitExtraData === true && liveOmit === true) {
|
|
3563
|
+
const retrievedSchema = schemaUtils.retrieveSchema(schema, formData);
|
|
3564
|
+
const pathSchema = schemaUtils.toPathSchema(retrievedSchema, "", formData);
|
|
3565
|
+
const fieldNames = this.getFieldNames(pathSchema, formData);
|
|
3566
|
+
newFormData = this.getUsedFormData(formData, fieldNames);
|
|
3567
|
+
state = {
|
|
3568
|
+
formData: newFormData
|
|
3569
|
+
};
|
|
3570
|
+
}
|
|
3571
|
+
|
|
3572
|
+
if (mustValidate) {
|
|
3573
|
+
const schemaValidation = this.validate(schemaUtils, newFormData);
|
|
3574
|
+
let errors = schemaValidation.errors;
|
|
3575
|
+
let errorSchema = schemaValidation.errorSchema;
|
|
3576
|
+
const schemaValidationErrors = errors;
|
|
3577
|
+
const schemaValidationErrorSchema = errorSchema;
|
|
3578
|
+
|
|
3579
|
+
if (extraErrors) {
|
|
3580
|
+
const merged = schemaUtils.mergeValidationData(schemaValidation, extraErrors);
|
|
3581
|
+
errorSchema = merged.errorSchema;
|
|
3582
|
+
errors = merged.errors;
|
|
3583
|
+
}
|
|
3584
|
+
|
|
3585
|
+
state = {
|
|
3586
|
+
formData: newFormData,
|
|
3587
|
+
errors,
|
|
3588
|
+
errorSchema,
|
|
3589
|
+
schemaValidationErrors,
|
|
3590
|
+
schemaValidationErrorSchema
|
|
3591
|
+
};
|
|
3592
|
+
} else if (!noValidate && newErrorSchema) {
|
|
3593
|
+
const errorSchema = extraErrors ? utils.mergeObjects(newErrorSchema, extraErrors, true) : newErrorSchema;
|
|
3594
|
+
state = {
|
|
3595
|
+
formData: newFormData,
|
|
3596
|
+
errorSchema: errorSchema,
|
|
3597
|
+
errors: schemaUtils.getValidator().toErrorList(errorSchema)
|
|
3598
|
+
};
|
|
3599
|
+
}
|
|
3600
|
+
|
|
3601
|
+
this.setState(state, () => onChange && onChange({ ...this.state,
|
|
3602
|
+
...state
|
|
3603
|
+
}));
|
|
3604
|
+
};
|
|
3605
|
+
|
|
3606
|
+
this.onBlur = (id, data) => {
|
|
3607
|
+
const {
|
|
3608
|
+
onBlur
|
|
3609
|
+
} = this.props;
|
|
3610
|
+
|
|
3611
|
+
if (onBlur) {
|
|
3612
|
+
onBlur(id, data);
|
|
3613
|
+
}
|
|
3614
|
+
};
|
|
3615
|
+
|
|
3616
|
+
this.onFocus = (id, data) => {
|
|
3617
|
+
const {
|
|
3618
|
+
onFocus
|
|
3619
|
+
} = this.props;
|
|
3620
|
+
|
|
3621
|
+
if (onFocus) {
|
|
3622
|
+
onFocus(id, data);
|
|
3623
|
+
}
|
|
3624
|
+
};
|
|
3625
|
+
|
|
3626
|
+
this.onSubmit = event => {
|
|
3627
|
+
event.preventDefault();
|
|
3628
|
+
|
|
3629
|
+
if (event.target !== event.currentTarget) {
|
|
3630
|
+
return;
|
|
3631
|
+
}
|
|
3632
|
+
|
|
3633
|
+
event.persist();
|
|
3634
|
+
const {
|
|
3635
|
+
omitExtraData,
|
|
3636
|
+
extraErrors,
|
|
3637
|
+
noValidate,
|
|
3638
|
+
onSubmit,
|
|
3639
|
+
onError
|
|
3640
|
+
} = this.props;
|
|
3641
|
+
let {
|
|
3642
|
+
formData: newFormData
|
|
3643
|
+
} = this.state;
|
|
3644
|
+
const {
|
|
3645
|
+
schema,
|
|
3646
|
+
schemaUtils
|
|
3647
|
+
} = this.state;
|
|
3648
|
+
|
|
3649
|
+
if (omitExtraData === true) {
|
|
3650
|
+
const retrievedSchema = schemaUtils.retrieveSchema(schema, newFormData);
|
|
3651
|
+
const pathSchema = schemaUtils.toPathSchema(retrievedSchema, "", newFormData);
|
|
3652
|
+
const fieldNames = this.getFieldNames(pathSchema, newFormData);
|
|
3653
|
+
newFormData = this.getUsedFormData(newFormData, fieldNames);
|
|
3654
|
+
}
|
|
3655
|
+
|
|
3656
|
+
if (!noValidate) {
|
|
3657
|
+
const schemaValidation = this.validate(schemaUtils, newFormData);
|
|
3658
|
+
let errors = schemaValidation.errors;
|
|
3659
|
+
let errorSchema = schemaValidation.errorSchema;
|
|
3660
|
+
const schemaValidationErrors = errors;
|
|
3661
|
+
const schemaValidationErrorSchema = errorSchema;
|
|
3662
|
+
|
|
3663
|
+
if (errors.length > 0) {
|
|
3664
|
+
if (extraErrors) {
|
|
3665
|
+
const merged = schemaUtils.mergeValidationData(schemaValidation, extraErrors);
|
|
3666
|
+
errorSchema = merged.errorSchema;
|
|
3667
|
+
errors = merged.errors;
|
|
3668
|
+
}
|
|
3669
|
+
|
|
3670
|
+
this.setState({
|
|
3671
|
+
errors,
|
|
3672
|
+
errorSchema,
|
|
3673
|
+
schemaValidationErrors,
|
|
3674
|
+
schemaValidationErrorSchema
|
|
3675
|
+
}, () => {
|
|
3676
|
+
if (onError) {
|
|
3677
|
+
onError(errors);
|
|
3678
|
+
} else {
|
|
3679
|
+
console.error("Form validation failed", errors);
|
|
3680
|
+
}
|
|
3681
|
+
});
|
|
3682
|
+
return;
|
|
3683
|
+
}
|
|
3684
|
+
} // There are no errors generated through schema validation.
|
|
3685
|
+
// Check for user provided errors and update state accordingly.
|
|
3686
|
+
|
|
3687
|
+
|
|
3688
|
+
const errorSchema = extraErrors || {};
|
|
3689
|
+
const errors = extraErrors ? schemaUtils.getValidator().toErrorList(extraErrors) : [];
|
|
3690
|
+
this.setState({
|
|
3691
|
+
formData: newFormData,
|
|
3692
|
+
errors,
|
|
3693
|
+
errorSchema,
|
|
3694
|
+
schemaValidationErrors: [],
|
|
3695
|
+
schemaValidationErrorSchema: {}
|
|
3696
|
+
}, () => {
|
|
3697
|
+
if (onSubmit) {
|
|
3698
|
+
onSubmit({ ...this.state,
|
|
3699
|
+
formData: newFormData,
|
|
3700
|
+
status: "submitted"
|
|
3701
|
+
}, event);
|
|
3702
|
+
}
|
|
3703
|
+
});
|
|
3704
|
+
};
|
|
3705
|
+
|
|
3706
|
+
if (!props.validator) {
|
|
3707
|
+
throw new Error("A validator is required for Form functionality to work");
|
|
3708
|
+
}
|
|
3709
|
+
|
|
3710
|
+
this.state = this.getStateFromProps(props, props.formData);
|
|
3711
|
+
|
|
3712
|
+
if (this.props.onChange && !utils.deepEquals(this.state.formData, this.props.formData)) {
|
|
3713
|
+
this.props.onChange(this.state);
|
|
3714
|
+
}
|
|
3715
|
+
|
|
3716
|
+
this.formElement = /*#__PURE__*/React__default["default"].createRef();
|
|
3717
|
+
}
|
|
3718
|
+
/** React lifecycle method that gets called before new props are provided, updates the state based on new props. It
|
|
3719
|
+
* will also call the`onChange` handler if the `formData` is modified to add missing default values as part of the
|
|
3720
|
+
* state construction.
|
|
3721
|
+
*
|
|
3722
|
+
* @param nextProps - The new set of props about to be applied to the `Form`
|
|
3723
|
+
*/
|
|
3724
|
+
|
|
3725
|
+
|
|
3726
|
+
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
3727
|
+
const nextState = this.getStateFromProps(nextProps, nextProps.formData);
|
|
3728
|
+
|
|
3729
|
+
if (!utils.deepEquals(nextState.formData, nextProps.formData) && !utils.deepEquals(nextState.formData, this.state.formData) && nextProps.onChange) {
|
|
3730
|
+
nextProps.onChange(nextState);
|
|
3731
|
+
}
|
|
3732
|
+
|
|
3733
|
+
this.setState(nextState);
|
|
3734
|
+
}
|
|
3735
|
+
/** Extracts the updated state from the given `props` and `inputFormData`. As part of this process, the
|
|
3736
|
+
* `inputFormData` is first processed to add any missing required defaults. After that, the data is run through the
|
|
3737
|
+
* validation process IF required by the `props`.
|
|
3738
|
+
*
|
|
3739
|
+
* @param props - The props passed to the `Form`
|
|
3740
|
+
* @param inputFormData - The new or current data for the `Form`
|
|
3741
|
+
* @returns - The new state for the `Form`
|
|
3742
|
+
*/
|
|
3743
|
+
|
|
3744
|
+
|
|
3745
|
+
getStateFromProps(props, inputFormData) {
|
|
3746
|
+
const state = this.state || {};
|
|
3747
|
+
const schema = "schema" in props ? props.schema : this.props.schema;
|
|
3748
|
+
const uiSchema = ("uiSchema" in props ? props.uiSchema : this.props.uiSchema) || {};
|
|
3749
|
+
const edit = typeof inputFormData !== "undefined";
|
|
3750
|
+
const liveValidate = "liveValidate" in props ? props.liveValidate : this.props.liveValidate;
|
|
3751
|
+
const mustValidate = edit && !props.noValidate && liveValidate;
|
|
3752
|
+
const rootSchema = schema;
|
|
3753
|
+
let schemaUtils = state.schemaUtils;
|
|
3754
|
+
|
|
3755
|
+
if (!schemaUtils || schemaUtils.doesSchemaUtilsDiffer(props.validator, rootSchema)) {
|
|
3756
|
+
schemaUtils = utils.createSchemaUtils(props.validator, rootSchema);
|
|
3757
|
+
}
|
|
3758
|
+
|
|
3759
|
+
const formData = schemaUtils.getDefaultFormState(schema, inputFormData);
|
|
3760
|
+
const retrievedSchema = schemaUtils.retrieveSchema(schema, formData);
|
|
3761
|
+
|
|
3762
|
+
const getCurrentErrors = () => {
|
|
3763
|
+
if (props.noValidate) {
|
|
3764
|
+
return {
|
|
3765
|
+
errors: [],
|
|
3766
|
+
errorSchema: {}
|
|
3767
|
+
};
|
|
3768
|
+
} else if (!props.liveValidate) {
|
|
3769
|
+
return {
|
|
3770
|
+
errors: state.schemaValidationErrors || [],
|
|
3771
|
+
errorSchema: state.schemaValidationErrorSchema || {}
|
|
3772
|
+
};
|
|
3773
|
+
}
|
|
3774
|
+
|
|
3775
|
+
return {
|
|
3776
|
+
errors: state.errors || [],
|
|
3777
|
+
errorSchema: state.errorSchema || {}
|
|
3778
|
+
};
|
|
3779
|
+
};
|
|
3780
|
+
|
|
3781
|
+
let errors;
|
|
3782
|
+
let errorSchema;
|
|
3783
|
+
let schemaValidationErrors = state.schemaValidationErrors;
|
|
3784
|
+
let schemaValidationErrorSchema = state.schemaValidationErrorSchema;
|
|
3785
|
+
|
|
3786
|
+
if (mustValidate) {
|
|
3787
|
+
const schemaValidation = this.validate(schemaUtils, formData, schema);
|
|
3788
|
+
errors = schemaValidation.errors;
|
|
3789
|
+
errorSchema = schemaValidation.errorSchema;
|
|
3790
|
+
schemaValidationErrors = errors;
|
|
3791
|
+
schemaValidationErrorSchema = errorSchema;
|
|
3792
|
+
} else {
|
|
3793
|
+
const currentErrors = getCurrentErrors();
|
|
3794
|
+
errors = currentErrors.errors;
|
|
3795
|
+
errorSchema = currentErrors.errorSchema;
|
|
3796
|
+
}
|
|
3797
|
+
|
|
3798
|
+
if (props.extraErrors) {
|
|
3799
|
+
const merged = schemaUtils.mergeValidationData({
|
|
3800
|
+
errorSchema,
|
|
3801
|
+
errors
|
|
3802
|
+
}, props.extraErrors);
|
|
3803
|
+
errorSchema = merged.errorSchema;
|
|
3804
|
+
errors = merged.errors;
|
|
3805
|
+
}
|
|
3806
|
+
|
|
3807
|
+
const idSchema = schemaUtils.toIdSchema(retrievedSchema, uiSchema["ui:rootFieldId"], formData, props.idPrefix, props.idSeparator);
|
|
3808
|
+
const nextState = {
|
|
3809
|
+
schemaUtils,
|
|
3810
|
+
schema,
|
|
3811
|
+
uiSchema,
|
|
3812
|
+
idSchema,
|
|
3813
|
+
formData,
|
|
3814
|
+
edit,
|
|
3815
|
+
errors,
|
|
3816
|
+
errorSchema,
|
|
3817
|
+
schemaValidationErrors,
|
|
3818
|
+
schemaValidationErrorSchema
|
|
3819
|
+
};
|
|
3820
|
+
return nextState;
|
|
3821
|
+
}
|
|
3822
|
+
/** React lifecycle method that is used to determine whether component should be updated.
|
|
3823
|
+
*
|
|
3824
|
+
* @param nextProps - The next version of the props
|
|
3825
|
+
* @param nextState - The next version of the state
|
|
3826
|
+
* @returns - True if the component should be updated, false otherwise
|
|
3827
|
+
*/
|
|
3828
|
+
|
|
3829
|
+
|
|
3830
|
+
shouldComponentUpdate(nextProps, nextState) {
|
|
3831
|
+
return utils.shouldRender(this, nextProps, nextState);
|
|
3832
|
+
}
|
|
3833
|
+
/** Validates the `formData` against the `schema` using the `schemaUtils`, returning the results.
|
|
3834
|
+
*
|
|
3835
|
+
* @param schemaUtils - The schemaUtils to use for validation
|
|
3836
|
+
* @param formData - The new form data to validate
|
|
3837
|
+
* @param schema - The schema used to validate against
|
|
3838
|
+
*/
|
|
3839
|
+
|
|
3840
|
+
|
|
3841
|
+
validate(schemaUtils, formData, schema) {
|
|
3842
|
+
if (schema === void 0) {
|
|
3843
|
+
schema = this.props.schema;
|
|
3844
|
+
}
|
|
3845
|
+
|
|
3846
|
+
const {
|
|
3847
|
+
customValidate,
|
|
3848
|
+
transformErrors
|
|
3849
|
+
} = this.props;
|
|
3850
|
+
const resolvedSchema = schemaUtils.retrieveSchema(schema, formData);
|
|
3851
|
+
return schemaUtils.getValidator().validateFormData(formData, resolvedSchema, customValidate, transformErrors);
|
|
3852
|
+
}
|
|
3853
|
+
/** Renders any errors contained in the `state` in using the `ErrorList`, if not disabled by `showErrorList`. */
|
|
3854
|
+
|
|
3855
|
+
|
|
3856
|
+
renderErrors(registry) {
|
|
3857
|
+
const {
|
|
3858
|
+
errors,
|
|
3859
|
+
errorSchema,
|
|
3860
|
+
schema,
|
|
3861
|
+
uiSchema
|
|
3862
|
+
} = this.state;
|
|
3863
|
+
const {
|
|
3864
|
+
showErrorList,
|
|
3865
|
+
formContext
|
|
3866
|
+
} = this.props;
|
|
3867
|
+
const options = utils.getUiOptions(uiSchema);
|
|
3868
|
+
const ErrorListTemplate = utils.getTemplate("ErrorListTemplate", registry, options);
|
|
3869
|
+
|
|
3870
|
+
if (errors && errors.length && showErrorList != false) {
|
|
3871
|
+
return /*#__PURE__*/React__default["default"].createElement(ErrorListTemplate, {
|
|
3872
|
+
errors: errors,
|
|
3873
|
+
errorSchema: errorSchema || {},
|
|
3874
|
+
schema: schema,
|
|
3875
|
+
uiSchema: uiSchema,
|
|
3876
|
+
formContext: formContext
|
|
3877
|
+
});
|
|
3878
|
+
}
|
|
3879
|
+
|
|
3880
|
+
return null;
|
|
3881
|
+
}
|
|
3882
|
+
/** Returns the `formData` with only the elements specified in the `fields` list
|
|
3883
|
+
*
|
|
3884
|
+
* @param formData - The data for the `Form`
|
|
3885
|
+
* @param fields - The fields to keep while filtering
|
|
3886
|
+
*/
|
|
3887
|
+
|
|
3888
|
+
|
|
3889
|
+
/** Returns the registry for the form */
|
|
3890
|
+
getRegistry() {
|
|
3891
|
+
var _this$props$templates;
|
|
3892
|
+
|
|
3893
|
+
const {
|
|
3894
|
+
schemaUtils
|
|
3895
|
+
} = this.state;
|
|
3896
|
+
const {
|
|
3897
|
+
fields,
|
|
3898
|
+
templates,
|
|
3899
|
+
widgets,
|
|
3900
|
+
formContext
|
|
3901
|
+
} = getDefaultRegistry();
|
|
3902
|
+
return {
|
|
3903
|
+
fields: { ...fields,
|
|
3904
|
+
...this.props.fields
|
|
3905
|
+
},
|
|
3906
|
+
templates: { ...templates,
|
|
3907
|
+
...this.props.templates,
|
|
3908
|
+
ButtonTemplates: { ...templates.ButtonTemplates,
|
|
3909
|
+
...((_this$props$templates = this.props.templates) === null || _this$props$templates === void 0 ? void 0 : _this$props$templates.ButtonTemplates)
|
|
3910
|
+
}
|
|
3911
|
+
},
|
|
3912
|
+
widgets: { ...widgets,
|
|
3913
|
+
...this.props.widgets
|
|
3914
|
+
},
|
|
3915
|
+
rootSchema: this.props.schema,
|
|
3916
|
+
formContext: this.props.formContext || formContext,
|
|
3917
|
+
schemaUtils
|
|
3918
|
+
};
|
|
3919
|
+
}
|
|
3920
|
+
/** Provides a function that can be used to programmatically submit the `Form` */
|
|
3921
|
+
|
|
3922
|
+
|
|
3923
|
+
submit() {
|
|
3924
|
+
if (this.formElement.current) {
|
|
3925
|
+
this.formElement.current.dispatchEvent(new CustomEvent("submit", {
|
|
3926
|
+
cancelable: true
|
|
3927
|
+
}));
|
|
3928
|
+
this.formElement.current.requestSubmit();
|
|
3929
|
+
}
|
|
3930
|
+
}
|
|
3931
|
+
/** Renders the `Form` fields inside the <form> | `tagName` or `_internalFormWrapper`, rendering any errors if
|
|
3932
|
+
* needed along with the submit button or any children of the form.
|
|
3933
|
+
*/
|
|
3934
|
+
|
|
3935
|
+
|
|
3936
|
+
render() {
|
|
3937
|
+
const {
|
|
3938
|
+
children,
|
|
3939
|
+
id,
|
|
3940
|
+
idPrefix,
|
|
3941
|
+
idSeparator,
|
|
3942
|
+
className = "",
|
|
3943
|
+
tagName,
|
|
3944
|
+
name,
|
|
3945
|
+
method,
|
|
3946
|
+
target,
|
|
3947
|
+
action,
|
|
3948
|
+
autoComplete,
|
|
3949
|
+
enctype,
|
|
3950
|
+
acceptcharset,
|
|
3951
|
+
noHtml5Validate = false,
|
|
3952
|
+
disabled = false,
|
|
3953
|
+
readonly = false,
|
|
3954
|
+
formContext,
|
|
3955
|
+
_internalFormWrapper
|
|
3956
|
+
} = this.props;
|
|
3957
|
+
const {
|
|
3958
|
+
schema,
|
|
3959
|
+
uiSchema,
|
|
3960
|
+
formData,
|
|
3961
|
+
errorSchema,
|
|
3962
|
+
idSchema
|
|
3963
|
+
} = this.state;
|
|
3964
|
+
const registry = this.getRegistry();
|
|
3965
|
+
const {
|
|
3966
|
+
SchemaField: _SchemaField
|
|
3967
|
+
} = registry.fields;
|
|
3968
|
+
const {
|
|
3969
|
+
SubmitButton
|
|
3970
|
+
} = registry.templates.ButtonTemplates; // The `semantic-ui` and `material-ui` themes have `_internalFormWrapper`s that take an `as` prop that is the
|
|
3971
|
+
// PropTypes.elementType to use for the inner tag so we'll need to pass `tagName` along if it is provided.
|
|
3972
|
+
// NOTE, the `as` prop is native to `semantic-ui` and is emulated in the `material-ui` theme
|
|
3973
|
+
|
|
3974
|
+
const as = _internalFormWrapper ? tagName : undefined;
|
|
3975
|
+
const FormTag = _internalFormWrapper || tagName || "form";
|
|
3976
|
+
return /*#__PURE__*/React__default["default"].createElement(FormTag, {
|
|
3977
|
+
className: className ? className : "rjsf",
|
|
3978
|
+
id: id,
|
|
3979
|
+
name: name,
|
|
3980
|
+
method: method,
|
|
3981
|
+
target: target,
|
|
3982
|
+
action: action,
|
|
3983
|
+
autoComplete: autoComplete,
|
|
3984
|
+
encType: enctype,
|
|
3985
|
+
acceptCharset: acceptcharset,
|
|
3986
|
+
noValidate: noHtml5Validate,
|
|
3987
|
+
onSubmit: this.onSubmit,
|
|
3988
|
+
as: as,
|
|
3989
|
+
ref: this.formElement
|
|
3990
|
+
}, this.renderErrors(registry), /*#__PURE__*/React__default["default"].createElement(_SchemaField, {
|
|
3991
|
+
name: "",
|
|
3992
|
+
schema: schema,
|
|
3993
|
+
uiSchema: uiSchema,
|
|
3994
|
+
errorSchema: errorSchema,
|
|
3995
|
+
idSchema: idSchema,
|
|
3996
|
+
idPrefix: idPrefix,
|
|
3997
|
+
idSeparator: idSeparator,
|
|
3998
|
+
formContext: formContext,
|
|
3999
|
+
formData: formData,
|
|
4000
|
+
onChange: this.onChange,
|
|
4001
|
+
onBlur: this.onBlur,
|
|
4002
|
+
onFocus: this.onFocus,
|
|
4003
|
+
registry: registry,
|
|
4004
|
+
disabled: disabled,
|
|
4005
|
+
readonly: readonly
|
|
4006
|
+
}), children ? children : /*#__PURE__*/React__default["default"].createElement(SubmitButton, {
|
|
4007
|
+
uiSchema: uiSchema
|
|
4008
|
+
}));
|
|
4009
|
+
}
|
|
4010
|
+
|
|
4011
|
+
}
|
|
4012
|
+
|
|
4013
|
+
/** A Higher-Order component that creates a wrapper around a `Form` with the overrides from the `WithThemeProps` */
|
|
4014
|
+
|
|
4015
|
+
function withTheme(themeProps) {
|
|
4016
|
+
return /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
4017
|
+
var _themeProps$templates, _templates;
|
|
4018
|
+
|
|
4019
|
+
let {
|
|
4020
|
+
fields,
|
|
4021
|
+
widgets,
|
|
4022
|
+
templates,
|
|
4023
|
+
...directProps
|
|
4024
|
+
} = _ref;
|
|
4025
|
+
fields = { ...themeProps.fields,
|
|
4026
|
+
...fields
|
|
4027
|
+
};
|
|
4028
|
+
widgets = { ...themeProps.widgets,
|
|
4029
|
+
...widgets
|
|
4030
|
+
};
|
|
4031
|
+
templates = { ...themeProps.templates,
|
|
4032
|
+
...templates,
|
|
4033
|
+
ButtonTemplates: { ...(themeProps === null || themeProps === void 0 ? void 0 : (_themeProps$templates = themeProps.templates) === null || _themeProps$templates === void 0 ? void 0 : _themeProps$templates.ButtonTemplates),
|
|
4034
|
+
...((_templates = templates) === null || _templates === void 0 ? void 0 : _templates.ButtonTemplates)
|
|
4035
|
+
}
|
|
4036
|
+
};
|
|
4037
|
+
return /*#__PURE__*/React__default["default"].createElement(Form, { ...themeProps,
|
|
4038
|
+
...directProps,
|
|
4039
|
+
fields: fields,
|
|
4040
|
+
widgets: widgets,
|
|
4041
|
+
templates: templates,
|
|
4042
|
+
ref: ref
|
|
4043
|
+
});
|
|
4044
|
+
});
|
|
4045
|
+
}
|
|
4046
|
+
|
|
4047
|
+
exports["default"] = Form;
|
|
4048
|
+
exports.getDefaultRegistry = getDefaultRegistry;
|
|
4049
|
+
exports.withTheme = withTheme;
|
|
4050
|
+
|
|
4051
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4052
|
+
|
|
4053
|
+
}));
|
|
4054
|
+
//# sourceMappingURL=core.umd.development.js.map
|