@rjsf/core 6.0.0-beta.2 → 6.0.0-beta.21

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 (114) hide show
  1. package/dist/core.umd.js +705 -471
  2. package/dist/{index.js → index.cjs} +1094 -844
  3. package/dist/index.cjs.map +7 -0
  4. package/dist/index.esm.js +1053 -774
  5. package/dist/index.esm.js.map +4 -4
  6. package/lib/components/Form.d.ts +88 -23
  7. package/lib/components/Form.d.ts.map +1 -1
  8. package/lib/components/Form.js +213 -151
  9. package/lib/components/fields/ArrayField.d.ts +17 -7
  10. package/lib/components/fields/ArrayField.d.ts.map +1 -1
  11. package/lib/components/fields/ArrayField.js +116 -70
  12. package/lib/components/fields/BooleanField.d.ts.map +1 -1
  13. package/lib/components/fields/BooleanField.js +7 -2
  14. package/lib/components/fields/LayoutGridField.d.ts +27 -25
  15. package/lib/components/fields/LayoutGridField.d.ts.map +1 -1
  16. package/lib/components/fields/LayoutGridField.js +83 -59
  17. package/lib/components/fields/LayoutHeaderField.d.ts +1 -1
  18. package/lib/components/fields/LayoutHeaderField.js +3 -3
  19. package/lib/components/fields/LayoutMultiSchemaField.js +6 -5
  20. package/lib/components/fields/MultiSchemaField.d.ts.map +1 -1
  21. package/lib/components/fields/MultiSchemaField.js +13 -9
  22. package/lib/components/fields/NullField.js +3 -3
  23. package/lib/components/fields/NumberField.d.ts.map +1 -1
  24. package/lib/components/fields/NumberField.js +3 -3
  25. package/lib/components/fields/ObjectField.d.ts +3 -3
  26. package/lib/components/fields/ObjectField.d.ts.map +1 -1
  27. package/lib/components/fields/ObjectField.js +34 -34
  28. package/lib/components/fields/OptionalDataControlsField.d.ts +8 -0
  29. package/lib/components/fields/OptionalDataControlsField.d.ts.map +1 -0
  30. package/lib/components/fields/OptionalDataControlsField.js +43 -0
  31. package/lib/components/fields/SchemaField.d.ts.map +1 -1
  32. package/lib/components/fields/SchemaField.js +17 -17
  33. package/lib/components/fields/StringField.d.ts.map +1 -1
  34. package/lib/components/fields/StringField.js +7 -2
  35. package/lib/components/fields/index.d.ts.map +1 -1
  36. package/lib/components/fields/index.js +2 -0
  37. package/lib/components/templates/ArrayFieldDescriptionTemplate.d.ts +1 -1
  38. package/lib/components/templates/ArrayFieldDescriptionTemplate.js +3 -3
  39. package/lib/components/templates/ArrayFieldItemButtonsTemplate.js +2 -2
  40. package/lib/components/templates/ArrayFieldTemplate.d.ts.map +1 -1
  41. package/lib/components/templates/ArrayFieldTemplate.js +4 -3
  42. package/lib/components/templates/ArrayFieldTitleTemplate.d.ts +1 -1
  43. package/lib/components/templates/ArrayFieldTitleTemplate.d.ts.map +1 -1
  44. package/lib/components/templates/ArrayFieldTitleTemplate.js +3 -3
  45. package/lib/components/templates/ButtonTemplates/AddButton.d.ts +1 -1
  46. package/lib/components/templates/ButtonTemplates/AddButton.d.ts.map +1 -1
  47. package/lib/components/templates/ButtonTemplates/AddButton.js +2 -2
  48. package/lib/components/templates/FieldErrorTemplate.js +2 -2
  49. package/lib/components/templates/FieldHelpTemplate.js +2 -2
  50. package/lib/components/templates/MultiSchemaFieldTemplate.d.ts +8 -0
  51. package/lib/components/templates/MultiSchemaFieldTemplate.d.ts.map +1 -0
  52. package/lib/components/templates/MultiSchemaFieldTemplate.js +10 -0
  53. package/lib/components/templates/ObjectFieldTemplate.d.ts.map +1 -1
  54. package/lib/components/templates/ObjectFieldTemplate.js +3 -2
  55. package/lib/components/templates/OptionalDataControlsTemplate.d.ts +11 -0
  56. package/lib/components/templates/OptionalDataControlsTemplate.d.ts.map +1 -0
  57. package/lib/components/templates/OptionalDataControlsTemplate.js +20 -0
  58. package/lib/components/templates/TitleField.d.ts.map +1 -1
  59. package/lib/components/templates/TitleField.js +2 -2
  60. package/lib/components/templates/UnsupportedField.js +3 -3
  61. package/lib/components/templates/index.d.ts.map +1 -1
  62. package/lib/components/templates/index.js +4 -0
  63. package/lib/components/widgets/AltDateWidget.d.ts.map +1 -1
  64. package/lib/components/widgets/AltDateWidget.js +15 -18
  65. package/lib/components/widgets/CheckboxesWidget.js +2 -2
  66. package/lib/getDefaultRegistry.d.ts.map +1 -1
  67. package/lib/getDefaultRegistry.js +2 -1
  68. package/lib/getTestRegistry.d.ts +5 -0
  69. package/lib/getTestRegistry.d.ts.map +1 -0
  70. package/lib/getTestRegistry.js +19 -0
  71. package/lib/index.d.ts +2 -1
  72. package/lib/index.d.ts.map +1 -1
  73. package/lib/index.js +2 -1
  74. package/lib/tsconfig.tsbuildinfo +1 -1
  75. package/package.json +18 -19
  76. package/src/components/Form.tsx +306 -177
  77. package/src/components/fields/ArrayField.tsx +127 -80
  78. package/src/components/fields/BooleanField.tsx +12 -3
  79. package/src/components/fields/LayoutGridField.tsx +95 -88
  80. package/src/components/fields/LayoutHeaderField.tsx +3 -3
  81. package/src/components/fields/LayoutMultiSchemaField.tsx +5 -5
  82. package/src/components/fields/MultiSchemaField.tsx +51 -35
  83. package/src/components/fields/NullField.tsx +3 -3
  84. package/src/components/fields/NumberField.tsx +11 -3
  85. package/src/components/fields/ObjectField.tsx +47 -53
  86. package/src/components/fields/OptionalDataControlsField.tsx +84 -0
  87. package/src/components/fields/SchemaField.tsx +24 -30
  88. package/src/components/fields/StringField.tsx +12 -3
  89. package/src/components/fields/index.ts +2 -0
  90. package/src/components/templates/ArrayFieldDescriptionTemplate.tsx +3 -3
  91. package/src/components/templates/ArrayFieldItemButtonsTemplate.tsx +5 -5
  92. package/src/components/templates/ArrayFieldTemplate.tsx +9 -5
  93. package/src/components/templates/ArrayFieldTitleTemplate.tsx +4 -3
  94. package/src/components/templates/BaseInputTemplate.tsx +3 -3
  95. package/src/components/templates/ButtonTemplates/AddButton.tsx +2 -0
  96. package/src/components/templates/FieldErrorTemplate.tsx +2 -2
  97. package/src/components/templates/FieldHelpTemplate.tsx +2 -2
  98. package/src/components/templates/MultiSchemaFieldTemplate.tsx +20 -0
  99. package/src/components/templates/ObjectFieldTemplate.tsx +10 -5
  100. package/src/components/templates/OptionalDataControlsTemplate.tsx +43 -0
  101. package/src/components/templates/TitleField.tsx +6 -1
  102. package/src/components/templates/UnsupportedField.tsx +3 -3
  103. package/src/components/templates/WrapIfAdditionalTemplate.tsx +1 -1
  104. package/src/components/templates/index.ts +4 -0
  105. package/src/components/widgets/AltDateWidget.tsx +21 -23
  106. package/src/components/widgets/CheckboxWidget.tsx +2 -2
  107. package/src/components/widgets/CheckboxesWidget.tsx +3 -3
  108. package/src/components/widgets/RadioWidget.tsx +1 -1
  109. package/src/components/widgets/SelectWidget.tsx +1 -1
  110. package/src/components/widgets/TextareaWidget.tsx +1 -1
  111. package/src/getDefaultRegistry.ts +10 -1
  112. package/src/getTestRegistry.tsx +34 -0
  113. package/src/index.ts +2 -1
  114. package/dist/index.js.map +0 -7
