@rjsf/core 6.0.0-alpha.0 → 6.0.0-beta.2

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.
Files changed (301) hide show
  1. package/dist/core.umd.js +1680 -809
  2. package/dist/index.esm.js +2166 -1198
  3. package/dist/index.esm.js.map +4 -4
  4. package/dist/index.js +2221 -1299
  5. package/dist/index.js.map +4 -4
  6. package/lib/components/Form.d.ts +22 -9
  7. package/lib/components/Form.d.ts.map +1 -0
  8. package/lib/components/Form.js +368 -312
  9. package/lib/components/RichDescription.d.ts +20 -0
  10. package/lib/components/RichDescription.d.ts.map +1 -0
  11. package/lib/components/RichDescription.js +17 -0
  12. package/lib/components/fields/ArrayField.d.ts +20 -9
  13. package/lib/components/fields/ArrayField.d.ts.map +1 -0
  14. package/lib/components/fields/ArrayField.js +212 -206
  15. package/lib/components/fields/BooleanField.d.ts +1 -0
  16. package/lib/components/fields/BooleanField.d.ts.map +1 -0
  17. package/lib/components/fields/BooleanField.js +6 -14
  18. package/lib/components/fields/LayoutGridField.d.ts +480 -0
  19. package/lib/components/fields/LayoutGridField.d.ts.map +1 -0
  20. package/lib/components/fields/LayoutGridField.js +711 -0
  21. package/lib/components/fields/LayoutHeaderField.d.ts +12 -0
  22. package/lib/components/fields/LayoutHeaderField.d.ts.map +1 -0
  23. package/lib/components/fields/LayoutHeaderField.js +23 -0
  24. package/lib/components/fields/LayoutMultiSchemaField.d.ts +28 -0
  25. package/lib/components/fields/LayoutMultiSchemaField.d.ts.map +1 -0
  26. package/lib/components/fields/LayoutMultiSchemaField.js +114 -0
  27. package/lib/components/fields/MultiSchemaField.d.ts +1 -0
  28. package/lib/components/fields/MultiSchemaField.d.ts.map +1 -0
  29. package/lib/components/fields/MultiSchemaField.js +31 -31
  30. package/lib/components/fields/NullField.d.ts +1 -0
  31. package/lib/components/fields/NullField.d.ts.map +1 -0
  32. package/lib/components/fields/NullField.js +0 -1
  33. package/lib/components/fields/NumberField.d.ts +1 -0
  34. package/lib/components/fields/NumberField.d.ts.map +1 -0
  35. package/lib/components/fields/NumberField.js +2 -3
  36. package/lib/components/fields/ObjectField.d.ts +1 -0
  37. package/lib/components/fields/ObjectField.d.ts.map +1 -0
  38. package/lib/components/fields/ObjectField.js +146 -141
  39. package/lib/components/fields/SchemaField.d.ts +1 -0
  40. package/lib/components/fields/SchemaField.d.ts.map +1 -0
  41. package/lib/components/fields/SchemaField.js +10 -20
  42. package/lib/components/fields/StringField.d.ts +1 -0
  43. package/lib/components/fields/StringField.d.ts.map +1 -0
  44. package/lib/components/fields/StringField.js +1 -3
  45. package/lib/components/fields/index.d.ts +1 -0
  46. package/lib/components/fields/index.d.ts.map +1 -0
  47. package/lib/components/fields/index.js +14 -9
  48. package/lib/components/templates/ArrayFieldDescriptionTemplate.d.ts +1 -0
  49. package/lib/components/templates/ArrayFieldDescriptionTemplate.d.ts.map +1 -0
  50. package/lib/components/templates/ArrayFieldDescriptionTemplate.js +0 -1
  51. package/lib/components/templates/ArrayFieldItemButtonsTemplate.d.ts +8 -0
  52. package/lib/components/templates/ArrayFieldItemButtonsTemplate.d.ts.map +1 -0
  53. package/lib/components/templates/ArrayFieldItemButtonsTemplate.js +17 -0
  54. package/lib/components/templates/ArrayFieldItemTemplate.d.ts +4 -3
  55. package/lib/components/templates/ArrayFieldItemTemplate.d.ts.map +1 -0
  56. package/lib/components/templates/ArrayFieldItemTemplate.js +7 -6
  57. package/lib/components/templates/ArrayFieldTemplate.d.ts +2 -1
  58. package/lib/components/templates/ArrayFieldTemplate.d.ts.map +1 -0
  59. package/lib/components/templates/ArrayFieldTemplate.js +3 -4
  60. package/lib/components/templates/ArrayFieldTitleTemplate.d.ts +1 -0
  61. package/lib/components/templates/ArrayFieldTitleTemplate.d.ts.map +1 -0
  62. package/lib/components/templates/ArrayFieldTitleTemplate.js +0 -1
  63. package/lib/components/templates/BaseInputTemplate.d.ts +1 -0
  64. package/lib/components/templates/BaseInputTemplate.d.ts.map +1 -0
  65. package/lib/components/templates/BaseInputTemplate.js +0 -1
  66. package/lib/components/templates/ButtonTemplates/AddButton.d.ts +1 -0
  67. package/lib/components/templates/ButtonTemplates/AddButton.d.ts.map +1 -0
  68. package/lib/components/templates/ButtonTemplates/AddButton.js +1 -2
  69. package/lib/components/templates/ButtonTemplates/IconButton.d.ts +1 -0
  70. package/lib/components/templates/ButtonTemplates/IconButton.d.ts.map +1 -0
  71. package/lib/components/templates/ButtonTemplates/IconButton.js +4 -5
  72. package/lib/components/templates/ButtonTemplates/SubmitButton.d.ts +1 -0
  73. package/lib/components/templates/ButtonTemplates/SubmitButton.d.ts.map +1 -0
  74. package/lib/components/templates/ButtonTemplates/SubmitButton.js +0 -1
  75. package/lib/components/templates/ButtonTemplates/index.d.ts +1 -0
  76. package/lib/components/templates/ButtonTemplates/index.d.ts.map +1 -0
  77. package/lib/components/templates/ButtonTemplates/index.js +3 -4
  78. package/lib/components/templates/DescriptionField.d.ts +1 -0
  79. package/lib/components/templates/DescriptionField.d.ts.map +1 -0
  80. package/lib/components/templates/DescriptionField.js +3 -8
  81. package/lib/components/templates/ErrorList.d.ts +1 -0
  82. package/lib/components/templates/ErrorList.d.ts.map +1 -0
  83. package/lib/components/templates/ErrorList.js +0 -1
  84. package/lib/components/templates/FieldErrorTemplate.d.ts +1 -0
  85. package/lib/components/templates/FieldErrorTemplate.d.ts.map +1 -0
  86. package/lib/components/templates/FieldErrorTemplate.js +0 -1
  87. package/lib/components/templates/FieldHelpTemplate.d.ts +1 -0
  88. package/lib/components/templates/FieldHelpTemplate.d.ts.map +1 -0
  89. package/lib/components/templates/FieldHelpTemplate.js +0 -1
  90. package/lib/components/templates/FieldTemplate/FieldTemplate.d.ts +1 -0
  91. package/lib/components/templates/FieldTemplate/FieldTemplate.d.ts.map +1 -0
  92. package/lib/components/templates/FieldTemplate/FieldTemplate.js +1 -2
  93. package/lib/components/templates/FieldTemplate/Label.d.ts +1 -0
  94. package/lib/components/templates/FieldTemplate/Label.d.ts.map +1 -0
  95. package/lib/components/templates/FieldTemplate/Label.js +0 -1
  96. package/lib/components/templates/FieldTemplate/index.d.ts +2 -1
  97. package/lib/components/templates/FieldTemplate/index.d.ts.map +1 -0
  98. package/lib/components/templates/FieldTemplate/index.js +1 -2
  99. package/lib/components/templates/GridTemplate.d.ts +8 -0
  100. package/lib/components/templates/GridTemplate.d.ts.map +1 -0
  101. package/lib/components/templates/GridTemplate.js +10 -0
  102. package/lib/components/templates/ObjectFieldTemplate.d.ts +1 -0
  103. package/lib/components/templates/ObjectFieldTemplate.d.ts.map +1 -0
  104. package/lib/components/templates/ObjectFieldTemplate.js +2 -3
  105. package/lib/components/templates/TitleField.d.ts +1 -0
  106. package/lib/components/templates/TitleField.d.ts.map +1 -0
  107. package/lib/components/templates/TitleField.js +0 -1
  108. package/lib/components/templates/UnsupportedField.d.ts +1 -0
  109. package/lib/components/templates/UnsupportedField.d.ts.map +1 -0
  110. package/lib/components/templates/UnsupportedField.js +0 -1
  111. package/lib/components/templates/WrapIfAdditionalTemplate.d.ts +1 -0
  112. package/lib/components/templates/WrapIfAdditionalTemplate.d.ts.map +1 -0
  113. package/lib/components/templates/WrapIfAdditionalTemplate.js +10 -6
  114. package/lib/components/templates/index.d.ts +1 -0
  115. package/lib/components/templates/index.d.ts.map +1 -0
  116. package/lib/components/templates/index.js +19 -16
  117. package/lib/components/widgets/AltDateTimeWidget.d.ts +1 -0
  118. package/lib/components/widgets/AltDateTimeWidget.d.ts.map +1 -0
  119. package/lib/components/widgets/AltDateTimeWidget.js +0 -1
  120. package/lib/components/widgets/AltDateWidget.d.ts +1 -0
  121. package/lib/components/widgets/AltDateWidget.d.ts.map +1 -0
  122. package/lib/components/widgets/AltDateWidget.js +0 -1
  123. package/lib/components/widgets/CheckboxWidget.d.ts +1 -0
  124. package/lib/components/widgets/CheckboxWidget.d.ts.map +1 -0
  125. package/lib/components/widgets/CheckboxWidget.js +2 -4
  126. package/lib/components/widgets/CheckboxesWidget.d.ts +1 -0
  127. package/lib/components/widgets/CheckboxesWidget.d.ts.map +1 -0
  128. package/lib/components/widgets/CheckboxesWidget.js +0 -1
  129. package/lib/components/widgets/ColorWidget.d.ts +1 -0
  130. package/lib/components/widgets/ColorWidget.d.ts.map +1 -0
  131. package/lib/components/widgets/ColorWidget.js +0 -1
  132. package/lib/components/widgets/DateTimeWidget.d.ts +1 -0
  133. package/lib/components/widgets/DateTimeWidget.d.ts.map +1 -0
  134. package/lib/components/widgets/DateTimeWidget.js +0 -1
  135. package/lib/components/widgets/DateWidget.d.ts +1 -0
  136. package/lib/components/widgets/DateWidget.d.ts.map +1 -0
  137. package/lib/components/widgets/DateWidget.js +0 -1
  138. package/lib/components/widgets/EmailWidget.d.ts +1 -0
  139. package/lib/components/widgets/EmailWidget.d.ts.map +1 -0
  140. package/lib/components/widgets/EmailWidget.js +0 -1
  141. package/lib/components/widgets/FileWidget.d.ts +1 -0
  142. package/lib/components/widgets/FileWidget.d.ts.map +1 -0
  143. package/lib/components/widgets/FileWidget.js +3 -5
  144. package/lib/components/widgets/HiddenWidget.d.ts +1 -0
  145. package/lib/components/widgets/HiddenWidget.d.ts.map +1 -0
  146. package/lib/components/widgets/HiddenWidget.js +0 -1
  147. package/lib/components/widgets/PasswordWidget.d.ts +1 -0
  148. package/lib/components/widgets/PasswordWidget.d.ts.map +1 -0
  149. package/lib/components/widgets/PasswordWidget.js +0 -1
  150. package/lib/components/widgets/RadioWidget.d.ts +1 -0
  151. package/lib/components/widgets/RadioWidget.d.ts.map +1 -0
  152. package/lib/components/widgets/RadioWidget.js +3 -4
  153. package/lib/components/widgets/RangeWidget.d.ts +1 -0
  154. package/lib/components/widgets/RangeWidget.d.ts.map +1 -0
  155. package/lib/components/widgets/RangeWidget.js +0 -1
  156. package/lib/components/widgets/RatingWidget.d.ts +15 -0
  157. package/lib/components/widgets/RatingWidget.d.ts.map +1 -0
  158. package/lib/components/widgets/RatingWidget.js +63 -0
  159. package/lib/components/widgets/SelectWidget.d.ts +1 -0
  160. package/lib/components/widgets/SelectWidget.d.ts.map +1 -0
  161. package/lib/components/widgets/SelectWidget.js +4 -5
  162. package/lib/components/widgets/TextWidget.d.ts +1 -0
  163. package/lib/components/widgets/TextWidget.d.ts.map +1 -0
  164. package/lib/components/widgets/TextWidget.js +0 -1
  165. package/lib/components/widgets/TextareaWidget.d.ts +1 -0
  166. package/lib/components/widgets/TextareaWidget.d.ts.map +1 -0
  167. package/lib/components/widgets/TextareaWidget.js +0 -1
  168. package/lib/components/widgets/TimeWidget.d.ts +1 -0
  169. package/lib/components/widgets/TimeWidget.d.ts.map +1 -0
  170. package/lib/components/widgets/TimeWidget.js +0 -1
  171. package/lib/components/widgets/URLWidget.d.ts +1 -0
  172. package/lib/components/widgets/URLWidget.d.ts.map +1 -0
  173. package/lib/components/widgets/URLWidget.js +0 -1
  174. package/lib/components/widgets/UpDownWidget.d.ts +1 -0
  175. package/lib/components/widgets/UpDownWidget.d.ts.map +1 -0
  176. package/lib/components/widgets/UpDownWidget.js +0 -1
  177. package/lib/components/widgets/index.d.ts +1 -0
  178. package/lib/components/widgets/index.d.ts.map +1 -0
  179. package/lib/components/widgets/index.js +21 -20
  180. package/lib/getDefaultRegistry.d.ts +1 -0
  181. package/lib/getDefaultRegistry.d.ts.map +1 -0
  182. package/lib/getDefaultRegistry.js +3 -4
  183. package/lib/index.d.ts +7 -5
  184. package/lib/index.d.ts.map +1 -0
  185. package/lib/index.js +5 -5
  186. package/lib/tsconfig.tsbuildinfo +1 -1
  187. package/lib/withTheme.d.ts +2 -1
  188. package/lib/withTheme.d.ts.map +1 -0
  189. package/lib/withTheme.js +7 -8
  190. package/package.json +46 -37
  191. package/src/components/Form.tsx +127 -41
  192. package/src/components/RichDescription.tsx +50 -0
  193. package/src/components/fields/ArrayField.tsx +34 -24
  194. package/src/components/fields/BooleanField.tsx +6 -14
  195. package/src/components/fields/LayoutGridField.tsx +967 -0
  196. package/src/components/fields/LayoutHeaderField.tsx +49 -0
  197. package/src/components/fields/LayoutMultiSchemaField.tsx +228 -0
  198. package/src/components/fields/MultiSchemaField.tsx +9 -4
  199. package/src/components/fields/NullField.tsx +1 -1
  200. package/src/components/fields/NumberField.tsx +5 -5
  201. package/src/components/fields/ObjectField.tsx +32 -24
  202. package/src/components/fields/SchemaField.tsx +17 -30
  203. package/src/components/fields/StringField.tsx +2 -2
  204. package/src/components/fields/index.ts +7 -1
  205. package/src/components/templates/ArrayFieldDescriptionTemplate.tsx +2 -2
  206. package/src/components/templates/ArrayFieldItemButtonsTemplate.tsx +85 -0
  207. package/src/components/templates/ArrayFieldItemTemplate.tsx +18 -57
  208. package/src/components/templates/ArrayFieldTemplate.tsx +10 -8
  209. package/src/components/templates/ArrayFieldTitleTemplate.tsx +2 -2
  210. package/src/components/templates/BaseInputTemplate.tsx +4 -4
  211. package/src/components/templates/ButtonTemplates/IconButton.tsx +9 -36
  212. package/src/components/templates/ButtonTemplates/SubmitButton.tsx +1 -1
  213. package/src/components/templates/ButtonTemplates/index.ts +1 -1
  214. package/src/components/templates/DescriptionField.tsx +9 -15
  215. package/src/components/templates/FieldErrorTemplate.tsx +1 -1
  216. package/src/components/templates/FieldHelpTemplate.tsx +1 -1
  217. package/src/components/templates/FieldTemplate/FieldTemplate.tsx +2 -2
  218. package/src/components/templates/GridTemplate.tsx +15 -0
  219. package/src/components/templates/ObjectFieldTemplate.tsx +5 -3
  220. package/src/components/templates/TitleField.tsx +1 -1
  221. package/src/components/templates/UnsupportedField.tsx +1 -1
  222. package/src/components/templates/WrapIfAdditionalTemplate.tsx +14 -4
  223. package/src/components/templates/index.ts +4 -0
  224. package/src/components/widgets/AltDateWidget.tsx +9 -6
  225. package/src/components/widgets/CheckboxWidget.tsx +5 -5
  226. package/src/components/widgets/CheckboxesWidget.tsx +2 -2
  227. package/src/components/widgets/ColorWidget.tsx +1 -1
  228. package/src/components/widgets/DateTimeWidget.tsx +1 -1
  229. package/src/components/widgets/DateWidget.tsx +1 -1
  230. package/src/components/widgets/EmailWidget.tsx +1 -1
  231. package/src/components/widgets/FileWidget.tsx +5 -5
  232. package/src/components/widgets/PasswordWidget.tsx +1 -1
  233. package/src/components/widgets/RadioWidget.tsx +3 -3
  234. package/src/components/widgets/RangeWidget.tsx +1 -1
  235. package/src/components/widgets/RatingWidget.tsx +129 -0
  236. package/src/components/widgets/SelectWidget.tsx +4 -3
  237. package/src/components/widgets/TextWidget.tsx +1 -1
  238. package/src/components/widgets/TextareaWidget.tsx +3 -3
  239. package/src/components/widgets/TimeWidget.tsx +1 -1
  240. package/src/components/widgets/URLWidget.tsx +1 -1
  241. package/src/components/widgets/UpDownWidget.tsx +1 -1
  242. package/src/components/widgets/index.ts +3 -1
  243. package/src/getDefaultRegistry.ts +1 -1
  244. package/src/index.ts +3 -2
  245. package/src/tsconfig.json +14 -6
  246. package/src/withTheme.tsx +4 -3
  247. package/LICENSE.md +0 -201
  248. package/lib/components/Form.js.map +0 -1
  249. package/lib/components/fields/ArrayField.js.map +0 -1
  250. package/lib/components/fields/BooleanField.js.map +0 -1
  251. package/lib/components/fields/MultiSchemaField.js.map +0 -1
  252. package/lib/components/fields/NullField.js.map +0 -1
  253. package/lib/components/fields/NumberField.js.map +0 -1
  254. package/lib/components/fields/ObjectField.js.map +0 -1
  255. package/lib/components/fields/SchemaField.js.map +0 -1
  256. package/lib/components/fields/StringField.js.map +0 -1
  257. package/lib/components/fields/index.js.map +0 -1
  258. package/lib/components/templates/ArrayFieldDescriptionTemplate.js.map +0 -1
  259. package/lib/components/templates/ArrayFieldItemTemplate.js.map +0 -1
  260. package/lib/components/templates/ArrayFieldTemplate.js.map +0 -1
  261. package/lib/components/templates/ArrayFieldTitleTemplate.js.map +0 -1
  262. package/lib/components/templates/BaseInputTemplate.js.map +0 -1
  263. package/lib/components/templates/ButtonTemplates/AddButton.js.map +0 -1
  264. package/lib/components/templates/ButtonTemplates/IconButton.js.map +0 -1
  265. package/lib/components/templates/ButtonTemplates/SubmitButton.js.map +0 -1
  266. package/lib/components/templates/ButtonTemplates/index.js.map +0 -1
  267. package/lib/components/templates/DescriptionField.js.map +0 -1
  268. package/lib/components/templates/ErrorList.js.map +0 -1
  269. package/lib/components/templates/FieldErrorTemplate.js.map +0 -1
  270. package/lib/components/templates/FieldHelpTemplate.js.map +0 -1
  271. package/lib/components/templates/FieldTemplate/FieldTemplate.js.map +0 -1
  272. package/lib/components/templates/FieldTemplate/Label.js.map +0 -1
  273. package/lib/components/templates/FieldTemplate/index.js.map +0 -1
  274. package/lib/components/templates/ObjectFieldTemplate.js.map +0 -1
  275. package/lib/components/templates/TitleField.js.map +0 -1
  276. package/lib/components/templates/UnsupportedField.js.map +0 -1
  277. package/lib/components/templates/WrapIfAdditionalTemplate.js.map +0 -1
  278. package/lib/components/templates/index.js.map +0 -1
  279. package/lib/components/widgets/AltDateTimeWidget.js.map +0 -1
  280. package/lib/components/widgets/AltDateWidget.js.map +0 -1
  281. package/lib/components/widgets/CheckboxWidget.js.map +0 -1
  282. package/lib/components/widgets/CheckboxesWidget.js.map +0 -1
  283. package/lib/components/widgets/ColorWidget.js.map +0 -1
  284. package/lib/components/widgets/DateTimeWidget.js.map +0 -1
  285. package/lib/components/widgets/DateWidget.js.map +0 -1
  286. package/lib/components/widgets/EmailWidget.js.map +0 -1
  287. package/lib/components/widgets/FileWidget.js.map +0 -1
  288. package/lib/components/widgets/HiddenWidget.js.map +0 -1
  289. package/lib/components/widgets/PasswordWidget.js.map +0 -1
  290. package/lib/components/widgets/RadioWidget.js.map +0 -1
  291. package/lib/components/widgets/RangeWidget.js.map +0 -1
  292. package/lib/components/widgets/SelectWidget.js.map +0 -1
  293. package/lib/components/widgets/TextWidget.js.map +0 -1
  294. package/lib/components/widgets/TextareaWidget.js.map +0 -1
  295. package/lib/components/widgets/TimeWidget.js.map +0 -1
  296. package/lib/components/widgets/URLWidget.js.map +0 -1
  297. package/lib/components/widgets/UpDownWidget.js.map +0 -1
  298. package/lib/components/widgets/index.js.map +0 -1
  299. package/lib/getDefaultRegistry.js.map +0 -1
  300. package/lib/index.js.map +0 -1
  301. package/lib/withTheme.js.map +0 -1
