@rjsf/core 6.4.1 → 6.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/core.umd.js +102 -38
  2. package/dist/index.cjs +102 -38
  3. package/dist/index.cjs.map +3 -3
  4. package/dist/index.esm.js +114 -44
  5. package/dist/index.esm.js.map +3 -3
  6. package/lib/components/Form.d.ts +6 -0
  7. package/lib/components/Form.d.ts.map +1 -1
  8. package/lib/components/Form.js +30 -14
  9. package/lib/components/fields/ArrayField.d.ts.map +1 -1
  10. package/lib/components/fields/ArrayField.js +5 -1
  11. package/lib/components/fields/ObjectField.d.ts.map +1 -1
  12. package/lib/components/fields/ObjectField.js +29 -5
  13. package/lib/components/templates/WrapIfAdditionalTemplate.d.ts.map +1 -1
  14. package/lib/components/templates/WrapIfAdditionalTemplate.js +1 -1
  15. package/lib/components/widgets/CheckboxesWidget.d.ts +1 -1
  16. package/lib/components/widgets/CheckboxesWidget.d.ts.map +1 -1
  17. package/lib/components/widgets/CheckboxesWidget.js +7 -5
  18. package/lib/components/widgets/RadioWidget.d.ts.map +1 -1
  19. package/lib/components/widgets/RadioWidget.js +5 -4
  20. package/lib/components/widgets/SelectWidget.d.ts.map +1 -1
  21. package/lib/components/widgets/SelectWidget.js +11 -10
  22. package/lib/tsconfig.tsbuildinfo +1 -1
  23. package/package.json +7 -7
  24. package/src/components/Form.tsx +59 -15
  25. package/src/components/fields/ArrayField.tsx +6 -2
  26. package/src/components/fields/ObjectField.tsx +30 -5
  27. package/src/components/templates/WrapIfAdditionalTemplate.tsx +1 -0
  28. package/src/components/widgets/CheckboxesWidget.tsx +12 -7
  29. package/src/components/widgets/RadioWidget.tsx +9 -6
  30. package/src/components/widgets/SelectWidget.tsx +14 -11
package/dist/core.umd.js CHANGED
@@ -82,7 +82,7 @@
82
82
  } = props;
83
83
  const { widgets: widgets2, schemaUtils, globalFormOptions, globalUiOptions } = registry;
84
84
  const itemsSchema = schemaUtils.retrieveSchema(schema.items, items);
85
- const itemsUiSchema = uiSchema?.items ?? {};
85
+ const itemsUiSchema = uiSchema?.items ?? uiSchema;
86
86
  const enumOptions = utils.optionsList(itemsSchema, itemsUiSchema);
87
87
  const { widget = "select", title: uiTitle, ...options } = utils.getUiOptions(uiSchema, globalUiOptions);
88
88
  const Widget = utils.getWidget(schema, widget, widgets2);
@@ -415,6 +415,7 @@
415
415
  name: name && `${name}-${index}`,
416
416
  registry,
417
417
  uiOptions,
418
+ parentUiSchema: uiSchema,
418
419
  hideError,
419
420
  readonly,
420
421
  disabled,
@@ -532,6 +533,7 @@
532
533
  name: name && `${name}-${index}`,
533
534
  registry,
534
535
  uiOptions,
536
+ parentUiSchema: uiSchema,
535
537
  hideError,
536
538
  readonly,
537
539
  disabled,
@@ -1857,10 +1859,13 @@
1857
1859
  } = props;
1858
1860
  const { fields: fields2, schemaUtils, translateString, globalUiOptions } = registry;
1859
1861
  const { OptionalDataControlsField: OptionalDataControlsField2 } = fields2;
1862
+ const formDataRef = react.useRef(formData);
1863
+ formDataRef.current = formData;
1860
1864
  const schema = schemaUtils.retrieveSchema(rawSchema, formData, true);
1861
1865
  const uiOptions = utils.getUiOptions(uiSchema, globalUiOptions);