@@ -6,12 +6,16 @@ import {
6
6
  isFixedItems,
7
7
  allowAdditionalItems,
8
8
  isCustomWidget,
9
+ isFormDataAvailable,
9
10
  optionsList,
11
+ shouldRenderOptionalField,
12
+ toFieldPathId,
10
13
  ArrayFieldTemplateProps,
11
14
  ErrorSchema,
15
+ FieldPathId,
16
+ FieldPathList,
12
17
  FieldProps,
13
18
  FormContextType,
14
- IdSchema,
15
19
  RJSFSchema,
16
20
  StrictRJSFSchema,
17
21
  TranslatableString,
@@ -22,7 +26,7 @@ import cloneDeep from 'lodash/cloneDeep';
22
26
  import get from 'lodash/get';
23
27
  import isObject from 'lodash/isObject';
24
28
  import set from 'lodash/set';
25
- import { nanoid } from 'nanoid';
29
+ import uniqueId from 'lodash/uniqueId';
26
30
 
27
31
  /** Type used to represent the keyed form data used in the state */
28
32
  type KeyedFormDataType<T> = { key: string; item: T };
@@ -37,7 +41,7 @@ type ArrayFieldState<T> = {
37
41
 
38
42
  /** Used to generate a unique ID for an element in a row */
39
43
  function generateRowId() {
40
- return nanoid();
44
+ return uniqueId('rjsf-array-item-');
41
45
  }
42
46
 
43
47
  /** Converts the `formData` into `KeyedFormDataType` data, using the `generateRowId()` function to create the key
@@ -45,7 +49,7 @@ function generateRowId() {
45
49
  * @param formData - The data for the form
46
50
  * @returns - The `formData` converted into a `KeyedFormDataType` element
47
51
  */
48
- function generateKeyedFormData<T>(formData: T[]): KeyedFormDataType<T>[] {
52
+ function generateKeyedFormData<T>(formData?: T[]): KeyedFormDataType<T>[] {
49
53
  return !Array.isArray(formData)
50
54
  ? []
51
55
  : formData.map((item) => {
@@ -81,7 +85,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
81
85
  */
82
86
  constructor(props: FieldProps<T[], S, F>) {
83
87
  super(props);
84
- const { formData = [] } = props;
88
+ const { formData } = props;
85
89
  const keyedFormData = generateKeyedFormData<T>(formData);
86
90
  this.state = {
87
91
  keyedFormData,
@@ -198,7 +202,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
198
202
  event.preventDefault();
199
203
  }
200
204
 
201
- const { onChange, errorSchema } = this.props;
205
+ const { onChange, errorSchema, fieldPathId } = this.props;
202
206
  const { keyedFormData } = this.state;
203
207
  // refs #195: revalidate to ensure properly reindexing errors
204
208
  let newErrorSchema: ErrorSchema<T>;
@@ -229,7 +233,8 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
229
233
  keyedFormData: newKeyedFormData,
230
234
  updatedKeyedFormData: true,
231
235
  },
232
- () => onChange(keyedToPlainFormData(newKeyedFormData), newErrorSchema as ErrorSchema<T[]>),
236
+ // add click will pass the empty `path` array to the onChange which adds the appropriate path
237
+ () => onChange(keyedToPlainFormData(newKeyedFormData), fieldPathId.path, newErrorSchema as ErrorSchema<T[]>),
233
238
  );
234
239
  }
235
240
 
@@ -267,7 +272,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
267
272
  event.preventDefault();
268
273
  }
269
274
 
270
- const { onChange, errorSchema } = this.props;
275
+ const { onChange, errorSchema, fieldPathId } = this.props;
271
276
  const { keyedFormData } = this.state;
272
277
  // refs #195: revalidate to ensure properly reindexing errors
273
278
  let newErrorSchema: ErrorSchema<T>;
@@ -298,7 +303,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
298
303
  keyedFormData: newKeyedFormData,
299
304
  updatedKeyedFormData: true,
300
305
  },
301
- () => onChange(keyedToPlainFormData(newKeyedFormData), newErrorSchema as ErrorSchema<T[]>),
306
+ () => onChange(keyedToPlainFormData(newKeyedFormData), fieldPathId.path, newErrorSchema as ErrorSchema<T[]>),
302
307
  );
303
308
  };