@@ -1,14 +1,19 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Component, createRef } from 'react';
3
- import { createSchemaUtils, deepEquals, getTemplate, getUiOptions, isObject, mergeObjects, NAME_KEY, RJSF_ADDITIONAL_PROPERTIES_FLAG, shouldRender, SUBMIT_BTN_OPTIONS_KEY, toErrorList, UI_GLOBAL_OPTIONS_KEY, UI_OPTIONS_KEY, validationDataMerge, } from '@rjsf/utils';
4
- import _forEach from 'lodash/forEach';
5
- import _get from 'lodash/get';
6
- import _isEmpty from 'lodash/isEmpty';
7
- import _pick from 'lodash/pick';
8
- import _toPath from 'lodash/toPath';
9
- import getDefaultRegistry from '../getDefaultRegistry';
3
+ import { createSchemaUtils, deepEquals, getChangedFields, getTemplate, getUiOptions, isObject, mergeObjects, NAME_KEY, RJSF_ADDITIONAL_PROPERTIES_FLAG, shouldRender, SUBMIT_BTN_OPTIONS_KEY, toErrorList, UI_GLOBAL_OPTIONS_KEY, UI_OPTIONS_KEY, validationDataMerge, createErrorHandler, unwrapErrorHandler, } from '@rjsf/utils';
4
+ import _forEach from 'lodash-es/forEach.js';
5
+ import _get from 'lodash-es/get.js';
6
+ import _isEmpty from 'lodash-es/isEmpty.js';
7
+ import _isNil from 'lodash-es/isNil.js';
8
+ import _pick from 'lodash-es/pick.js';
9
+ import _toPath from 'lodash-es/toPath.js';
10
+ import getDefaultRegistry from '../getDefaultRegistry.js';
10
11
  /** The `Form` component renders the outer form and all the fields defined in the `schema` */