1862
1866
  const { properties: schemaProperties = {} } = schema;
1863
1867
  const childFieldPathId = props.childFieldPathId ?? fieldPathId;
1868
+ const lastRenamedProperty = react.useRef({ previousKey: "", currentKey: void 0 });
1864
1869
  const templateTitle = uiOptions.title ?? schema.title ?? title ?? name;
1865
1870
  const description = uiOptions.description ?? schema.description;
1866
1871
  const renderOptionalField = utils.shouldRenderOptionalField(registry, schema, required, uiSchema);
@@ -1910,14 +1915,19 @@
1910
1915
  const newValue = constValue ?? defaultValue ?? getDefaultValue(translateString2, type);
1911
1916
  set(newFormData, newKey, newValue);
1912
1917
  }
1918
+ if (lastRenamedProperty.current.previousKey === newKey) {
1919
+ lastRenamedProperty.current.currentKey = newKey;
1920
+ lastRenamedProperty.current.previousKey = getAvailableKey(newKey, newFormData);
1921
+ }
1913
1922
  onChange(newFormData, childFieldPathId.path);
1914
1923
  }, [formData, onChange, registry, childFieldPathId, getAvailableKey, schema]);
1915
1924
  const handleKeyRename = react.useCallback(
1916
1925
  (oldKey, newKey) => {
1917
1926
  if (oldKey !== newKey) {
1918
- const actualNewKey = getAvailableKey(newKey, formData);
1927
+ const currentFormData = formDataRef.current;
1928
+ const actualNewKey = getAvailableKey(newKey, currentFormData);
1919
1929
  const newFormData = {
1920
- ...formData
1930
+ ...currentFormData
1921
1931
  };
1922
1932
  const newKeys = { [oldKey]: actualNewKey };
1923
1933
  const keyValues = Object.keys(newFormData).map((key) => {
@@ -1925,10 +1935,15 @@
1925
1935
  return { [newKey2]: newFormData[key] };
1926
1936
  });
1927
1937
  const renamedObj = Object.assign({}, ...keyValues);
1938
+ formDataRef.current = renamedObj;
1939
+ if (oldKey !== lastRenamedProperty.current.currentKey) {
1940
+ lastRenamedProperty.current.previousKey = oldKey;
1941
+ }
1942
+ lastRenamedProperty.current.currentKey = actualNewKey;
1928
1943
  onChange(renamedObj, childFieldPathId.path);
1929
1944
  }
1930
1945
  },
1931
- [formData, onChange, childFieldPathId, getAvailableKey]
1946
+ [onChange, childFieldPathId, getAvailableKey]
1932
1947
  );
1933
1948
  const handleRemoveProperty = react.useCallback(
1934
1949
  (key) => {
@@ -1936,6 +1951,12 @@
1936
1951
  },
1937
1952
  [onChange, childFieldPathId]
1938
1953
  );
1954
+ const getStableKey = react.useCallback((property) => {
1955
+ if (lastRenamedProperty.current.currentKey === property) {
1956
+ return lastRenamedProperty.current.previousKey;
1957
+ }
1958
+ return property;
1959
+ }, []);
1939
1960
  if (!renderOptionalField || hasFormData) {
1940
1961
  try {
1941
1962
  const properties = Object.keys(schemaProperties);
@@ -1978,7 +1999,7 @@
1978
1999
  readonly,
1979
2000
  hideError
1980
2001
  },
1981
- name2
2002
+ getStableKey(name2)
1982
2003
  );
1983
2004
  return {
1984
2005
  content,
@@ -3085,7 +3106,8 @@
3085
3106
  id: `${id}-key`,
3086
3107
  onBlur: onKeyRenameBlur,
3087
3108
  defaultValue: label
3088
- }
3109
+ },
3110
+ label
3089
3111
  )
3090
3112
  ] }) }),