304
309
  };
@@ -314,7 +319,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
314
319
  if (event) {
315
320
  event.preventDefault();
316
321
  }
317
- const { onChange, errorSchema } = this.props;
322
+ const { onChange, errorSchema, fieldPathId } = this.props;
318
323
  const { keyedFormData } = this.state;
319
324
  // refs #195: revalidate to ensure properly reindexing errors
320
325
  let newErrorSchema: ErrorSchema<T>;
@@ -335,7 +340,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
335
340
  keyedFormData: newKeyedFormData,
336
341
  updatedKeyedFormData: true,
337
342
  },
338
- () => onChange(keyedToPlainFormData(newKeyedFormData), newErrorSchema as ErrorSchema<T[]>),
343
+ () => onChange(keyedToPlainFormData(newKeyedFormData), fieldPathId.path, newErrorSchema as ErrorSchema<T[]>),
339
344
  );
340
345
  };
341
346
  };
@@ -353,7 +358,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
353
358
  event.preventDefault();
354
359
  event.currentTarget.blur();
355
360
  }
356
- const { onChange, errorSchema } = this.props;
361
+ const { onChange, errorSchema, fieldPathId } = this.props;
357
362
  let newErrorSchema: ErrorSchema<T>;
358
363
  if (errorSchema) {
359
364
  newErrorSchema = {};
@@ -385,7 +390,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
385
390
  {
386
391
  keyedFormData: newKeyedFormData,
387
392
  },
388
- () => onChange(keyedToPlainFormData(newKeyedFormData), newErrorSchema as ErrorSchema<T[]>),
393
+ () => onChange(keyedToPlainFormData(newKeyedFormData), fieldPathId.path, newErrorSchema as ErrorSchema<T[]>),
389
394
  );