11
12
  export default class Form extends Component {
13
+ /** The ref used to hold the `form` element, this needs to be `any` because `tagName` or `_internalFormWrapper` can
14
+ * provide any possible type here
15
+ */
16
+ formElement;
12
17
  /** Constructs the `Form` from the `props`. Will setup the initial state from the props. It will also call the
13
18
  * `onChange` handler if the initially provided `formData` is modified to add missing default values as part of the
14
19
  * state construction.
@@ -17,282 +22,6 @@ export default class Form extends Component {
17
22
  */
18
23
  constructor(props) {
19
24
  super(props);
20
- /** Returns the `formData` with only the elements specified in the `fields` list
21
- *
22
- * @param formData - The data for the `Form`
23
- * @param fields - The fields to keep while filtering
24
- */
25
- this.getUsedFormData = (formData, fields) => {
26
- // For the case of a single input form
27
- if (fields.length === 0 && typeof formData !== 'object') {
28
- return formData;
29
- }
30
- // _pick has incorrect type definition, it works with string[][], because lodash/hasIn supports it
31
- const data = _pick(formData, fields);
32
- if (Array.isArray(formData)) {
33
- return Object.keys(data).map((key) => data[key]);
34
- }
35
- return data;
36
- };
37
- /** Returns the list of field names from inspecting the `pathSchema` as well as using the `formData`
38
- *
39
- * @param pathSchema - The `PathSchema` object for the form
40
- * @param [formData] - The form data to use while checking for empty objects/arrays
41
- */
42
- this.getFieldNames = (pathSchema, formData) => {
43
- const getAllPaths = (_obj, acc = [], paths = [[]]) => {
44
- Object.keys(_obj).forEach((key) => {
45
- if (typeof _obj[key] === 'object') {
46
- const newPaths = paths.map((path) => [...path, key]);
47
- // If an object is marked with additionalProperties, all its keys are valid
48
- if (_obj[key][RJSF_ADDITIONAL_PROPERTIES_FLAG] && _obj[key][NAME_KEY] !== '') {
49
- acc.push(_obj[key][NAME_KEY]);
50
- }
51
- else {
52
- getAllPaths(_obj[key], acc, newPaths);
53
- }
54
- }
55
- else if (key === NAME_KEY && _obj[key] !== '') {
56
- paths.forEach((path) => {
57
- const formValue = _get(formData, path);
58
- // adds path to fieldNames if it points to a value
59
- // or an empty object/array
60
- if (typeof formValue !== 'object' ||
61
- _isEmpty(formValue) ||
62
- (Array.isArray(formValue) && formValue.every((val) => typeof val !== 'object'))) {
63
- acc.push(path);
64
- }
65
- });
66
- }
67
- });
68
- return acc;
69
- };
70
- return getAllPaths(pathSchema);
71
- };
72
- /** Returns the `formData` after filtering to remove any extra data not in a form field
73
- *
74
- * @param formData - The data for the `Form`
75
- * @returns The `formData` after omitting extra data
76
- */
77
- this.omitExtraData = (formData) => {
78
- const { schema, schemaUtils } = this.state;
79
- const retrievedSchema = schemaUtils.retrieveSchema(schema, formData);
80
- const pathSchema = schemaUtils.toPathSchema(retrievedSchema, '', formData);
81
- const fieldNames = this.getFieldNames(pathSchema, formData);
82
- const newFormData = this.getUsedFormData(formData, fieldNames);
83
- return newFormData;
84
- };
85
- /** Function to handle changes made to a field in the `Form`. This handler receives an entirely new copy of the
86
- * `formData` along with a new `ErrorSchema`. It will first update the `formData` with any missing default fields and
87
- * then, if `omitExtraData` and `liveOmit` are turned on, the `formData` will be filtered to remove any extra data not
88
- * in a form field. Then, the resulting formData will be validated if required. The state will be updated with the new
89
- * updated (potentially filtered) `formData`, any errors that resulted from validation. Finally the `onChange`
90
- * callback will be called if specified with the updated state.
91
- *
92
- * @param formData - The new form data from a change to a field
93
- * @param newErrorSchema - The new `ErrorSchema` based on the field change
94
- * @param id - The id of the field that caused the change
95
- */
96
- this.onChange = (formData, newErrorSchema, id) => {
97
- const { extraErrors, omitExtraData, liveOmit, noValidate, liveValidate, onChange } = this.props;
98
- const { schemaUtils, schema, retrievedSchema } = this.state;
99
- if (isObject(formData) || Array.isArray(formData)) {
100
- const newState = this.getStateFromProps(this.props, formData, retrievedSchema);
101
- formData = newState.formData;
102
- }
103
- const mustValidate = !noValidate && liveValidate;
104
- let state = { formData, schema };
105
- let newFormData = formData;
106
- let _retrievedSchema;
107
- if (omitExtraData === true && liveOmit === true) {
108
- newFormData = this.omitExtraData(formData);
109
- state = {
110
- formData: newFormData,
111
- };
112
- }
113
- if (mustValidate) {
114
- const schemaValidation = this.validate(newFormData, schema, schemaUtils, retrievedSchema);
115
- let errors = schemaValidation.errors;
116
- let errorSchema = schemaValidation.errorSchema;
117
- const schemaValidationErrors = errors;
118
- const schemaValidationErrorSchema = errorSchema;
119
- if (extraErrors) {
120
- const merged = validationDataMerge(schemaValidation, extraErrors);
121
- errorSchema = merged.errorSchema;
122
- errors = merged.errors;
123
- }
124
- // Merging 'newErrorSchema' into 'errorSchema' to display the custom raised errors.
125
- if (newErrorSchema) {
126
- const filteredErrors = this.filterErrorsBasedOnSchema(newErrorSchema, retrievedSchema, newFormData);
127
- errorSchema = mergeObjects(errorSchema, filteredErrors, 'preventDuplicates');
128
- }
129
- state = {
130
- formData: newFormData,
131
- errors,
132
- errorSchema,
133
- schemaValidationErrors,
134
- schemaValidationErrorSchema,
135
- };
136
- }
137
- else if (!noValidate && newErrorSchema) {
138
- const errorSchema = extraErrors
139
- ? mergeObjects(newErrorSchema, extraErrors, 'preventDuplicates')
140
- : newErrorSchema;
141
- state = {
142
- formData: newFormData,
143
- errorSchema: errorSchema,
144
- errors: toErrorList(errorSchema),
145
- };
146
- }
147
- if (_retrievedSchema) {
148
- state.retrievedSchema = _retrievedSchema;
149
- }
150
- this.setState(state, () => onChange && onChange({ ...this.state, ...state }, id));
151
- };
152
- /**
153
- * Callback function to handle reset form data.
154
- * - Reset all fields with default values.
155
- * - Reset validations and errors
156
- *
157
- */
158
- this.reset = () => {
159
- const { onChange } = this.props;
160
- const newState = this.getStateFromProps(this.props, undefined);
161
- const newFormData = newState.formData;
162
- const state = {
163
- formData: newFormData,
164
- errorSchema: {},
165
- errors: [],
166
- schemaValidationErrors: [],
167
- schemaValidationErrorSchema: {},
168
- };
169
- this.setState(state, () => onChange && onChange({ ...this.state, ...state }));
170
- };
171
- /** Callback function to handle when a field on the form is blurred. Calls the `onBlur` callback for the `Form` if it
172
- * was provided.
173
- *
174
- * @param id - The unique `id` of the field that was blurred
175
- * @param data - The data associated with the field that was blurred
176
- */
177
- this.onBlur = (id, data) => {
178
- const { onBlur } = this.props;
179
- if (onBlur) {
180
- onBlur(id, data);
181
- }
182
- };
183
- /** Callback function to handle when a field on the form is focused. Calls the `onFocus` callback for the `Form` if it
184
- * was provided.
185
- *
186
- * @param id - The unique `id` of the field that was focused
187
- * @param data - The data associated with the field that was focused
188
- */
189
- this.onFocus = (id, data) => {
190
- const { onFocus } = this.props;
191
- if (onFocus) {
192
- onFocus(id, data);
193
- }
194
- };
195
- /** Callback function to handle when the form is submitted. First, it prevents the default event behavior. Nothing
196
- * happens if the target and currentTarget of the event are not the same. It will omit any extra data in the
197
- * `formData` in the state if `omitExtraData` is true. It will validate the resulting `formData`, reporting errors
198
- * via the `onError()` callback unless validation is disabled. Finally, it will add in any `extraErrors` and then call
199
- * back the `onSubmit` callback if it was provided.
200
- *
201
- * @param event - The submit HTML form event
202
- */
203
- this.onSubmit = (event) => {
204
- event.preventDefault();
205
- if (event.target !== event.currentTarget) {
206
- return;
207
- }
208
- event.persist();
209
- const { omitExtraData, extraErrors, noValidate, onSubmit } = this.props;
210
- let { formData: newFormData } = this.state;
211
- if (omitExtraData === true) {
212
- newFormData = this.omitExtraData(newFormData);
213
- }
214
- if (noValidate || this.validateFormWithFormData(newFormData)) {
215
- // There are no errors generated through schema validation.
216
- // Check for user provided errors and update state accordingly.
217
- const errorSchema = extraErrors || {};
218
- const errors = extraErrors ? toErrorList(extraErrors) : [];
219
- this.setState({
220
- formData: newFormData,
221
- errors,
222
- errorSchema,
223
- schemaValidationErrors: [],
224
- schemaValidationErrorSchema: {},
225
- }, () => {
226
- if (onSubmit) {
227
- onSubmit({ ...this.state, formData: newFormData, status: 'submitted' }, event);
228
- }
229
- });
230
- }
231
- };
232
- /** Provides a function that can be used to programmatically submit the `Form` */
233
- this.submit = () => {
234
- if (this.formElement.current) {
235
- const submitCustomEvent = new CustomEvent('submit', {
236
- cancelable: true,
237
- });
238
- submitCustomEvent.preventDefault();
239
- this.formElement.current.dispatchEvent(submitCustomEvent);
240
- this.formElement.current.requestSubmit();
241
- }
242
- };
243
- /** Validates the form using the given `formData`. For use on form submission or on programmatic validation.
244
- * If `onError` is provided, then it will be called with the list of errors.
245
- *
246
- * @param formData - The form data to validate
247
- * @returns - True if the form is valid, false otherwise.
248
- */
249
- this.validateFormWithFormData = (formData) => {
250
- const { extraErrors, extraErrorsBlockSubmit, focusOnFirstError, onError } = this.props;
251
- const { errors: prevErrors } = this.state;
252
- const schemaValidation = this.validate(formData);
253
- let errors = schemaValidation.errors;
254
- let errorSchema = schemaValidation.errorSchema;
255
- const schemaValidationErrors = errors;
256
- const schemaValidationErrorSchema = errorSchema;
257
- const hasError = errors.length > 0 || (extraErrors && extraErrorsBlockSubmit);
258
- if (hasError) {
259
- if (extraErrors) {
260
- const merged = validationDataMerge(schemaValidation, extraErrors);
261
- errorSchema = merged.errorSchema;
262
- errors = merged.errors;
263
- }
264
- if (focusOnFirstError) {
265
- if (typeof focusOnFirstError === 'function') {
266
- focusOnFirstError(errors[0]);
267
- }
268
- else {
269
- this.focusOnError(errors[0]);
270
- }
271
- }
272
- this.setState({
273
- errors,
274
- errorSchema,
275
- schemaValidationErrors,
276
- schemaValidationErrorSchema,
277
- }, () => {
278
- if (onError) {
279
- onError(errors);
280
- }
281
- else {
282
- console.error('Form validation failed', errors);
283
- }
284
- });
285
- }
286
- else if (prevErrors.length > 0) {
287
- this.setState({
288
- errors: [],
289
- errorSchema: {},
290
- schemaValidationErrors: [],
291
- schemaValidationErrorSchema: {},
292
- });
293
- }
294
- return !hasError;
295
- };
296
25
  if (!props.validator) {
297
26
  throw new Error('A validator is required for Form functionality to work');
298
27
  }
@@ -322,13 +51,16 @@ export default class Form extends Component {
322
51
  */
323
52
  getSnapshotBeforeUpdate(prevProps, prevState) {
324
53
  if (!deepEquals(this.props, prevProps)) {
54
+ const formDataChangedFields = getChangedFields(this.props.formData, prevProps.formData);
325
55
  const isSchemaChanged = !deepEquals(prevProps.schema, this.props.schema);
326
- const isFormDataChanged = !deepEquals(prevProps.formData, this.props.formData);
56
+ // When formData is not an object, getChangedFields returns an empty array.
57
+ // In this case, deepEquals is most needed to check again.
58
+ const isFormDataChanged = formDataChangedFields.length > 0 || !deepEquals(prevProps.formData, this.props.formData);
327
59
  const nextState = this.getStateFromProps(this.props, this.props.formData,
328
60
  // If the `schema` has changed, we need to update the retrieved schema.
329
61
  // Or if the `formData` changes, for example in the case of a schema with dependencies that need to
330
62
  // match one of the subSchemas, the retrieved schema must be updated.
331
- isSchemaChanged || isFormDataChanged ? undefined : this.state.retrievedSchema, isSchemaChanged);
63
+ isSchemaChanged || isFormDataChanged ? undefined : this.state.retrievedSchema, isSchemaChanged, formDataChangedFields);
332
64
  const shouldUpdate = !deepEquals(nextState, prevState);
333
65
  return { nextState, shouldUpdate };
334
66
  }
@@ -342,9 +74,6 @@ export default class Form extends Component {
342
74
  * If an update is required, it applies the next state and, if needed, triggers the `onChange` handler to inform about
343
75
  * changes.
344
76
  *
345
- * This method effectively replaces the deprecated `UNSAFE_componentWillReceiveProps`, providing a safer alternative
346
- * to handle prop changes and state updates.
347
- *
348
77
  * @param _ - The previous set of props.
349
78
  * @param prevState - The previous state of the component before the update.
350
79
  * @param snapshot - The value returned from `getSnapshotBeforeUpdate`.
@@ -368,10 +97,10 @@ export default class Form extends Component {
368
97
  * @param inputFormData - The new or current data for the `Form`
369
98
  * @param retrievedSchema - An expanded schema, if not provided, it will be retrieved from the `schema` and `formData`.
370
99
  * @param isSchemaChanged - A flag indicating whether the schema has changed.
100
+ * @param formDataChangedFields - The changed fields of `formData`
371
101
  * @returns - The new state for the `Form`
372
102
  */
373
- getStateFromProps(props, inputFormData, retrievedSchema, isSchemaChanged = false) {
374
- var _a;
103
+ getStateFromProps(props, inputFormData, retrievedSchema, isSchemaChanged = false, formDataChangedFields = []) {
375
104
  const state = this.state || {};
376
105
  const schema = 'schema' in props ? props.schema : this.props.schema;
377
106
  const uiSchema = ('uiSchema' in props ? props.uiSchema : this.props.uiSchema) || {};
@@ -382,13 +111,16 @@ export default class Form extends Component {
382
111
  const experimental_defaultFormStateBehavior = 'experimental_defaultFormStateBehavior' in props
383
112
  ? props.experimental_defaultFormStateBehavior
384
113
  : this.props.experimental_defaultFormStateBehavior;
114
+ const experimental_customMergeAllOf = 'experimental_customMergeAllOf' in props
115
+ ? props.experimental_customMergeAllOf
116
+ : this.props.experimental_customMergeAllOf;
385
117
  let schemaUtils = state.schemaUtils;
386
118
  if (!schemaUtils ||
387
- schemaUtils.doesSchemaUtilsDiffer(props.validator, rootSchema, experimental_defaultFormStateBehavior)) {
388
- schemaUtils = createSchemaUtils(props.validator, rootSchema, experimental_defaultFormStateBehavior);
119
+ schemaUtils.doesSchemaUtilsDiffer(props.validator, rootSchema, experimental_defaultFormStateBehavior, experimental_customMergeAllOf)) {
120
+ schemaUtils = createSchemaUtils(props.validator, rootSchema, experimental_defaultFormStateBehavior, experimental_customMergeAllOf);
389
121
  }
390
122
  const formData = schemaUtils.getDefaultFormState(schema, inputFormData);
391
- const _retrievedSchema = retrievedSchema !== null && retrievedSchema !== void 0 ? retrievedSchema : schemaUtils.retrieveSchema(schema, formData);
123
+ const _retrievedSchema = this.updateRetrievedSchema(retrievedSchema ?? schemaUtils.retrieveSchema(schema, formData));
392
124
  const getCurrentErrors = () => {
393
125
  // If the `props.noValidate` option is set or the schema has changed, we reset the error state.
394
126
  if (props.noValidate || isSchemaChanged) {
@@ -412,13 +144,13 @@ export default class Form extends Component {
412
144
  if (mustValidate) {
413
145
  const schemaValidation = this.validate(formData, schema, schemaUtils, _retrievedSchema);
414
146
  errors = schemaValidation.errors;
415
- // If the schema has changed, we do not merge state.errorSchema.
147
+ // If retrievedSchema is undefined which means the schema or formData has changed, we do not merge state.
416
148
  // Else in the case where it hasn't changed, we merge 'state.errorSchema' with 'schemaValidation.errorSchema.' This done to display the raised field error.
417
- if (isSchemaChanged) {
149
+ if (retrievedSchema === undefined) {
418
150
  errorSchema = schemaValidation.errorSchema;
419
151
  }
420
152
  else {
421
- errorSchema = mergeObjects((_a = this.state) === null || _a === void 0 ? void 0 : _a.errorSchema, schemaValidation.errorSchema, 'preventDuplicates');
153
+ errorSchema = mergeObjects(this.state?.errorSchema, schemaValidation.errorSchema, 'preventDuplicates');
422
154
  }
423
155
  schemaValidationErrors = errors;
424
156
  schemaValidationErrorSchema = errorSchema;
@@ -427,6 +159,13 @@ export default class Form extends Component {
427
159
  const currentErrors = getCurrentErrors();
428
160
  errors = currentErrors.errors;
429
161
  errorSchema = currentErrors.errorSchema;
162
+ if (formDataChangedFields.length > 0) {
163
+ const newErrorSchema = formDataChangedFields.reduce((acc, key) => {
164
+ acc[key] = undefined;
165
+ return acc;
166
+ }, {});
167
+ errorSchema = schemaValidationErrorSchema = mergeObjects(currentErrors.errorSchema, newErrorSchema, 'preventDuplicates');
168
+ }
430
169
  }
431
170
  if (props.extraErrors) {
432
171
  const merged = validationDataMerge({ errorSchema, errors }, props.extraErrors);
@@ -458,6 +197,21 @@ export default class Form extends Component {
458
197
  shouldComponentUpdate(nextProps, nextState) {
459
198
  return shouldRender(this, nextProps, nextState);
460
199
  }
200
+ /** Gets the previously raised customValidate errors.
201
+ *
202
+ * @returns the previous customValidate errors
203
+ */
204
+ getPreviousCustomValidateErrors() {
205
+ const { customValidate, uiSchema } = this.props;
206
+ const prevFormData = this.state.formData;
207
+ let customValidateErrors = {};
208
+ if (typeof customValidate === 'function') {
209
+ const errorHandler = customValidate(prevFormData, createErrorHandler(prevFormData), uiSchema);
210
+ const userErrorSchema = unwrapErrorHandler(errorHandler);
211
+ customValidateErrors = userErrorSchema;
212
+ }
213
+ return customValidateErrors;
214
+ }
461
215
  /** Validates the `formData` against the `schema` using the `altSchemaUtils` (if provided otherwise it uses the
462
216
  * `schemaUtils` in the state), returning the results.
463
217
  *
@@ -468,7 +222,7 @@ export default class Form extends Component {
468
222
  validate(formData, schema = this.props.schema, altSchemaUtils, retrievedSchema) {
469
223
  const schemaUtils = altSchemaUtils ? altSchemaUtils : this.state.schemaUtils;
470
224
  const { customValidate, transformErrors, uiSchema } = this.props;
471
- const resolvedSchema = retrievedSchema !== null && retrievedSchema !== void 0 ? retrievedSchema : schemaUtils.retrieveSchema(schema, formData);
225
+ const resolvedSchema = retrievedSchema ?? schemaUtils.retrieveSchema(schema, formData);
472
226
  return schemaUtils
473
227
  .getValidator()
474
228
  .validateFormData(formData, resolvedSchema, customValidate, transformErrors, uiSchema);
@@ -484,34 +238,273 @@ export default class Form extends Component {
484
238
  }
485
239
  return null;
486
240
  }
241
+ /** Returns the `formData` with only the elements specified in the `fields` list
242
+ *
243
+ * @param formData - The data for the `Form`
244
+ * @param fields - The fields to keep while filtering
245
+ */
246
+ getUsedFormData = (formData, fields) => {
247
+ // For the case of a single input form
248
+ if (fields.length === 0 && typeof formData !== 'object') {
249
+ return formData;
250
+ }
251
+ // _pick has incorrect type definition, it works with string[][], because lodash/hasIn supports it
252
+ const data = _pick(formData, fields);
253
+ if (Array.isArray(formData)) {
254
+ return Object.keys(data).map((key) => data[key]);
255
+ }
256
+ return data;
257
+ };
258
+ /** Returns the list of field names from inspecting the `pathSchema` as well as using the `formData`
259
+ *
260
+ * @param pathSchema - The `PathSchema` object for the form
261
+ * @param [formData] - The form data to use while checking for empty objects/arrays
262
+ */
263
+ getFieldNames = (pathSchema, formData) => {
264
+ const getAllPaths = (_obj, acc = [], paths = [[]]) => {
265
+ Object.keys(_obj).forEach((key) => {
266
+ if (typeof _obj[key] === 'object') {
267
+ const newPaths = paths.map((path) => [...path, key]);
268
+ // If an object is marked with additionalProperties, all its keys are valid
269
+ if (_obj[key][RJSF_ADDITIONAL_PROPERTIES_FLAG] && _obj[key][NAME_KEY] !== '') {
270
+ acc.push(_obj[key][NAME_KEY]);
271
+ }
272
+ else {
273
+ getAllPaths(_obj[key], acc, newPaths);
274
+ }
275
+ }
276
+ else if (key === NAME_KEY && _obj[key] !== '') {
277
+ paths.forEach((path) => {
278
+ const formValue = _get(formData, path);
279
+ // adds path to fieldNames if it points to a value
280
+ // or an empty object/array
281
+ if (typeof formValue !== 'object' ||
282
+ _isEmpty(formValue) ||
283
+ (Array.isArray(formValue) && formValue.every((val) => typeof val !== 'object'))) {
284
+ acc.push(path);
285
+ }
286
+ });
287
+ }
288
+ });
289
+ return acc;
290
+ };
291
+ return getAllPaths(pathSchema);
292
+ };
293
+ /** Returns the `formData` after filtering to remove any extra data not in a form field
294
+ *
295
+ * @param formData - The data for the `Form`
296
+ * @returns The `formData` after omitting extra data
297
+ */
298
+ omitExtraData = (formData) => {
299
+ const { schema, schemaUtils } = this.state;
300
+ const retrievedSchema = schemaUtils.retrieveSchema(schema, formData);
301
+ const pathSchema = schemaUtils.toPathSchema(retrievedSchema, '', formData);
302
+ const fieldNames = this.getFieldNames(pathSchema, formData);
303
+ const newFormData = this.getUsedFormData(formData, fieldNames);
304
+ return newFormData;
305
+ };
487
306
  // Filtering errors based on your retrieved schema to only show errors for properties in the selected branch.
488
307
  filterErrorsBasedOnSchema(schemaErrors, resolvedSchema, formData) {
489
308
  const { retrievedSchema, schemaUtils } = this.state;
490
- const _retrievedSchema = resolvedSchema !== null && resolvedSchema !== void 0 ? resolvedSchema : retrievedSchema;
309
+ const _retrievedSchema = resolvedSchema ?? retrievedSchema;
491
310
  const pathSchema = schemaUtils.toPathSchema(_retrievedSchema, '', formData);
492
311
  const fieldNames = this.getFieldNames(pathSchema, formData);
493
312
  const filteredErrors = _pick(schemaErrors, fieldNames);
494
313
  // If the root schema is of a primitive type, do not filter out the __errors
495
- if ((resolvedSchema === null || resolvedSchema === void 0 ? void 0 : resolvedSchema.type) !== 'object' && (resolvedSchema === null || resolvedSchema === void 0 ? void 0 : resolvedSchema.type) !== 'array') {
314
+ if (resolvedSchema?.type !== 'object' && resolvedSchema?.type !== 'array') {
496
315
  filteredErrors.__errors = schemaErrors.__errors;
497
316
  }
498
- // Removing undefined and empty errors.
499
- const filterUndefinedErrors = (errors) => {
317
+ const prevCustomValidateErrors = this.getPreviousCustomValidateErrors();
318
+ // Filtering out the previous raised customValidate errors so that they are cleared when no longer valid.
319
+ const filterPreviousCustomErrors = (errors = [], prevCustomErrors) => {
320
+ if (errors.length === 0) {
321
+ return errors;
322
+ }
323
+ return errors.filter((error) => {
324
+ return !prevCustomErrors.includes(error);
325
+ });
326
+ };
327
+ // Removing undefined, null and empty errors.
328
+ const filterNilOrEmptyErrors = (errors, previousCustomValidateErrors = {}) => {
500
329
  _forEach(errors, (errorAtKey, errorKey) => {
501
- if (errorAtKey === undefined) {
330
+ const prevCustomValidateErrorAtKey = previousCustomValidateErrors[errorKey];
331
+ if (_isNil(errorAtKey) || (Array.isArray(errorAtKey) && errorAtKey.length === 0)) {
502
332
  delete errors[errorKey];
503
333
  }
334
+ else if (isObject(errorAtKey) &&
335
+ isObject(prevCustomValidateErrorAtKey) &&
336
+ Array.isArray(prevCustomValidateErrorAtKey?.__errors)) {
337
+ // if previous customValidate error is an object and has __errors array, filter out the errors previous customValidate errors.
338
+ errors[errorKey] = filterPreviousCustomErrors(errorAtKey.__errors, prevCustomValidateErrorAtKey.__errors);
339
+ }
504
340
  else if (typeof errorAtKey === 'object' && !Array.isArray(errorAtKey.__errors)) {
505
- filterUndefinedErrors(errorAtKey);
341
+ filterNilOrEmptyErrors(errorAtKey, previousCustomValidateErrors[errorKey]);
506
342
  }
507
343
  });
508
344
  return errors;
509
345
  };
510
- return filterUndefinedErrors(filteredErrors);
346
+ return filterNilOrEmptyErrors(filteredErrors, prevCustomValidateErrors);
511
347
  }
348
+ /** Function to handle changes made to a field in the `Form`. This handler receives an entirely new copy of the
349
+ * `formData` along with a new `ErrorSchema`. It will first update the `formData` with any missing default fields and
350
+ * then, if `omitExtraData` and `liveOmit` are turned on, the `formData` will be filtered to remove any extra data not
351
+ * in a form field. Then, the resulting formData will be validated if required. The state will be updated with the new
352
+ * updated (potentially filtered) `formData`, any errors that resulted from validation. Finally the `onChange`
353
+ * callback will be called if specified with the updated state.
354
+ *
355
+ * @param formData - The new form data from a change to a field
356
+ * @param newErrorSchema - The new `ErrorSchema` based on the field change
357
+ * @param id - The id of the field that caused the change
358
+ */
359
+ onChange = (formData, newErrorSchema, id) => {
360
+ const { extraErrors, omitExtraData, liveOmit, noValidate, liveValidate, onChange } = this.props;
361
+ const { schemaUtils, schema } = this.state;
362
+ let retrievedSchema = this.state.retrievedSchema;
363
+ if (isObject(formData) || Array.isArray(formData)) {
364
+ const newState = this.getStateFromProps(this.props, formData);
365
+ formData = newState.formData;
366
+ retrievedSchema = newState.retrievedSchema;
367
+ }
368
+ const mustValidate = !noValidate && liveValidate;
369
+ let state = { formData, schema };
370
+ let newFormData = formData;
371
+ if (omitExtraData === true && liveOmit === true) {
372
+ newFormData = this.omitExtraData(formData);
373
+ state = {
374
+ formData: newFormData,
375
+ };
376
+ }
377
+ if (mustValidate) {
378
+ const schemaValidation = this.validate(newFormData, schema, schemaUtils, retrievedSchema);
379
+ let errors = schemaValidation.errors;
380
+ let errorSchema = schemaValidation.errorSchema;
381
+ const schemaValidationErrors = errors;
382
+ const schemaValidationErrorSchema = errorSchema;
383
+ if (extraErrors) {
384
+ const merged = validationDataMerge(schemaValidation, extraErrors);
385
+ errorSchema = merged.errorSchema;
386
+ errors = merged.errors;
387
+ }
388
+ // Merging 'newErrorSchema' into 'errorSchema' to display the custom raised errors.
389
+ if (newErrorSchema) {
390
+ const filteredErrors = this.filterErrorsBasedOnSchema(newErrorSchema, retrievedSchema, newFormData);
391
+ errorSchema = mergeObjects(errorSchema, filteredErrors, 'preventDuplicates');
392
+ }
393
+ state = {
394
+ formData: newFormData,
395
+ errors,
396
+ errorSchema,
397
+ schemaValidationErrors,
398
+ schemaValidationErrorSchema,
399
+ };
400
+ }
401
+ else if (!noValidate && newErrorSchema) {
402
+ const errorSchema = extraErrors
403
+ ? mergeObjects(newErrorSchema, extraErrors, 'preventDuplicates')
404
+ : newErrorSchema;
405
+ state = {
406
+ formData: newFormData,
407
+ errorSchema: errorSchema,
408
+ errors: toErrorList(errorSchema),
409
+ };
410
+ }
411
+ this.setState(state, () => onChange && onChange({ ...this.state, ...state }, id));
412
+ };
413
+ /**
414
+ * If the retrievedSchema has changed the new retrievedSchema is returned.
415
+ * Otherwise, the old retrievedSchema is returned to persist reference.
416
+ * - This ensures that AJV retrieves the schema from the cache when it has not changed,
417
+ * avoiding the performance cost of recompiling the schema.
418
+ *
419
+ * @param retrievedSchema The new retrieved schema.
420
+ * @returns The new retrieved schema if it has changed, else the old retrieved schema.
421
+ */
422
+ updateRetrievedSchema(retrievedSchema) {
423
+ const isTheSame = deepEquals(retrievedSchema, this.state?.retrievedSchema);
424
+ return isTheSame ? this.state.retrievedSchema : retrievedSchema;
425
+ }
426
+ /**
427
+ * Callback function to handle reset form data.
428
+ * - Reset all fields with default values.
429
+ * - Reset validations and errors
430
+ *
431
+ */
432
+ reset = () => {
433
+ const { onChange } = this.props;
434
+ const newState = this.getStateFromProps(this.props, undefined);
435
+ const newFormData = newState.formData;
436
+ const state = {
437
+ formData: newFormData,
438
+ errorSchema: {},
439
+ errors: [],
440
+ schemaValidationErrors: [],
441
+ schemaValidationErrorSchema: {},
442
+ };
443
+ this.setState(state, () => onChange && onChange({ ...this.state, ...state }));
444
+ };
445
+ /** Callback function to handle when a field on the form is blurred. Calls the `onBlur` callback for the `Form` if it
446
+ * was provided.
447
+ *
448
+ * @param id - The unique `id` of the field that was blurred
449
+ * @param data - The data associated with the field that was blurred
450
+ */
451
+ onBlur = (id, data) => {
452
+ const { onBlur } = this.props;
453
+ if (onBlur) {
454
+ onBlur(id, data);
455
+ }
456
+ };
457
+ /** Callback function to handle when a field on the form is focused. Calls the `onFocus` callback for the `Form` if it
458
+ * was provided.
459
+ *
460
+ * @param id - The unique `id` of the field that was focused
461
+ * @param data - The data associated with the field that was focused
462
+ */
463
+ onFocus = (id, data) => {
464
+ const { onFocus } = this.props;
465
+ if (onFocus) {
466
+ onFocus(id, data);
467
+ }
468
+ };
469
+ /** Callback function to handle when the form is submitted. First, it prevents the default event behavior. Nothing
470
+ * happens if the target and currentTarget of the event are not the same. It will omit any extra data in the
471
+ * `formData` in the state if `omitExtraData` is true. It will validate the resulting `formData`, reporting errors
472
+ * via the `onError()` callback unless validation is disabled. Finally, it will add in any `extraErrors` and then call
473
+ * back the `onSubmit` callback if it was provided.
474
+ *
475
+ * @param event - The submit HTML form event
476
+ */
477
+ onSubmit = (event) => {
478
+ event.preventDefault();
479
+ if (event.target !== event.currentTarget) {
480
+ return;
481
+ }
482
+ event.persist();
483
+ const { omitExtraData, extraErrors, noValidate, onSubmit } = this.props;
484
+ let { formData: newFormData } = this.state;
485
+ if (omitExtraData === true) {
486
+ newFormData = this.omitExtraData(newFormData);
487
+ }
488
+ if (noValidate || this.validateFormWithFormData(newFormData)) {
489
+ // There are no errors generated through schema validation.
490
+ // Check for user provided errors and update state accordingly.
491
+ const errorSchema = extraErrors || {};
492
+ const errors = extraErrors ? toErrorList(extraErrors) : [];
493
+ this.setState({
494
+ formData: newFormData,
495
+ errors,
496
+ errorSchema,
497
+ schemaValidationErrors: [],
498
+ schemaValidationErrorSchema: {},
499
+ }, () => {
500
+ if (onSubmit) {
501
+ onSubmit({ ...this.state, formData: newFormData, status: 'submitted' }, event);
502
+ }
503
+ });
504
+ }
505
+ };
512
506
  /** Returns the registry for the form */
513
507
  getRegistry() {
514
- var _a;
515
508
  const { translateString: customTranslateString, uiSchema = {} } = this.props;
516
509
  const { schemaUtils } = this.state;
517
510
  const { fields, templates, widgets, formContext, translateString } = getDefaultRegistry();
@@ -522,7 +515,7 @@ export default class Form extends Component {
522
515
  ...this.props.templates,
523
516
  ButtonTemplates: {
524
517
  ...templates.ButtonTemplates,
525
- ...(_a = this.props.templates) === null || _a === void 0 ? void 0 : _a.ButtonTemplates,
518
+ ...this.props.templates?.ButtonTemplates,
526
519
  },
527
520
  },
528
521
  widgets: { ...widgets, ...this.props.widgets },
@@ -533,6 +526,17 @@ export default class Form extends Component {
533
526
  globalUiOptions: uiSchema[UI_GLOBAL_OPTIONS_KEY],
534
527
  };
535
528
  }
529
+ /** Provides a function that can be used to programmatically submit the `Form` */
530
+ submit = () => {
531
+ if (this.formElement.current) {
532
+ const submitCustomEvent = new CustomEvent('submit', {
533
+ cancelable: true,
534
+ });
535
+ submitCustomEvent.preventDefault();
536
+ this.formElement.current.dispatchEvent(submitCustomEvent);
537
+ this.formElement.current.requestSubmit();
538
+ }
539
+ };
536
540
  /** Attempts to focus on the field associated with the `error`. Uses the `property` field to compute path of the error
537
541
  * field, then, using the `idPrefix` and `idSeparator` converts that path into an id. Then the input element with that
538
542
  * id is attempted to be found using the `formElement` ref. If it is located, then it is focused.
@@ -555,7 +559,7 @@ export default class Form extends Component {
555
559
  let field = this.formElement.current.elements[elementId];
556
560
  if (!field) {
557
561
  // if not an exact match, try finding an input starting with the element id (like radio buttons or checkboxes)
558
- field = this.formElement.current.querySelector(`input[id^=${elementId}`);
562
+ field = this.formElement.current.querySelector(`input[id^="${elementId}"`);
559
563
  }
560
564
  if (field && field.length) {
561
565
  // If we got a list with length > 0
@@ -565,6 +569,59 @@ export default class Form extends Component {
565
569
  field.focus();
566
570
  }
567
571
  }
572
+ /** Validates the form using the given `formData`. For use on form submission or on programmatic validation.
573
+ * If `onError` is provided, then it will be called with the list of errors.
574
+ *
575
+ * @param formData - The form data to validate
576
+ * @returns - True if the form is valid, false otherwise.
577
+ */
578
+ validateFormWithFormData = (formData) => {
579
+ const { extraErrors, extraErrorsBlockSubmit, focusOnFirstError, onError } = this.props;
580
+ const { errors: prevErrors } = this.state;
581
+ const schemaValidation = this.validate(formData);
582
+ let errors = schemaValidation.errors;
583
+ let errorSchema = schemaValidation.errorSchema;
584
+ const schemaValidationErrors = errors;
585
+ const schemaValidationErrorSchema = errorSchema;
586
+ const hasError = errors.length > 0 || (extraErrors && extraErrorsBlockSubmit);
587
+ if (hasError) {
588
+ if (extraErrors) {
589
+ const merged = validationDataMerge(schemaValidation, extraErrors);
590
+ errorSchema = merged.errorSchema;
591
+ errors = merged.errors;
592
+ }
593
+ if (focusOnFirstError) {
594
+ if (typeof focusOnFirstError === 'function') {
595
+ focusOnFirstError(errors[0]);
596
+ }
597
+ else {
598
+ this.focusOnError(errors[0]);
599
+ }
600
+ }
601
+ this.setState({
602
+ errors,
603
+ errorSchema,
604
+ schemaValidationErrors,
605
+ schemaValidationErrorSchema,
606
+ }, () => {
607
+ if (onError) {
608
+ onError(errors);
609
+ }
610
+ else {
611
+ console.error('Form validation failed', errors);
612
+ }
613
+ });
614
+ }
615
+ else if (prevErrors.length > 0) {
616
+ this.setState({
617
+ errors: [],
618
+ errorSchema: {},
619
+ schemaValidationErrors: [],
620
+ schemaValidationErrorSchema: {},
621
+ });
622
+ }
623
+ return !hasError;
624
+ };
568
625
  /** Programmatically validate the form. If `omitExtraData` is true, the `formData` will first be filtered to remove
569
626
  * any extra data not in a form field. If `onError` is provided, then it will be called with the list of errors the
570
627
  * same way as would happen on form submission.
@@ -583,14 +640,14 @@ export default class Form extends Component {
583
640
  * needed along with the submit button or any children of the form.
584
641
  */
585
642
  render() {
586
- const { children, id, idPrefix, idSeparator, className = '', tagName, name, method, target, action, autoComplete, enctype, acceptcharset, acceptCharset, noHtml5Validate = false, disabled, readonly, formContext, showErrorList = 'top', _internalFormWrapper, } = this.props;
643
+ const { children, id, idPrefix, idSeparator, className = '', tagName, name, method, target, action, autoComplete, enctype, acceptCharset, noHtml5Validate = false, disabled, readonly, formContext, showErrorList = 'top', _internalFormWrapper, } = this.props;
587
644
  const { schema, uiSchema, formData, errorSchema, idSchema } = this.state;
588
645
  const registry = this.getRegistry();
589
646
  const { SchemaField: _SchemaField } = registry.fields;
590
647
  const { SubmitButton } = registry.templates.ButtonTemplates;
591
- // The `semantic-ui` theme has an `_internalFormWrapper` that take an `as` prop that is the
648
+ // The `semantic-ui` and `material-ui` themes have `_internalFormWrapper`s that take an `as` prop that is the
592
649
  // PropTypes.elementType to use for the inner tag, so we'll need to pass `tagName` along if it is provided.
593
- // NOTE, the `as` prop is native to `semantic-ui`
650
+ // NOTE, the `as` prop is native to `semantic-ui` and is emulated in the `material-ui` theme
594
651
  const as = _internalFormWrapper ? tagName : undefined;
595
652
  const FormTag = _internalFormWrapper || tagName || 'form';
596
653
  let { [SUBMIT_BTN_OPTIONS_KEY]: submitOptions = {} } = getUiOptions(uiSchema);
@@ -598,7 +655,6 @@ export default class Form extends Component {
598
655
  submitOptions = { ...submitOptions, props: { ...submitOptions.props, disabled: true } };
599
656
  }
600
657
  const submitUiSchema = { [UI_OPTIONS_KEY]: { [SUBMIT_BTN_OPTIONS_KEY]: submitOptions } };
601
- return (_jsxs(FormTag, { className: className ? className : 'rjsf', id: id, name: name, method: method, target: target, action: action, autoComplete: autoComplete, encType: enctype, acceptCharset: acceptCharset || acceptcharset, noValidate: noHtml5Validate, onSubmit: this.onSubmit, as: as, ref: this.formElement, children: [showErrorList === 'top' && this.renderErrors(registry), _jsx(_SchemaField, { name: '', schema: schema, uiSchema: uiSchema, errorSchema: errorSchema, idSchema: idSchema, idPrefix: idPrefix, idSeparator: idSeparator, formContext: formContext, formData: formData, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, registry: registry, disabled: disabled, readonly: readonly }), children ? children : _jsx(SubmitButton, { uiSchema: submitUiSchema, registry: registry }), showErrorList === 'bottom' && this.renderErrors(registry)] }));
658
+ return (_jsxs(FormTag, { className: className ? className : 'rjsf', id: id, name: name, method: method, target: target, action: action, autoComplete: autoComplete, encType: enctype, acceptCharset: acceptCharset, noValidate: noHtml5Validate, onSubmit: this.onSubmit, as: as, ref: this.formElement, children: [showErrorList === 'top' && this.renderErrors(registry), _jsx(_SchemaField, { name: '', schema: schema, uiSchema: uiSchema, errorSchema: errorSchema, idSchema: idSchema, idPrefix: idPrefix, idSeparator: idSeparator, formContext: formContext, formData: formData, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, registry: registry, disabled: disabled, readonly: readonly }), children ? children : _jsx(SubmitButton, { uiSchema: submitUiSchema, registry: registry }), showErrorList === 'bottom' && this.renderErrors(registry)] }));
602
659
  }
603
660
  }
604
- //# sourceMappingURL=Form.js.map