3091
3113
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "form-additional form-group col-xs-5", children }),
@@ -3237,7 +3259,7 @@
3237
3259
  function CheckboxesWidget({
3238
3260
  id,
3239
3261
  disabled,
3240
- options: { inline = false, enumOptions, enumDisabled, emptyValue },
3262
+ options,
3241
3263
  value,
3242
3264
  autofocus = false,
3243
3265
  readonly,
@@ -3246,14 +3268,16 @@
3246
3268
  onFocus,
3247
3269
  htmlName
3248
3270
  }) {
3271
+ const { inline = false, enumOptions, enumDisabled, emptyValue } = options;
3272
+ const optionValueFormat = utils.getOptionValueFormat(options);
3249
3273
  const checkboxesValues = Array.isArray(value) ? value : [value];
3250
3274
  const handleBlur = react.useCallback(
3251
- ({ target }) => onBlur(id, utils.enumOptionsValueForIndex(target && target.value, enumOptions, emptyValue)),
3252
- [onBlur, id, enumOptions, emptyValue]
3275
+ ({ target }) => onBlur(id, utils.enumOptionValueDecoder(target && target.value, enumOptions, optionValueFormat, emptyValue)),
3276
+ [onBlur, id, enumOptions, emptyValue, optionValueFormat]
3253
3277
  );
3254
3278
  const handleFocus = react.useCallback(
3255
- ({ target }) => onFocus(id, utils.enumOptionsValueForIndex(target && target.value, enumOptions, emptyValue)),
3256
- [onFocus, id, enumOptions, emptyValue]
3279
+ ({ target }) => onFocus(id, utils.enumOptionValueDecoder(target && target.value, enumOptions, optionValueFormat, emptyValue)),
3280
+ [onFocus, id, enumOptions, emptyValue, optionValueFormat]
3257
3281
  );
3258
3282
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "checkboxes", id, children: Array.isArray(enumOptions) && enumOptions.map((option, index) => {
3259
3283
  const checked = utils.enumOptionsIsSelected(option.value, checkboxesValues);
@@ -3274,7 +3298,7 @@
3274
3298
  id: utils.optionId(id, index),
3275
3299
  name: htmlName || id,
3276
3300
  checked,
3277
- value: String(index),
3301
+ value: utils.enumOptionValueEncoder(option.value, index, optionValueFormat),
3278
3302
  disabled: disabled || itemDisabled || readonly,
3279
3303
  autoFocus: autofocus && index === 0,
3280
3304
  onChange: handleChange,
@@ -3419,13 +3443,14 @@
3419
3443
  htmlName
3420
3444
  }) {
3421
3445
  const { enumOptions, enumDisabled, inline, emptyValue } = options;
3446
+ const optionValueFormat = utils.getOptionValueFormat(options);
3422
3447
  const handleBlur = react.useCallback(
3423
- ({ target }) => onBlur(id, utils.enumOptionsValueForIndex(target && target.value, enumOptions, emptyValue)),
3424
- [onBlur, enumOptions, emptyValue, id]
3448
+ ({ target }) => onBlur(id, utils.enumOptionValueDecoder(target && target.value, enumOptions, optionValueFormat, emptyValue)),
3449
+ [onBlur, enumOptions, emptyValue, id, optionValueFormat]
3425
3450
  );
3426
3451
  const handleFocus = react.useCallback(
3427
- ({ target }) => onFocus(id, utils.enumOptionsValueForIndex(target && target.value, enumOptions, emptyValue)),
3428
- [onFocus, enumOptions, emptyValue, id]
3452
+ ({ target }) => onFocus(id, utils.enumOptionValueDecoder(target && target.value, enumOptions, optionValueFormat, emptyValue)),
3453
+ [onFocus, enumOptions, emptyValue, id, optionValueFormat]
3429
3454
  );
3430
3455
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "field-radio-group", id, role: "radiogroup", children: Array.isArray(enumOptions) && enumOptions.map((option, i) => {
3431
3456
  const checked = utils.enumOptionsIsSelected(option.value, value);
@@ -3441,7 +3466,7 @@
3441
3466
  checked,
3442
3467
  name: htmlName || id,
3443
3468
  required,
3444
- value: String(i),
3469
+ value: utils.enumOptionValueEncoder(option.value, i, optionValueFormat),
3445
3470
  disabled: disabled || itemDisabled || readonly,
3446
3471
  autoFocus: autofocus && i === 0,
3447
3472
  onChange: handleChange,
@@ -3592,28 +3617,29 @@
3592
3617
  }) {
3593
3618
  const { enumOptions, enumDisabled, emptyValue: optEmptyVal } = options;
3594
3619
  const emptyValue = multiple ? [] : "";
3620
+ const optionValueFormat = utils.getOptionValueFormat(options);
3595
3621
  const handleFocus = react.useCallback(
3596
3622
  (event) => {
3597
3623
  const newValue = getValue(event, multiple);
3598
- return onFocus(id, utils.enumOptionsValueForIndex(newValue, enumOptions, optEmptyVal));
3624
+ return onFocus(id, utils.enumOptionValueDecoder(newValue, enumOptions, optionValueFormat, optEmptyVal));
3599
3625
  },
3600
- [onFocus, id, multiple, enumOptions, optEmptyVal]
3626
+ [onFocus, id, multiple, enumOptions, optEmptyVal, optionValueFormat]
3601
3627
  );
3602
3628
  const handleBlur = react.useCallback(
3603
3629
  (event) => {
3604
3630
  const newValue = getValue(event, multiple);
3605
- return onBlur(id, utils.enumOptionsValueForIndex(newValue, enumOptions, optEmptyVal));
3631
+ return onBlur(id, utils.enumOptionValueDecoder(newValue, enumOptions, optionValueFormat, optEmptyVal));
3606
3632
  },
3607
- [onBlur, id, multiple, enumOptions, optEmptyVal]
3633
+ [onBlur, id, multiple, enumOptions, optEmptyVal, optionValueFormat]
3608
3634
  );
3609
3635
  const handleChange = react.useCallback(
3610
3636
  (event) => {
3611
3637
  const newValue = getValue(event, multiple);
3612
- return onChange(utils.enumOptionsValueForIndex(newValue, enumOptions, optEmptyVal));
3638
+ return onChange(utils.enumOptionValueDecoder(newValue, enumOptions, optionValueFormat, optEmptyVal));
3613
3639
  },
3614
- [onChange, multiple, enumOptions, optEmptyVal]
3640
+ [onChange, multiple, enumOptions, optEmptyVal, optionValueFormat]
3615
3641
  );
3616
- const selectedIndexes = utils.enumOptionsIndexForValue(value, enumOptions, multiple);
3642
+ const selectValue = utils.enumOptionSelectedValue(value, enumOptions, multiple, optionValueFormat, emptyValue);
3617
3643
  const showPlaceholderOption = !multiple && schema.default === void 0;
3618
3644
  return /* @__PURE__ */ jsxRuntime.jsxs(
3619
3645
  "select",
@@ -3623,7 +3649,7 @@
3623
3649
  multiple,
3624
3650
  role: "combobox",
3625
3651
  className: "form-control",
3626
- value: typeof selectedIndexes === "undefined" ? emptyValue : selectedIndexes,
3652
+ value: selectValue,
3627
3653
  required,
3628
3654
  disabled: disabled || readonly,
3629
3655
  autoFocus: autofocus,
@@ -3635,7 +3661,7 @@
3635
3661
  showPlaceholderOption && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: placeholder }),
3636
3662
  Array.isArray(enumOptions) && enumOptions.map(({ value: value2, label }, i) => {
3637
3663
  const disabled2 = enumDisabled && enumDisabled.indexOf(value2) !== -1;
3638
- return /* @__PURE__ */ jsxRuntime.jsx("option", { value: String(i), disabled: disabled2, children: label }, i);
3664
+ return /* @__PURE__ */ jsxRuntime.jsx("option", { value: utils.enumOptionValueEncoder(value2, i, optionValueFormat), disabled: disabled2, children: label }, i);
3639
3665
  })
3640
3666
  ]
3641
3667
  }