390
395
  };
391
396
  };
@@ -396,22 +401,14 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
396
401
  * @param index - The index of the item being changed
397
402
  */
398
403
  onChangeForIndex = (index: number) => {
399
- return (value: any, newErrorSchema?: ErrorSchema<T>, id?: string) => {
400
- const { formData, onChange, errorSchema } = this.props;
401
- const arrayData = Array.isArray(formData) ? formData : [];
402
- const newFormData = arrayData.map((item: T, i: number) => {
404
+ return (value: any, path: FieldPathList, newErrorSchema?: ErrorSchema<T>, id?: string) => {
405
+ const { onChange } = this.props;
406
+ onChange(
403
407
  // We need to treat undefined items as nulls to have validation.
404
408
  // See https://github.com/tdegrunt/jsonschema/issues/206
405
- const jsonValue = typeof value === 'undefined' ? null : value;
406
- return index === i ? jsonValue : item;
407
- });
408
- onChange(
409
- newFormData,
410
- errorSchema &&
411
- errorSchema && {
412
- ...errorSchema,
413
- [index]: newErrorSchema,
414
- },
409
+ value === undefined ? null : value,
410
+ path,
411
+ newErrorSchema as ErrorSchema<T[]>,
415
412
  id,
416
413
  );
417
414
  };
@@ -419,14 +416,48 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
419
416
 
420
417
  /** Callback handler used to change the value for a checkbox */
421
418
  onSelectChange = (value: any) => {
422
- const { onChange, idSchema } = this.props;
423
- onChange(value, undefined, idSchema && idSchema.$id);
419
+ const { onChange, fieldPathId } = this.props;
420
+ // select change will pass an empty `path` array since the `ObjectField` will add the path value automatically
421
+ onChange(value, fieldPathId.path, undefined, fieldPathId && fieldPathId.$id);
424
422
  };
425
423
 
424
+ /** Helper method to compute item UI schema for both normal and fixed arrays
425
+ * Handles both static object and dynamic function cases
426
+ *
427
+ * @param uiSchema - The parent UI schema containing items definition
428
+ * @param item - The item data
429
+ * @param index - The index of the item
430
+ * @param formContext - The form context
431
+ * @returns The computed UI schema for the item
432
+ */
433
+ private computeItemUiSchema(
434
+ uiSchema: UiSchema<T[], S, F>,
435
+ item: T,
436
+ index: number,
437
+ formContext: F,
438
+ ): UiSchema<T[], S, F> | undefined {
439
+ if (typeof uiSchema.items === 'function') {
440
+ try {
441
+ // Call the function with item data, index, and form context
442
+ // TypeScript now correctly infers the types thanks to the ArrayElement type in UiSchema
443
+ const result = uiSchema.items(item, index, formContext);
444
+ // Only use the result if it's truthy
445
+ return result as UiSchema<T[], S, F>;
446
+ } catch (e) {
447
+ console.error(`Error executing dynamic uiSchema.items function for item at index ${index}:`, e);
448
+ // Fall back to undefined to allow the field to still render
449
+ return undefined;
450
+ }
451
+ } else {
452
+ // Static object case - preserve undefined to maintain backward compatibility
453
+ return uiSchema.items as UiSchema<T[], S, F> | undefined;
454
+ }
455
+ }
456
+
426
457
  /** Renders the `ArrayField` depending on the specific needs of the schema and uischema elements
427
458
  */
428
459
  render() {
429
- const { schema, uiSchema, idSchema, registry } = this.props;
460
+ const { schema, uiSchema, fieldPathId, registry } = this.props;
430
461
  const { schemaUtils, translateString } = registry;
431
462
  if (!(ITEMS_KEY in schema)) {
432
463
  const uiOptions = getUiOptions<T[], S, F>(uiSchema);
@@ -439,7 +470,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
439
470
  return (
440
471
  <UnsupportedFieldTemplate
441
472
  schema={schema}
442
- idSchema={idSchema}
473
+ fieldPathId={fieldPathId}
443
474
  reason={translateString(TranslatableString.MissingItems)}
444
475
  registry={registry}
445
476
  />
@@ -468,7 +499,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
468
499
  schema,
469
500
  uiSchema = {},
470
501
  errorSchema,
471
- idSchema,
502
+ fieldPathId,
472
503
  name,
473
504
  title,
474
505
  disabled = false,
@@ -478,28 +509,35 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
478
509
  registry,
479
510
  onBlur,
480
511
  onFocus,
481
- idPrefix,
482
- idSeparator = '_',
483
512
  rawErrors,
484
513
  } = this.props;
485
514
  const { keyedFormData } = this.state;
486
515
  const fieldTitle = schema.title || title || name;
487
- const { schemaUtils, formContext } = registry;
516
+ const { schemaUtils, fields, formContext, globalFormOptions } = registry;
517
+ const { OptionalDataControlsField } = fields;
488
518
  const uiOptions = getUiOptions<T[], S, F>(uiSchema);
489
519
  const _schemaItems: S = isObject(schema.items) ? (schema.items as S) : ({} as S);
490
520
  const itemsSchema: S = schemaUtils.retrieveSchema(_schemaItems);
491
521
  const formData = keyedToPlainFormData(this.state.keyedFormData);
492
- const canAdd = this.canAddItem(formData);
522
+ const renderOptionalField = shouldRenderOptionalField(registry, schema, required, uiSchema);
523
+ const hasFormData = isFormDataAvailable(this.props.formData);
524
+ const canAdd = this.canAddItem(formData) && (!renderOptionalField || hasFormData);
525
+ const actualFormData = hasFormData ? keyedFormData : [];
526
+ const extraClass = renderOptionalField ? ' rjsf-optional-array-field' : '';
527
+ const optionalDataControl = renderOptionalField ? <OptionalDataControlsField {...this.props} /> : undefined;
493
528
  const arrayProps: ArrayFieldTemplateProps<T[], S, F> = {
494
529
  canAdd,
495
- items: keyedFormData.map((keyedItem, index) => {
530
+ items: actualFormData.map((keyedItem, index) => {
496
531
  const { key, item } = keyedItem;
497
532
  // While we are actually dealing with a single item of type T, the types require a T[], so cast
498
533
  const itemCast = item as unknown as T[];
499
534
  const itemSchema = schemaUtils.retrieveSchema(_schemaItems, itemCast);
500
535
  const itemErrorSchema = errorSchema ? (errorSchema[index] as ErrorSchema<T[]>) : undefined;
501
- const itemIdPrefix = idSchema.$id + idSeparator + index;
502
- const itemIdSchema = schemaUtils.toIdSchema(itemSchema, itemIdPrefix, itemCast, idPrefix, idSeparator);
536
+ const itemFieldPathId = toFieldPathId(index, globalFormOptions, fieldPathId);
537
+
538
+ // Compute the item UI schema using the helper method
539
+ const itemUiSchema = this.computeItemUiSchema(uiSchema, item, index, formContext);
540
+
503
541
  return this.renderArrayFieldItem({
504
542
  key,
505
543
  index,
@@ -509,10 +547,10 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
509
547
  canMoveUp: index > 0,
510
548
  canMoveDown: index < formData.length - 1,
511
549
  itemSchema,
512
- itemIdSchema,
550
+ itemFieldPathId,
513
551
  itemErrorSchema,
514
552
  itemData: itemCast,
515
- itemUiSchema: uiSchema.items,
553
+ itemUiSchema,
516
554
  autofocus: autofocus && index === 0,
517
555
  onBlur,
518
556
  onFocus,
@@ -520,19 +558,19 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
520
558
  totalItems: keyedFormData.length,
521
559
  });
522
560
  }),
523
- className: `rjsf-field rjsf-field-array rjsf-field-array-of-${itemsSchema.type}`,
561
+ className: `rjsf-field rjsf-field-array rjsf-field-array-of-${itemsSchema.type}${extraClass}`,
524
562
  disabled,
525
- idSchema,
563
+ fieldPathId,
526
564
  uiSchema,
527
565
  onAddClick: this.onAddClick,
528
566
  readonly,
529
567
  required,
530
568
  schema,
531
569
  title: fieldTitle,
532
- formContext,
533
570
  formData,
534
571
  rawErrors,
535
572
  registry,
573
+ optionalDataControl,
536
574
  };
537
575
 
538
576
  const Template = getTemplate<'ArrayFieldTemplate', T[], S, F>('ArrayFieldTemplate', registry, uiOptions);
@@ -544,7 +582,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
544
582
  renderCustomWidget() {
545
583
  const {
546
584
  schema,
547
- idSchema,
585
+ fieldPathId,
548
586
  uiSchema,
549
587
  disabled = false,
550
588
  readonly = false,
@@ -566,7 +604,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
566
604
  const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema, globalUiOptions);
567
605
  return (
568
606
  <Widget
569
- id={idSchema.$id}
607
+ id={fieldPathId.$id}
570
608
  name={name}
571
609
  multiple
572
610
  onChange={this.onSelectChange}
@@ -596,7 +634,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
596
634
  renderMultiSelect() {
597
635
  const {
598
636
  schema,
599
- idSchema,
637
+ fieldPathId,
600
638
  uiSchema,
601
639
  formData: items = [],
602
640
  disabled = false,
@@ -619,7 +657,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
619
657
  const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema, globalUiOptions);
620
658
  return (
621
659
  <Widget
622
- id={idSchema.$id}
660
+ id={fieldPathId.$id}
623
661
  name={name}
624
662
  multiple
625
663
  onChange={this.onSelectChange}
@@ -649,7 +687,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
649
687
  const {
650
688
  schema,
651
689
  uiSchema,
652
- idSchema,
690
+ fieldPathId,
653
691
  name,
654
692
  disabled = false,
655
693
  readonly = false,
@@ -669,7 +707,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
669
707
  return (
670
708
  <Widget
671
709
  options={options}
672
- id={idSchema.$id}
710
+ id={fieldPathId.$id}
673
711
  name={name}
674
712
  multiple
675
713
  onChange={this.onSelectChange}
@@ -697,11 +735,9 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
697
735
  const {
698
736
  schema,
699
737
  uiSchema = {},
700
- formData = [],
738
+ formData,
701
739
  errorSchema,
702
- idPrefix,
703
- idSeparator = '_',
704
- idSchema,
740
+ fieldPathId,
705
741
  name,
706
742
  title,
707
743
  disabled = false,
@@ -713,34 +749,39 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
713
749
  onFocus,
714
750
  rawErrors,
715
751
  } = this.props;
716
- const { keyedFormData } = this.state;
717
752
  let { formData: items = [] } = this.props;
753
+ const { keyedFormData } = this.state;
718
754
  const fieldTitle = schema.title || title || name;
719
755
  const uiOptions = getUiOptions<T[], S, F>(uiSchema);
720
- const { schemaUtils, formContext } = registry;
756
+ const { schemaUtils, fields, formContext, globalFormOptions } = registry;
757
+ const { OptionalDataControlsField } = fields;
758
+ const renderOptionalField = shouldRenderOptionalField(registry, schema, required, uiSchema);
759
+ const hasFormData = isFormDataAvailable(formData);
721
760
  const _schemaItems: S[] = isObject(schema.items) ? (schema.items as S[]) : ([] as S[]);
722
761
  const itemSchemas = _schemaItems.map((item: S, index: number) =>
723
- schemaUtils.retrieveSchema(item, formData[index] as unknown as T[]),
762
+ schemaUtils.retrieveSchema(item, items[index] as unknown as T[]),
724
763
  );
725
764
  const additionalSchema = isObject(schema.additionalItems)
726
765
  ? schemaUtils.retrieveSchema(schema.additionalItems as S, formData)
727
766
  : null;
728
767
 
729
- if (!items || items.length < itemSchemas.length) {
768
+ if (items.length < itemSchemas.length) {
730
769
  // to make sure at least all fixed items are generated
731
- items = items || [];
732
770
  items = items.concat(new Array(itemSchemas.length - items.length));
733
771
  }
772
+ const actualFormData = hasFormData ? keyedFormData : [];
773
+ const extraClass = renderOptionalField ? ' rjsf-optional-array-field' : '';
774
+ const optionalDataControl = renderOptionalField ? <OptionalDataControlsField {...this.props} /> : undefined;
734
775
 
735
776
  // These are the props passed into the render function
736
- const canAdd = this.canAddItem(items) && !!additionalSchema;
777
+ const canAdd = this.canAddItem(items) && !!additionalSchema && (!renderOptionalField || hasFormData);
737
778
  const arrayProps: ArrayFieldTemplateProps<T[], S, F> = {
738
779
  canAdd,
739
- className: 'rjsf-field rjsf-field-array rjsf-field-array-fixed-items',
780
+ className: `rjsf-field rjsf-field-array rjsf-field-array-fixed-items${extraClass}`,
740
781
  disabled,
741
- idSchema,
782
+ fieldPathId,
742
783
  formData,
743
- items: keyedFormData.map((keyedItem, index) => {
784
+ items: actualFormData.map((keyedItem, index) => {
744
785
  const { key, item } = keyedItem;
745
786
  // While we are actually dealing with a single item of type T, the types require a T[], so cast
746
787
  const itemCast = item as unknown as T[];
@@ -749,13 +790,21 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
749
790
  (additional && isObject(schema.additionalItems)
750
791
  ? schemaUtils.retrieveSchema(schema.additionalItems as S, itemCast)
751
792
  : itemSchemas[index]) || {};
752
- const itemIdPrefix = idSchema.$id + idSeparator + index;
753
- const itemIdSchema = schemaUtils.toIdSchema(itemSchema, itemIdPrefix, itemCast, idPrefix, idSeparator);
754
- const itemUiSchema = additional
755
- ? uiSchema.additionalItems || {}
756
- : Array.isArray(uiSchema.items)
757
- ? uiSchema.items[index]
758
- : uiSchema.items || {};
793
+ const itemFieldPathId = toFieldPathId(index, globalFormOptions, fieldPathId);
794
+ // Compute the item UI schema - handle both static and dynamic cases
795
+ let itemUiSchema: UiSchema<T[], S, F> | undefined;
796
+ if (additional) {
797
+ // For additional items, use additionalItems uiSchema
798
+ itemUiSchema = uiSchema.additionalItems as UiSchema<T[], S, F>;
799
+ } else {
800
+ // For fixed items, uiSchema.items can be an array, a function, or a single object
801
+ if (Array.isArray(uiSchema.items)) {
802
+ itemUiSchema = uiSchema.items[index] as UiSchema<T[], S, F>;
803
+ } else {
804
+ // Use the helper method for function or static object cases
805
+ itemUiSchema = this.computeItemUiSchema(uiSchema, item, index, formContext);
806
+ }
807
+ }
759
808
  const itemErrorSchema = errorSchema ? (errorSchema[index] as ErrorSchema<T[]>) : undefined;
760
809
 
761
810
  return this.renderArrayFieldItem({
@@ -770,7 +819,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
770
819
  itemSchema,
771
820
  itemData: itemCast,
772
821
  itemUiSchema,
773
- itemIdSchema,
822
+ itemFieldPathId,
774
823
  itemErrorSchema,
775
824
  autofocus: autofocus && index === 0,
776
825
  onBlur,
@@ -786,9 +835,9 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
786
835
  schema,
787
836
  uiSchema,
788
837
  title: fieldTitle,
789
- formContext,
790
838
  errorSchema,
791
839
  rawErrors,
840
+ optionalDataControl,
792
841
  };
793
842
 
794
843
  const Template = getTemplate<'ArrayFieldTemplate', T[], S, F>('ArrayFieldTemplate', registry, uiOptions);
@@ -811,8 +860,8 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
811
860
  canMoveDown: boolean;
812
861
  itemSchema: S;
813
862
  itemData: T[];
814
- itemUiSchema: UiSchema<T[], S, F>;
815
- itemIdSchema: IdSchema<T[]>;
863
+ itemUiSchema: UiSchema<T[], S, F> | undefined;
864
+ itemFieldPathId: FieldPathId;
816
865
  itemErrorSchema?: ErrorSchema<T[]>;
817
866
  autofocus?: boolean;
818
867
  onBlur: FieldProps<T[], S, F>['onBlur'];
@@ -831,7 +880,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
831
880
  itemSchema,
832
881
  itemData,
833
882
  itemUiSchema,
834
- itemIdSchema,
883
+ itemFieldPathId,
835
884
  itemErrorSchema,
836
885
  autofocus,
837
886
  onBlur,
@@ -840,7 +889,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
840
889
  totalItems,
841
890
  title,
842
891
  } = props;
843
- const { disabled, hideError, idPrefix, idSeparator, readonly, uiSchema, registry, formContext } = this.props;
892
+ const { disabled, hideError, readonly, uiSchema, registry, formContext } = this.props;
844
893
  const {
845
894
  fields: { ArraySchemaField, SchemaField },
846
895
  globalUiOptions,
@@ -867,9 +916,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
867
916
  formData={itemData}
868
917
  formContext={formContext}
869
918
  errorSchema={itemErrorSchema}
870
- idPrefix={idPrefix}
871
- idSeparator={idSeparator}
872
- idSchema={itemIdSchema}
919
+ fieldPathId={itemFieldPathId}
873
920
  required={this.isItemRequired(itemSchema)}
874
921
  onChange={this.onChangeForIndex(index)}
875
922
  onBlur={onBlur}
@@ -883,7 +930,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
883
930
  />
884
931
  ),
885
932
  buttonsProps: {
886
- idSchema: itemIdSchema,
933
+ fieldPathId: itemFieldPathId,
887
934
  disabled: disabled,
888
935
  readonly: readonly,
889
936
  canAdd,
@@ -1,3 +1,4 @@
1
+ import { useCallback } from 'react';
1
2
  import {
2
3
  getWidget,
3
4
  getUiOptions,
@@ -5,6 +6,7 @@ import {
5
6
  FieldProps,
6
7
  FormContextType,
7
8
  EnumOptionsType,
9
+ ErrorSchema,
8
10
  RJSFSchema,
9
11
  StrictRJSFSchema,
10
12
  TranslatableString,
@@ -23,7 +25,7 @@ function BooleanField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extend
23
25
  schema,
24
26
  name,
25
27
  uiSchema,
26
- idSchema,
28
+ fieldPathId,
27
29
  formData,
28
30
  registry,
29
31
  required,
@@ -86,15 +88,22 @@ function BooleanField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extend
86
88
  enumOptions = optionsList<T, S, F>({ enum: enums } as S, uiSchema);
87
89
  }
88
90
  }
91
+ const onWidgetChange = useCallback(
92
+ (value: T | undefined, errorSchema?: ErrorSchema, id?: string) => {
93
+ // Boolean field change passes an empty path array to the parent field which adds the appropriate path
94
+ return onChange(value, fieldPathId.path, errorSchema, id);
95
+ },
96
+ [onChange, fieldPathId],
97
+ );
89
98
 
90
99
  return (
91
100
  <Widget
92
101
  options={{ ...options, enumOptions }}
93
102
  schema={schema}
94
103
  uiSchema={uiSchema}
95
- id={idSchema.$id}
104
+ id={fieldPathId.$id}
96
105
  name={name}
97
- onChange={onChange}
106
+ onChange={onWidgetChange}
98
107
  onFocus={onFocus}
99
108
  onBlur={onBlur}
100
109
  label={label}