@rjsf/core 5.15.0 → 5.16.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.
@@ -533,7 +533,11 @@ export default class Form<
533
533
  const formValue = _get(formData, path);
534
534
  // adds path to fieldNames if it points to a value
535
535
  // or an empty object/array
536
- if (typeof formValue !== 'object' || _isEmpty(formValue)) {
536
+ if (
537
+ typeof formValue !== 'object' ||
538
+ _isEmpty(formValue) ||
539
+ (Array.isArray(formValue) && formValue.every((val) => typeof val !== 'object'))
540
+ ) {
537
541
  acc.push(path);
538
542
  }
539
543
  });
@@ -738,7 +742,7 @@ export default class Form<
738
742
  }
739
743
 
740
744
  /** Provides a function that can be used to programmatically submit the `Form` */
741
- submit() {
745
+ submit = () => {
742
746
  if (this.formElement.current) {
743
747
  this.formElement.current.dispatchEvent(
744
748
  new CustomEvent('submit', {
@@ -747,7 +751,7 @@ export default class Form<
747
751
  );
748
752
  this.formElement.current.requestSubmit();
749
753
  }
750
- }
754
+ };
751
755
 
752
756
  /** Attempts to focus on the field associated with the `error`. Uses the `property` field to compute path of the error
753
757
  * field, then, using the `idPrefix` and `idSeparator` converts that path into an id. Then the input element with that
@@ -470,6 +470,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
470
470
  errorSchema,
471
471
  idSchema,
472
472
  name,
473
+ title,
473
474
  disabled = false,
474
475
  readonly = false,
475
476
  autofocus = false,
@@ -482,7 +483,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
482
483
  rawErrors,
483
484
  } = this.props;
484
485
  const { keyedFormData } = this.state;
485
- const title = schema.title === undefined ? name : schema.title;
486
+ const fieldTitle = schema.title || title || name;
486
487
  const { schemaUtils, formContext } = registry;
487
488
  const uiOptions = getUiOptions<T[], S, F>(uiSchema);
488
489
  const _schemaItems: S = isObject(schema.items) ? (schema.items as S) : ({} as S);
@@ -503,6 +504,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
503
504
  key,
504
505
  index,
505
506
  name: name && `${name}-${index}`,
507
+ title: fieldTitle ? `${fieldTitle}-${index + 1}` : undefined,
506
508
  canAdd,
507
509
  canMoveUp: index > 0,
508
510
  canMoveDown: index < formData.length - 1,
@@ -526,7 +528,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
526
528
  readonly,
527
529
  required,
528
530
  schema,
529
- title,
531
+ title: fieldTitle,
530
532
  formContext,
531
533
  formData,
532
534
  rawErrors,
@@ -701,6 +703,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
701
703
  idSeparator = '_',
702
704
  idSchema,
703
705
  name,
706
+ title,
704
707
  disabled = false,
705
708
  readonly = false,
706
709
  autofocus = false,
@@ -712,7 +715,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
712
715
  } = this.props;
713
716
  const { keyedFormData } = this.state;
714
717
  let { formData: items = [] } = this.props;
715
- const title = schema.title || name;
718
+ const fieldTitle = schema.title || title || name;
716
719
  const uiOptions = getUiOptions<T[], S, F>(uiSchema);
717
720
  const { schemaUtils, formContext } = registry;
718
721
  const _schemaItems: S[] = isObject(schema.items) ? (schema.items as S[]) : ([] as S[]);
@@ -759,6 +762,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
759
762
  key,
760
763
  index,
761
764
  name: name && `${name}-${index}`,
765
+ title: fieldTitle ? `${fieldTitle}-${index + 1}` : undefined,
762
766
  canAdd,
763
767
  canRemove: additional,
764
768
  canMoveUp: index >= itemSchemas.length + 1,
@@ -781,7 +785,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
781
785
  registry,
782
786
  schema,
783
787
  uiSchema,
784
- title,
788
+ title: fieldTitle,
785
789
  formContext,
786
790
  rawErrors,
787
791
  };
@@ -799,6 +803,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
799
803
  key: string;
800
804
  index: number;
801
805
  name: string;
806
+ title: string | undefined;
802
807
  canAdd: boolean;
803
808
  canRemove?: boolean;
804
809
  canMoveUp: boolean;
@@ -832,6 +837,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
832
837
  onFocus,
833
838
  rawErrors,
834
839
  totalItems,
840
+ title,
835
841
  } = props;
836
842
  const { disabled, hideError, idPrefix, idSeparator, readonly, uiSchema, registry, formContext } = this.props;
837
843
  const {
@@ -853,6 +859,7 @@ class ArrayField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
853
859
  children: (
854
860
  <ItemSchemaField
855
861
  name={name}
862
+ title={title}
856
863
  index={index}
857
864
  schema={itemSchema}
858
865
  uiSchema={itemUiSchema}
@@ -31,12 +31,13 @@ function BooleanField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extend
31
31
  readonly,
32
32
  hideError,
33
33
  autofocus,
34
+ title,
34
35
  onChange,
35
36
  onFocus,
36
37
  onBlur,
37
38
  rawErrors,
38
39
  } = props;
39
- const { title } = schema;
40
+ const { title: schemaTitle } = schema;
40
41
  const { widgets, formContext, translateString, globalUiOptions } = registry;
41
42
  const {
42
43
  widget = 'checkbox',
@@ -49,7 +50,7 @@ function BooleanField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extend
49
50
  const yes = translateString(TranslatableString.YesLabel);
50
51
  const no = translateString(TranslatableString.NoLabel);
51
52
  let enumOptions: EnumOptionsType<S>[] | undefined;
52
- const label = uiTitle ?? title ?? name;
53
+ const label = uiTitle ?? schemaTitle ?? title ?? name;
53
54
  if (Array.isArray(schema.oneOf)) {
54
55
  enumOptions = optionsList<S>({
55
56
  oneOf: schema.oneOf
@@ -241,6 +241,7 @@ class ObjectField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
241
241
  onBlur,
242
242
  onFocus,
243
243
  registry,
244
+ title,
244
245
  } = this.props;
245
246
 
246
247
  const { fields, formContext, schemaUtils, translateString, globalUiOptions } = registry;
@@ -249,7 +250,7 @@ class ObjectField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
249
250
  const uiOptions = getUiOptions<T, S, F>(uiSchema, globalUiOptions);
250
251
  const { properties: schemaProperties = {} } = schema;
251
252
 
252
- const title = uiOptions.title ?? schema.title ?? name;
253
+ const templateTitle = uiOptions.title ?? schema.title ?? title ?? name;
253
254
  const description = uiOptions.description ?? schema.description;
254
255
  let orderedProperties: string[];
255
256
  try {
@@ -272,7 +273,7 @@ class ObjectField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
272
273
 
273
274
  const templateProps = {
274
275
  // getDisplayLabel() always returns false for object types, so just check the `uiOptions.label`
275
- title: uiOptions.label === false ? '' : title,
276
+ title: uiOptions.label === false ? '' : templateTitle,
276
277
  description: uiOptions.label === false ? undefined : description,
277
278
  properties: orderedProperties.map((name) => {
278
279
  const addedByAdditionalProperties = has(schema, [PROPERTIES_KEY, name, ADDITIONAL_PROPERTY_FLAG]);
@@ -193,7 +193,10 @@ function SchemaFieldRender<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
193
193
  if (wasPropertyKeyModified) {
194
194
  label = name;
195
195
  } else {
196
- label = ADDITIONAL_PROPERTY_FLAG in schema ? name : uiOptions.title || props.schema.title || schema.title || name;
196
+ label =
197
+ ADDITIONAL_PROPERTY_FLAG in schema
198
+ ? name
199
+ : uiOptions.title || props.schema.title || schema.title || props.title || name;
197
200
  }
198
201
 
199
202
  const description = uiOptions.description || props.schema.description || schema.description || '';
@@ -5,11 +5,13 @@ import {
5
5
  toDateString,
6
6
  pad,
7
7
  DateObject,
8
+ type DateElementFormat,
8
9
  FormContextType,
9
10
  RJSFSchema,
10
11
  StrictRJSFSchema,
11
12
  TranslatableString,
12
13
  WidgetProps,
14
+ getDateElementProps,
13
15
  } from '@rjsf/utils';
14
16
 
15
17
  function rangeOptions(start: number, stop: number) {
@@ -24,31 +26,6 @@ function readyForChange(state: DateObject) {
24
26
  return Object.values(state).every((value) => value !== -1);
25
27
  }
26
28
 
27
- function dateElementProps(
28
- state: DateObject,
29
- time: boolean,
30
- yearsRange: [number, number] = [1900, new Date().getFullYear() + 2]
31
- ) {
32
- const { year, month, day, hour, minute, second } = state;
33
- const data = [
34
- {
35
- type: 'year',
36
- range: yearsRange,
37
- value: year,
38
- },
39
- { type: 'month', range: [1, 12], value: month },
40
- { type: 'day', range: [1, 31], value: day },
41
- ] as { type: string; range: [number, number]; value: number | undefined }[];
42
- if (time) {
43
- data.push(
44
- { type: 'hour', range: [0, 23], value: hour },
45
- { type: 'minute', range: [0, 59], value: minute },
46
- { type: 'second', range: [0, 59], value: second }
47
- );
48
- }
49
- return data;
50
- }
51
-
52
29
  type DateElementProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = Pick<
53
30
  WidgetProps<T, S, F>,
54
31
  'value' | 'name' | 'disabled' | 'readonly' | 'autofocus' | 'registry' | 'onBlur' | 'onFocus'
@@ -161,7 +138,12 @@ function AltDateWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F exten
161
138
 
162
139
  return (
163
140
  <ul className='list-inline'>
164
- {dateElementProps(state, time, options.yearsRange as [number, number] | undefined).map((elemProps, i) => (
141
+ {getDateElementProps(
142
+ state,
143
+ time,
144
+ options.yearsRange as [number, number] | undefined,
145
+ options.format as DateElementFormat | undefined
146
+ ).map((elemProps, i) => (
165
147
  <li className='list-inline-item' key={i}>
166
148
  <DateElement
167
149
  rootId={id}
@@ -1,4 +1,4 @@
1
- import { ChangeEvent, useCallback, useState } from 'react';
1
+ import { ChangeEvent, useCallback, useMemo } from 'react';
2
2
  import {
3
3
  dataURItoBlob,
4
4
  FormContextType,
@@ -7,6 +7,7 @@ import {
7
7
  RJSFSchema,
8
8
  StrictRJSFSchema,
9
9
  TranslatableString,
10
+ UIOptionsType,
10
11
  WidgetProps,
11
12
  } from '@rjsf/utils';
12
13
  import Markdown from 'markdown-to-jsx';
@@ -86,23 +87,32 @@ function FilesInfo<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends F
86
87
  filesInfo,
87
88
  registry,
88
89
  preview,
90
+ onRemove,
91
+ options,
89
92
  }: {
90
93
  filesInfo: FileInfoType[];
91
94
  registry: Registry<T, S, F>;
92
95
  preview?: boolean;
96
+ onRemove: (index: number) => void;
97
+ options: UIOptionsType<T, S, F>;
93
98
  }) {
94
99
  if (filesInfo.length === 0) {
95
100
  return null;
96
101
  }
97
102
  const { translateString } = registry;
103
+
104
+ const { RemoveButton } = getTemplate<'ButtonTemplates', T, S, F>('ButtonTemplates', registry, options);
105
+
98
106
  return (
99
107
  <ul className='file-info'>
100
108
  {filesInfo.map((fileInfo, key) => {
101
109
  const { name, size, type } = fileInfo;
110
+ const handleRemove = () => onRemove(key);
102
111
  return (
103
112
  <li key={key}>
104
113
  <Markdown>{translateString(TranslatableString.FilesInfo, [name, type, String(size)])}</Markdown>
105
114
  {preview && <FileInfoPreview<T, S, F> fileInfo={fileInfo} registry={registry} />}
115
+ <RemoveButton onClick={handleRemove} registry={registry} />
106
116
  </li>
107
117
  );
108
118
  })}
@@ -133,9 +143,6 @@ function FileWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends
133
143
  ) {
134
144
  const { disabled, readonly, required, multiple, onChange, value, options, registry } = props;
135
145
  const BaseInputTemplate = getTemplate<'BaseInputTemplate', T, S, F>('BaseInputTemplate', registry, options);
136
- const [filesInfo, setFilesInfo] = useState<FileInfoType[]>(
137
- Array.isArray(value) ? extractFileInfo(value) : extractFileInfo([value])
138
- );
139
146
 
140
147
  const handleChange = useCallback(
141
148
  (event: ChangeEvent<HTMLInputElement>) => {
@@ -148,17 +155,27 @@ function FileWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends
148
155
  processFiles(event.target.files).then((filesInfoEvent) => {
149
156
  const newValue = filesInfoEvent.map((fileInfo) => fileInfo.dataURL);
150
157
  if (multiple) {
151
- setFilesInfo(filesInfo.concat(filesInfoEvent[0]));
152
158
  onChange(value.concat(newValue[0]));
153
159
  } else {
154
- setFilesInfo(filesInfoEvent);
155
160
  onChange(newValue[0]);
156
161
  }
157
162
  });
158
163
  },
159
- [multiple, value, filesInfo, onChange]
164
+ [multiple, value, onChange]
160
165
  );
161
166
 
167
+ const filesInfo = useMemo(() => extractFileInfo(Array.isArray(value) ? value : [value]), [value]);
168
+ const rmFile = useCallback(
169
+ (index: number) => {
170
+ if (multiple) {
171
+ const newValue = value.filter((_: any, i: number) => i !== index);
172
+ onChange(newValue);
173
+ } else {
174
+ onChange(undefined);
175
+ }
176
+ },
177
+ [multiple, value, onChange]
178
+ );
162
179
  return (
163
180
  <div>
164
181
  <BaseInputTemplate
@@ -170,7 +187,13 @@ function FileWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends
170
187
  value=''
171
188
  accept={options.accept ? String(options.accept) : undefined}
172
189
  />
173
- <FilesInfo<T, S, F> filesInfo={filesInfo} registry={registry} preview={options.filePreview} />
190
+ <FilesInfo<T, S, F>
191
+ filesInfo={filesInfo}
192
+ onRemove={rmFile}
193
+ registry={registry}
194
+ preview={options.filePreview}
195
+ options={options}
196
+ />
174
197
  </div>
175
198
  );
176
199
  }