@@ -4003,12 +4029,11 @@
4003
4029
  }
4004
4030
  const newRegistry = this.getRegistry(props, rootSchema, schemaUtils);
4005
4031
  const registry = utils.deepEquals(state.registry, newRegistry) ? state.registry : newRegistry;
4006
- const expandedUiSchema = registry.uiSchemaDefinitions ? utils.expandUiSchemaDefinitions(rootSchema, uiSchema, registry) : uiSchema;
4007
4032
  const fieldPathId = state.fieldPathId && state.fieldPathId?.[utils.ID_KEY] === registry.globalFormOptions.idPrefix ? state.fieldPathId : utils.toFieldPathId("", registry.globalFormOptions);
4008
4033
  const nextState = {
4009
4034
  schemaUtils,
4010
4035
  schema: rootSchema,
4011
- uiSchema: expandedUiSchema,
4036
+ uiSchema,
4012
4037
  fieldPathId,
4013
4038
  formData,
4014
4039
  edit,
@@ -4082,7 +4107,7 @@
4082
4107
  errors = merged.errors;
4083
4108
  }
4084
4109
  if (customErrors) {
4085
- const merged = utils.validationDataMerge(schemaValidation, customErrors.ErrorSchema, true);
4110
+ const merged = utils.validationDataMerge({ errors, errorSchema }, customErrors.ErrorSchema, true);
4086
4111
  errorSchema = merged.errorSchema;
4087
4112
  errors = merged.errors;
4088
4113
  }
@@ -4190,7 +4215,7 @@
4190
4215
  this._isProcessingUserChange = true;
4191
4216
  const { newValue, path, id } = this.pendingChanges[0];
4192
4217
  const { newErrorSchema } = this.pendingChanges[0];
4193
- const { extraErrors, omitExtraData, liveOmit, noValidate, liveValidate, onChange } = this.props;
4218
+ const { extraErrors, omitExtraData, liveOmit, noValidate, liveValidate, onChange, removeEmptyOptionalObjects } = this.props;
4194
4219
  const { formData: oldFormData, schemaUtils, schema, fieldPathId, schemaValidationErrorSchema, errors } = this.state;
4195
4220
  let { customErrors, errorSchema: originalErrorSchema } = this.state;
4196
4221
  const rootPathId = fieldPathId.path[0] || "";
@@ -4219,6 +4244,18 @@
4219
4244
  formData: newFormData
4220
4245
  };
4221
4246
  }
4247
+ if (removeEmptyOptionalObjects) {
4248
+ newFormData = utils.removeOptionalEmptyObjects(
4249
+ schemaUtils.getValidator(),
4250
+ schema,
4251
+ schemaUtils.getRootSchema(),
4252
+ newFormData
4253
+ );
4254
+ state = {
4255
+ ...state,
4256
+ formData: newFormData
4257
+ };
4258
+ }
4222
4259
  if (newErrorSchema) {
4223
4260
  const oldValidationError = !isRootPath ? get(schemaValidationErrorSchema, path) : schemaValidationErrorSchema;
4224
4261
  if (!isEmpty(oldValidationError)) {
@@ -4319,24 +4356,33 @@
4319
4356
  * @param data - The data associated with the field that was blurred
4320
4357
  */
4321
4358
  onBlur = (id, data) => {
4322
- const { onBlur, omitExtraData, liveOmit, liveValidate } = this.props;
4359
+ const { onBlur, omitExtraData, liveOmit, liveValidate, removeEmptyOptionalObjects } = this.props;
4323
4360
  if (onBlur) {
4324
4361
  onBlur(id, data);
4325
4362
  }
4326
4363
  if (omitExtraData === true && liveOmit === "onBlur" || liveValidate === "onBlur") {
4327
4364
  const { onChange, extraErrors } = this.props;
4328
- const { formData } = this.state;
4365
+ const { formData, schemaUtils, schema } = this.state;
4329
4366
  let newFormData = formData;
4330
4367
  let state = { formData: newFormData };
4331
4368
  if (omitExtraData === true && liveOmit === "onBlur") {
4332
4369
  newFormData = this.omitExtraData(formData);
4333
4370
  state = { formData: newFormData };
4334
4371
  }
4372
+ if (removeEmptyOptionalObjects) {
4373
+ newFormData = utils.removeOptionalEmptyObjects(
4374
+ schemaUtils.getValidator(),
4375
+ schema,
4376
+ schemaUtils.getRootSchema(),
4377
+ newFormData
4378
+ );
4379
+ state = { ...state, formData: newFormData };
4380
+ }
4335
4381
  if (liveValidate === "onBlur") {
4336
- const { schema, schemaUtils, errorSchema, customErrors, retrievedSchema } = this.state;
4382
+ const { schema: schema2, schemaUtils: schemaUtils2, errorSchema, customErrors, retrievedSchema } = this.state;
4337
4383
  const liveValidation = this.liveValidate(
4338
- schema,
4339
- schemaUtils,
4384
+ schema2,
4385
+ schemaUtils2,
4340
4386
  errorSchema,
4341
4387
  newFormData,
4342
4388
  extraErrors,
@@ -4383,11 +4429,20 @@
4383
4429
  return;
4384
4430
  }
4385
4431
  event.persist();
4386
- const { omitExtraData, extraErrors, noValidate, onSubmit } = this.props;
4432
+ const { omitExtraData, extraErrors, noValidate, onSubmit, removeEmptyOptionalObjects } = this.props;
4387
4433
  let { formData: newFormData } = this.state;
4388
4434
  if (omitExtraData === true) {
4389
4435
  newFormData = this.omitExtraData(newFormData);
4390
4436
  }
4437
+ if (removeEmptyOptionalObjects) {
4438
+ const { schemaUtils, schema } = this.state;
4439
+ newFormData = utils.removeOptionalEmptyObjects(
4440
+ schemaUtils.getValidator(),
4441
+ schema,
4442
+ schemaUtils.getRootSchema(),
4443
+ newFormData
4444
+ );
4445
+ }
4391
4446
  if (noValidate || this.validateFormWithFormData(newFormData)) {
4392
4447
  const errorSchema = extraErrors || {};
4393
4448
  const errors = extraErrors ? utils.toErrorList(extraErrors) : [];
@@ -4484,7 +4539,7 @@
4484
4539
  const elementId = path.join(idSeparator);
4485
4540
  let field = this.formElement.current.elements[elementId];
4486
4541
  if (!field) {
4487
- field = this.formElement.current.querySelector(`input[id^="${elementId}"`);
4542
+ field = this.formElement.current.querySelector(`input[id^="${elementId}"], button[id^="${elementId}"]`);
4488
4543
  }
4489
4544
  if (field && field.length) {
4490
4545
  field = field[0];
@@ -4552,11 +4607,20 @@
4552
4607
  * @returns - True if the form is valid, false otherwise.
4553
4608
  */
4554
4609
  validateForm() {
4555
- const { omitExtraData } = this.props;
4610
+ const { omitExtraData, removeEmptyOptionalObjects } = this.props;
4556
4611
  let { formData: newFormData } = this.state;
4557
4612
  if (omitExtraData === true) {
4558
4613
  newFormData = this.omitExtraData(newFormData);
4559
4614
  }
4615
+ if (removeEmptyOptionalObjects) {
4616
+ const { schemaUtils, schema } = this.state;
4617
+ newFormData = utils.removeOptionalEmptyObjects(
4618
+ schemaUtils.getValidator(),
4619
+ schema,
4620
+ schemaUtils.getRootSchema(),
4621
+ newFormData
4622
+ );
4623
+ }
4560
4624
  return this.validateFormWithFormData(newFormData);
4561
4625
  }
4562
4626
  /** Renders the `Form` fields inside the <form> | `tagName` or `_internalFormWrapper`, rendering any errors if