@rjsf/utils 6.0.0-beta.22 → 6.0.0-beta.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/types.d.ts CHANGED
@@ -280,6 +280,8 @@ export type TemplatesType<T = any, S extends StrictRJSFSchema = RJSFSchema, F ex
280
280
  DescriptionFieldTemplate: ComponentType<DescriptionFieldProps<T, S, F>>;
281
281
  /** The template to use while rendering the errors for the whole form */
282
282
  ErrorListTemplate: ComponentType<ErrorListProps<T, S, F>>;
283
+ /** The template to use while rendering a fallback field for schemas that have an empty or unknown 'type' */
284
+ FallbackFieldTemplate: ComponentType<FallbackFieldTemplateProps<T, S, F>>;
283
285
  /** The template to use while rendering the errors for a single field */
284
286
  FieldErrorTemplate: ComponentType<FieldErrorProps<T, S, F>>;
285
287
  /** The template to use while rendering the errors for a single field */
@@ -367,6 +369,11 @@ export type GlobalFormOptions = {
367
369
  * (`root[tasks][0][title]`) or Django (`root__tasks-0__title`) to receive form data in their expected format.
368
370
  */
369
371
  readonly nameGenerator?: NameGeneratorFunction;
372
+ /**
373
+ * Boolean flag that, when set to true, will cause the form to use a fallback UI when encountering a schema type that
374
+ * is not supported by RJSF or a custom field. When false, the UnsupportedField error component will be shown instead.
375
+ */
376
+ readonly useFallbackUiForUnsupportedType?: boolean;
370
377
  };
371
378
  /** The object containing the registered core, theme and custom fields and widgets as well as the root schema, form
372
379
  * context, schema utils and templates.
@@ -491,6 +498,19 @@ export type FieldTemplateProps<T = any, S extends StrictRJSFSchema = RJSFSchema,
491
498
  /** Callback used to handle the removal of the additionalProperty */
492
499
  onRemoveProperty: () => void;
493
500
  };
501
+ /**
502
+ * The properties that are passed to a `FallbackField` implementation
503
+ */
504
+ export type FallbackFieldProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = FieldProps<T, S, F>;
505
+ /**
506
+ * The properties that are passed to a `FallbackFieldTemplate` implementation
507
+ */
508
+ export type FallbackFieldTemplateProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = RJSFBaseProps<T, S, F> & {
509
+ /** A ReactNode that allows the selecting a different type for the field */
510
+ typeSelector: ReactNode;
511
+ /** A ReactNode that renders the field with the present formData and matches the selected type */
512
+ schemaField: ReactNode;
513
+ };
494
514
  /** The properties that are passed to the `UnsupportedFieldTemplate` implementation */
495
515
  export type UnsupportedFieldProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = RJSFBaseProps<T, S, F> & {
496
516
  /** The FieldPathId of the field in the hierarchy */
@@ -0,0 +1,39 @@
1
+ import { MouseEvent } from 'react';
2
+ import { DateElementProp } from './getDateElementProps.js';
3
+ import { DateObject, FormContextType, RJSFSchema, StrictRJSFSchema, WidgetProps } from './types.js';
4
+ /** The Props for the `DateElement` component */
5
+ export type DateElementProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = Pick<WidgetProps<T, S, F>, 'value' | 'name' | 'disabled' | 'readonly' | 'autofocus' | 'registry' | 'onBlur' | 'onFocus' | 'className'> & {
6
+ /** The root id of the field */
7
+ rootId: string;
8
+ /** The selector function for a specific prop within the `DateObject`, for a value */
9
+ select: (property: keyof DateObject, value: any) => void;
10
+ /** The type of the date element */
11
+ type: DateElementProp['type'];
12
+ /** The range for the date element */
13
+ range: DateElementProp['range'];
14
+ };
15
+ /** The `DateElement` component renders one of the 6 date element selectors for an `AltDateWidget`, using the `select`
16
+ * widget from the registry.
17
+ *
18
+ * @param props - The `DateElementProps` for the date element
19
+ */
20
+ export declare function DateElement<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(props: DateElementProps<T, S, F>): import("react/jsx-runtime").JSX.Element;
21
+ /** The result of a call to the `useAltDateWidgetProps()` hook */
22
+ export type UseAltDateWidgetResult = {
23
+ /** The list of `DateElementProp` data to render for the `AltDateWidget` */
24
+ elements: DateElementProp[];
25
+ /** The callback that handles the changing of DateElement components */
26
+ handleChange: (property: keyof DateObject, value?: string) => void;
27
+ /** The callback that will clear the `AltDateWidget` when a button is clicked */
28
+ handleClear: (event: MouseEvent) => void;
29
+ /** The callback that will set the `AltDateWidget` to NOW when a button is clicked */
30
+ handleSetNow: (event: MouseEvent) => void;
31
+ };
32
+ /** Hook which encapsulates the logic needed to render an `AltDateWidget` with optional `time` elements. It contains
33
+ * the `state` of the current date(/time) selections in the widget. It returns a `UseAltDateWidgetResult` object
34
+ * that contains the `elements: DateElementProp[]` and three callbacks needed to change one of the rendered `elements`,
35
+ * and to handle the clicking of the `clear` and `setNow` buttons.
36
+ *
37
+ * @param props - The `WidgetProps` for the `AltDateWidget`
38
+ */
39
+ export default function useAltDateWidgetProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(props: WidgetProps<T, S, F>): UseAltDateWidgetResult;
@@ -0,0 +1,71 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useMemo, useState } from 'react';
3
+ import dateRangeOptions from './dateRangeOptions.js';
4
+ import getDateElementProps from './getDateElementProps.js';
5
+ import { ariaDescribedByIds } from './idGenerators.js';
6
+ import parseDateString from './parseDateString.js';
7
+ import toDateString from './toDateString.js';
8
+ /** Function that checks to see if a `DateObject` is ready for the onChange callback to be triggered
9
+ *
10
+ * @param state - The current `DateObject`
11
+ * @returns - True if the `state` is ready to trigger an onChange
12
+ */
13
+ function readyForChange(state) {
14
+ return Object.values(state).every((value) => value !== -1);
15
+ }
16
+ /** The `DateElement` component renders one of the 6 date element selectors for an `AltDateWidget`, using the `select`
17
+ * widget from the registry.
18
+ *
19
+ * @param props - The `DateElementProps` for the date element
20
+ */
21
+ export function DateElement(props) {
22
+ const { className = 'form-control', type, range, value, select, rootId, name, disabled, readonly, autofocus, registry, onBlur, onFocus, } = props;
23
+ const id = `${rootId}_${type}`;
24
+ const { SelectWidget } = registry.widgets;
25
+ const onChange = useCallback((value) => select(type, value), [select, type]);
26
+ return (_jsx(SelectWidget, { schema: { type: 'integer' }, id: id, name: name, className: className, options: { enumOptions: dateRangeOptions(range[0], range[1]) }, placeholder: type, value: value, disabled: disabled, readonly: readonly, autofocus: autofocus, onChange: onChange, onBlur: onBlur, onFocus: onFocus, registry: registry, label: '', "aria-describedby": ariaDescribedByIds(rootId) }));
27
+ }
28
+ /** Hook which encapsulates the logic needed to render an `AltDateWidget` with optional `time` elements. It contains
29
+ * the `state` of the current date(/time) selections in the widget. It returns a `UseAltDateWidgetResult` object
30
+ * that contains the `elements: DateElementProp[]` and three callbacks needed to change one of the rendered `elements`,
31
+ * and to handle the clicking of the `clear` and `setNow` buttons.
32
+ *
33
+ * @param props - The `WidgetProps` for the `AltDateWidget`
34
+ */
35
+ export default function useAltDateWidgetProps(props) {
36
+ const { time = false, disabled = false, readonly = false, options, onChange, value } = props;
37
+ const [state, setState] = useState(parseDateString(value, time));
38
+ useEffect(() => {
39
+ setState(parseDateString(value, time));
40
+ }, [time, value]);
41
+ const handleChange = useCallback((property, value) => {
42
+ const nextState = {
43
+ ...state,
44
+ [property]: typeof value === 'undefined' ? -1 : value,
45
+ };
46
+ if (readyForChange(nextState)) {
47
+ onChange(toDateString(nextState, time));
48
+ }
49
+ else {
50
+ setState(nextState);
51
+ }
52
+ }, [state, onChange, time]);
53
+ const handleClear = useCallback((event) => {
54
+ event.preventDefault();
55
+ if (disabled || readonly) {
56
+ return;
57
+ }
58
+ onChange(undefined);
59
+ }, [disabled, readonly, onChange]);
60
+ const handleSetNow = useCallback((event) => {
61
+ event.preventDefault();
62
+ if (disabled || readonly) {
63
+ return;
64
+ }
65
+ const nextState = parseDateString(new Date().toJSON(), time);
66
+ onChange(toDateString(nextState, time));
67
+ }, [disabled, readonly, time, onChange]);
68
+ const elements = useMemo(() => getDateElementProps(state, time, options.yearsRange, options.format), [state, time, options.yearsRange, options.format]);
69
+ return { elements, handleChange, handleClear, handleSetNow };
70
+ }
71
+ //# sourceMappingURL=useAltDateWidgetProps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAltDateWidgetProps.js","sourceRoot":"","sources":["../src/useAltDateWidgetProps.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /** The information about files used by a FileWidget */
2
+ export type FileInfoType = {
3
+ /** The url of the data containing the file */
4
+ dataURL?: string | null;
5
+ /** The name of the file */
6
+ name: string;
7
+ /** The size of the file */
8
+ size: number;
9
+ /** The type of the file */
10
+ type: string;
11
+ };
12
+ export interface UseFileWidgetPropsResult {
13
+ /** The list of FileInfoType contained within the FileWidget */
14
+ filesInfo: FileInfoType[];
15
+ /** The callback handler to pass to the onChange of the input */
16
+ handleChange: (files: FileList) => void;
17
+ /** The callback handler to pass in order to delete a file */
18
+ handleRemove: (index: number) => void;
19
+ }
20
+ /** Hook which encapsulates the logic needed to read and convert a `value` of `File` or `File[]` into the
21
+ * `filesInfo: FileInfoType[]` and the two callback implementations needed to change the list or to remove a
22
+ * `File` from the list. To be used by theme specific `FileWidget` implementations.
23
+ *
24
+ * @param value - The current value of the `FileWidget`
25
+ * @param onChange - The onChange handler for the `FileWidget`
26
+ * @param [multiple=false] - Flag indicating whether the control supports multiple selections
27
+ * @returns - The `UseFileWidgetPropsResult` to be used within a `FileWidget` implementation
28
+ */
29
+ export default function useFileWidgetProps(value: string | string[] | undefined | null, onChange: (value?: string | null | (string | null)[]) => void, multiple?: boolean): UseFileWidgetPropsResult;
@@ -0,0 +1,119 @@
1
+ import { useCallback, useMemo } from 'react';
2
+ import dataURItoBlob from './dataURItoBlob.js';
3
+ /** Updated the given `dataUrl` to add the `name` to it
4
+ *
5
+ * @param dataURL - The url description string
6
+ * @param name - The name of the file to add to the dataUrl
7
+ * @returns - The `dataUrl` updated to include the name
8
+ */
9
+ function addNameToDataURL(dataURL, name) {
10
+ return dataURL.replace(';base64', `;name=${encodeURIComponent(name)};base64`);
11
+ }
12
+ /** Returns a promise that will read the file from the browser and return it as the result of the promise.
13
+ *
14
+ * @param file - The `File` information to read
15
+ * @returns - A promise that resolves to the read file.
16
+ */
17
+ function processFile(file) {
18
+ const { name, size, type } = file;
19
+ return new Promise((resolve, reject) => {
20
+ const reader = new window.FileReader();
21
+ reader.onerror = reject;
22
+ reader.onload = (event) => {
23
+ var _a;
24
+ if (typeof ((_a = event.target) === null || _a === void 0 ? void 0 : _a.result) === 'string') {
25
+ resolve({
26
+ dataURL: addNameToDataURL(event.target.result, name),
27
+ name,
28
+ size,
29
+ type,
30
+ });
31
+ }
32
+ else {
33
+ resolve({
34
+ dataURL: null,
35
+ name,
36
+ size,
37
+ type,
38
+ });
39
+ }
40
+ };
41
+ reader.readAsDataURL(file);
42
+ });
43
+ }
44
+ /** Reads a list of files from the browser, returning the results of the promises of each individual file read.
45
+ *
46
+ * @param files - The list of files to read
47
+ * @returns - The list of read files
48
+ */
49
+ function processFiles(files) {
50
+ return Promise.all(Array.from(files).map(processFile));
51
+ }
52
+ /** Extracts the file information from the data URLs
53
+ *
54
+ * @param dataURLs - The information about the files
55
+ * @returns - The list of `FileInfoType` objects extracted from the data urls
56
+ */
57
+ function extractFileInfo(dataURLs) {
58
+ return dataURLs.reduce((acc, dataURL) => {
59
+ if (!dataURL) {
60
+ return acc;
61
+ }
62
+ try {
63
+ const { blob, name } = dataURItoBlob(dataURL);
64
+ return [
65
+ ...acc,
66
+ {
67
+ dataURL,
68
+ name: name,
69
+ size: blob.size,
70
+ type: blob.type,
71
+ },
72
+ ];
73
+ }
74
+ catch (_a) {
75
+ // Invalid dataURI, so just ignore it.
76
+ return acc;
77
+ }
78
+ }, []);
79
+ }
80
+ /** Hook which encapsulates the logic needed to read and convert a `value` of `File` or `File[]` into the
81
+ * `filesInfo: FileInfoType[]` and the two callback implementations needed to change the list or to remove a
82
+ * `File` from the list. To be used by theme specific `FileWidget` implementations.
83
+ *
84
+ * @param value - The current value of the `FileWidget`
85
+ * @param onChange - The onChange handler for the `FileWidget`
86
+ * @param [multiple=false] - Flag indicating whether the control supports multiple selections
87
+ * @returns - The `UseFileWidgetPropsResult` to be used within a `FileWidget` implementation
88
+ */
89
+ export default function useFileWidgetProps(value, onChange, multiple = false) {
90
+ const values = useMemo(() => {
91
+ if (multiple && value) {
92
+ return Array.isArray(value) ? value : [value];
93
+ }
94
+ return [];
95
+ }, [value, multiple]);
96
+ const filesInfo = useMemo(() => (Array.isArray(value) ? extractFileInfo(value) : extractFileInfo([value || ''])), [value]);
97
+ const handleChange = useCallback((files) => {
98
+ processFiles(files).then((filesInfoEvent) => {
99
+ const newValue = filesInfoEvent.map((fileInfo) => fileInfo.dataURL || null);
100
+ if (multiple) {
101
+ onChange(values.concat(...newValue));
102
+ }
103
+ else {
104
+ onChange(newValue[0]);
105
+ }
106
+ });
107
+ }, [values, multiple, onChange]);
108
+ const handleRemove = useCallback((index) => {
109
+ if (multiple) {
110
+ const newValue = values.filter((_, i) => i !== index);
111
+ onChange(newValue);
112
+ }
113
+ else {
114
+ onChange(undefined);
115
+ }
116
+ }, [values, multiple, onChange]);
117
+ return { filesInfo, handleChange, handleRemove };
118
+ }
119
+ //# sourceMappingURL=useFileWidgetProps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFileWidgetProps.js","sourceRoot":"","sources":["../src/useFileWidgetProps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAE7C,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAuB5C;;;;;GAKG;AACH,SAAS,gBAAgB,CAAC,OAAe,EAAE,IAAY;IACrD,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,SAAS,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAChF,CAAC;AAED;;;;GAIG;AACH,SAAS,WAAW,CAAC,IAAU;IAC7B,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;IAClC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACvC,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC;QACxB,MAAM,CAAC,MAAM,GAAG,CAAC,KAAK,EAAE,EAAE;;YACxB,IAAI,OAAO,CAAA,MAAA,KAAK,CAAC,MAAM,0CAAE,MAAM,CAAA,KAAK,QAAQ,EAAE,CAAC;gBAC7C,OAAO,CAAC;oBACN,OAAO,EAAE,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC;oBACpD,IAAI;oBACJ,IAAI;oBACJ,IAAI;iBACL,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC;oBACN,OAAO,EAAE,IAAI;oBACb,IAAI;oBACJ,IAAI;oBACJ,IAAI;iBACL,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC;QACF,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,SAAS,YAAY,CAAC,KAAe;IACnC,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC;AACzD,CAAC;AAED;;;;GAIG;AACH,SAAS,eAAe,CAAC,QAAkB;IACzC,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE;QACtC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG,CAAC;QACb,CAAC;QACD,IAAI,CAAC;YACH,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;YAC9C,OAAO;gBACL,GAAG,GAAG;gBACN;oBACE,OAAO;oBACP,IAAI,EAAE,IAAI;oBACV,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,IAAI,EAAE,IAAI,CAAC,IAAI;iBAChB;aACF,CAAC;QACJ,CAAC;QAAC,WAAM,CAAC;YACP,sCAAsC;YACtC,OAAO,GAAG,CAAC;QACb,CAAC;IACH,CAAC,EAAE,EAAoB,CAAC,CAAC;AAC3B,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,UAAU,kBAAkB,CACxC,KAA2C,EAC3C,QAA6D,EAC7D,QAAQ,GAAG,KAAK;IAEhB,MAAM,MAAM,GAAsB,OAAO,CAAC,GAAG,EAAE;QAC7C,IAAI,QAAQ,IAAI,KAAK,EAAE,CAAC;YACtB,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAChD,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;IACtB,MAAM,SAAS,GAAG,OAAO,CACvB,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,EACtF,CAAC,KAAK,CAAC,CACR,CAAC;IAEF,MAAM,YAAY,GAAG,WAAW,CAC9B,CAAC,KAAe,EAAE,EAAE;QAClB,YAAY,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,cAAc,EAAE,EAAE;YAC1C,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC;YAC5E,IAAI,QAAQ,EAAE,CAAC;gBACb,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;YACvC,CAAC;iBAAM,CAAC;gBACN,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;YACxB,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,EACD,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAC7B,CAAC;IACF,MAAM,YAAY,GAAG,WAAW,CAC9B,CAAC,KAAa,EAAE,EAAE;QAChB,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAS,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC;YAC9D,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACrB,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,SAAS,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,EACD,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAC7B,CAAC;IAEF,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC;AACnD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rjsf/utils",
3
- "version": "6.0.0-beta.22",
3
+ "version": "6.0.0-beta.23",
4
4
  "main": "dist/index.js",
5
5
  "module": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
package/src/enums.ts CHANGED
@@ -51,6 +51,10 @@ export enum TranslatableString {
51
51
  OptionalObjectRemove = 'Remove data for optional field',
52
52
  /** The label for when displaying a non-editable form with missing optional field data */
53
53
  OptionalObjectEmptyMsg = 'No data for optional field',
54
+ /** Label for the schema type selector, used by FallbackField */
55
+ Type = 'Type',
56
+ /** Label for the 'value' field, used by FallbackField */
57
+ Value = 'Value',
54
58
  // Strings with replaceable parameters
55
59
  /** Unknown field type reason, where %1 will be replaced with the type as provided by SchemaField */
56
60
  UnknownFieldType = 'Unknown field type %1',
@@ -4,7 +4,7 @@ import { type DateObject } from './types';
4
4
  export type DateElementFormat = 'DMY' | 'MDY' | 'YMD';
5
5
 
6
6
  /** Type describing format of DateElement prop */
7
- type DateElementProp = {
7
+ export type DateElementProp = {
8
8
  type: string;
9
9
  range: [number, number];
10
10
  value: number | undefined;
package/src/index.ts CHANGED
@@ -16,7 +16,7 @@ import enumOptionsValueForIndex from './enumOptionsValueForIndex';
16
16
  import ErrorSchemaBuilder from './ErrorSchemaBuilder';
17
17
  import findSchemaDefinition from './findSchemaDefinition';
18
18
  import getChangedFields from './getChangedFields';
19
- import getDateElementProps, { type DateElementFormat } from './getDateElementProps';
19
+ import getDateElementProps, { DateElementFormat, DateElementProp } from './getDateElementProps';
20
20
  import getDiscriminatorFieldFromSchema from './getDiscriminatorFieldFromSchema';
21
21
  import getInputProps from './getInputProps';
22
22
  import getOptionMatchingSimpleDiscriminator from './getOptionMatchingSimpleDiscriminator';
@@ -59,7 +59,7 @@ import parseDateString from './parseDateString';
59
59
  import rangeSpec from './rangeSpec';
60
60
  import replaceStringParameters from './replaceStringParameters';
61
61
  import schemaRequiresTrueValue from './schemaRequiresTrueValue';
62
- import shouldRender from './shouldRender';
62
+ import shouldRender, { ComponentUpdateStrategy } from './shouldRender';
63
63
  import shouldRenderOptionalField from './shouldRenderOptionalField';
64
64
  import toConstant from './toConstant';
65
65
  import toDateString from './toDateString';
@@ -67,7 +67,9 @@ import toErrorList from './toErrorList';
67
67
  import toErrorSchema from './toErrorSchema';
68
68
  import toFieldPathId from './toFieldPathId';
69
69
  import unwrapErrorHandler from './unwrapErrorHandler';
70
+ import useAltDateWidgetProps, { DateElement, DateElementProps, UseAltDateWidgetResult } from './useAltDateWidgetProps';
70
71
  import useDeepCompareMemo from './useDeepCompareMemo';
72
+ import useFileWidgetProps, { FileInfoType, UseFileWidgetPropsResult } from './useFileWidgetProps';
71
73
  import utcToLocal from './utcToLocal';
72
74
  import validationDataMerge from './validationDataMerge';
73
75
  import withIdRefPrefix from './withIdRefPrefix';
@@ -80,6 +82,16 @@ export * from './constants';
80
82
  export * from './parser';
81
83
  export * from './schema';
82
84
 
85
+ export type {
86
+ ComponentUpdateStrategy,
87
+ DateElementFormat,
88
+ DateElementProp,
89
+ DateElementProps,
90
+ FileInfoType,
91
+ UseAltDateWidgetResult,
92
+ UseFileWidgetPropsResult,
93
+ };
94
+
83
95
  export {
84
96
  allowAdditionalItems,
85
97
  ariaDescribedByIds,
@@ -88,7 +100,7 @@ export {
88
100
  canExpand,
89
101
  createErrorHandler,
90
102
  createSchemaUtils,
91
- DateElementFormat,
103
+ DateElement,
92
104
  dataURItoBlob,
93
105
  dateRangeOptions,
94
106
  deepEquals,
@@ -152,12 +164,12 @@ export {
152
164
  toErrorSchema,
153
165
  toFieldPathId,
154
166
  unwrapErrorHandler,
167
+ useAltDateWidgetProps,
155
168
  useDeepCompareMemo,
169
+ useFileWidgetProps,
156
170
  utcToLocal,
157
171
  validationDataMerge,
158
172
  withIdRefPrefix,
159
173
  bracketNameGenerator,
160
174
  dotNotationNameGenerator,
161
175
  };
162
-
163
- export type { ComponentUpdateStrategy } from './shouldRender';
package/src/types.ts CHANGED
@@ -344,6 +344,8 @@ export type TemplatesType<T = any, S extends StrictRJSFSchema = RJSFSchema, F ex
344
344
  DescriptionFieldTemplate: ComponentType<DescriptionFieldProps<T, S, F>>;
345
345
  /** The template to use while rendering the errors for the whole form */
346
346
  ErrorListTemplate: ComponentType<ErrorListProps<T, S, F>>;
347
+ /** The template to use while rendering a fallback field for schemas that have an empty or unknown 'type' */
348
+ FallbackFieldTemplate: ComponentType<FallbackFieldTemplateProps<T, S, F>>;
347
349
  /** The template to use while rendering the errors for a single field */
348
350
  FieldErrorTemplate: ComponentType<FieldErrorProps<T, S, F>>;
349
351
  /** The template to use while rendering the errors for a single field */
@@ -431,6 +433,11 @@ export type GlobalFormOptions = {
431
433
  * (`root[tasks][0][title]`) or Django (`root__tasks-0__title`) to receive form data in their expected format.
432
434
  */
433
435
  readonly nameGenerator?: NameGeneratorFunction;
436
+ /**
437
+ * Boolean flag that, when set to true, will cause the form to use a fallback UI when encountering a schema type that
438
+ * is not supported by RJSF or a custom field. When false, the UnsupportedField error component will be shown instead.
439
+ */
440
+ readonly useFallbackUiForUnsupportedType?: boolean;
434
441
  };
435
442
 
436
443
  /** The object containing the registered core, theme and custom fields and widgets as well as the root schema, form
@@ -569,6 +576,29 @@ export type FieldTemplateProps<
569
576
  onRemoveProperty: () => void;
570
577
  };
571
578
 
579
+ /**
580
+ * The properties that are passed to a `FallbackField` implementation
581
+ */
582
+ export type FallbackFieldProps<
583
+ T = any,
584
+ S extends StrictRJSFSchema = RJSFSchema,
585
+ F extends FormContextType = any,
586
+ > = FieldProps<T, S, F>;
587
+
588
+ /**
589
+ * The properties that are passed to a `FallbackFieldTemplate` implementation
590
+ */
591
+ export type FallbackFieldTemplateProps<
592
+ T = any,
593
+ S extends StrictRJSFSchema = RJSFSchema,
594
+ F extends FormContextType = any,
595
+ > = RJSFBaseProps<T, S, F> & {
596
+ /** A ReactNode that allows the selecting a different type for the field */
597
+ typeSelector: ReactNode;
598
+ /** A ReactNode that renders the field with the present formData and matches the selected type */
599
+ schemaField: ReactNode;
600
+ };
601
+
572
602
  /** The properties that are passed to the `UnsupportedFieldTemplate` implementation */
573
603
  export type UnsupportedFieldProps<
574
604
  T = any,
@@ -0,0 +1,163 @@
1
+ import { MouseEvent, useCallback, useEffect, useMemo, useState } from 'react';
2
+
3
+ import dateRangeOptions from './dateRangeOptions';
4
+ import getDateElementProps, { DateElementFormat, DateElementProp } from './getDateElementProps';
5
+ import { ariaDescribedByIds } from './idGenerators';
6
+ import parseDateString from './parseDateString';
7
+ import toDateString from './toDateString';
8
+ import { DateObject, FormContextType, RJSFSchema, StrictRJSFSchema, WidgetProps } from './types';
9
+
10
+ /** Function that checks to see if a `DateObject` is ready for the onChange callback to be triggered
11
+ *
12
+ * @param state - The current `DateObject`
13
+ * @returns - True if the `state` is ready to trigger an onChange
14
+ */
15
+ function readyForChange(state: DateObject) {
16
+ return Object.values(state).every((value) => value !== -1);
17
+ }
18
+
19
+ /** The Props for the `DateElement` component */
20
+ export type DateElementProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = Pick<
21
+ WidgetProps<T, S, F>,
22
+ 'value' | 'name' | 'disabled' | 'readonly' | 'autofocus' | 'registry' | 'onBlur' | 'onFocus' | 'className'
23
+ > & {
24
+ /** The root id of the field */
25
+ rootId: string;
26
+ /** The selector function for a specific prop within the `DateObject`, for a value */
27
+ select: (property: keyof DateObject, value: any) => void;
28
+ /** The type of the date element */
29
+ type: DateElementProp['type'];
30
+ /** The range for the date element */
31
+ range: DateElementProp['range'];
32
+ };
33
+
34
+ /** The `DateElement` component renders one of the 6 date element selectors for an `AltDateWidget`, using the `select`
35
+ * widget from the registry.
36
+ *
37
+ * @param props - The `DateElementProps` for the date element
38
+ */
39
+ export function DateElement<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
40
+ props: DateElementProps<T, S, F>,
41
+ ) {
42
+ const {
43
+ className = 'form-control',
44
+ type,
45
+ range,
46
+ value,
47
+ select,
48
+ rootId,
49
+ name,
50
+ disabled,
51
+ readonly,
52
+ autofocus,
53
+ registry,
54
+ onBlur,
55
+ onFocus,
56
+ } = props;
57
+ const id = `${rootId}_${type}`;
58
+ const { SelectWidget } = registry.widgets;
59
+ const onChange = useCallback((value: any) => select(type as keyof DateObject, value), [select, type]);
60
+ return (
61
+ <SelectWidget
62
+ schema={{ type: 'integer' } as S}
63
+ id={id}
64
+ name={name}
65
+ className={className}
66
+ options={{ enumOptions: dateRangeOptions<S>(range[0], range[1]) }}
67
+ placeholder={type}
68
+ value={value}
69
+ disabled={disabled}
70
+ readonly={readonly}
71
+ autofocus={autofocus}
72
+ onChange={onChange}
73
+ onBlur={onBlur}
74
+ onFocus={onFocus}
75
+ registry={registry}
76
+ label=''
77
+ aria-describedby={ariaDescribedByIds(rootId)}
78
+ />
79
+ );
80
+ }
81
+
82
+ /** The result of a call to the `useAltDateWidgetProps()` hook */
83
+ export type UseAltDateWidgetResult = {
84
+ /** The list of `DateElementProp` data to render for the `AltDateWidget` */
85
+ elements: DateElementProp[];
86
+ /** The callback that handles the changing of DateElement components */
87
+ handleChange: (property: keyof DateObject, value?: string) => void;
88
+ /** The callback that will clear the `AltDateWidget` when a button is clicked */
89
+ handleClear: (event: MouseEvent) => void;
90
+ /** The callback that will set the `AltDateWidget` to NOW when a button is clicked */
91
+ handleSetNow: (event: MouseEvent) => void;
92
+ };
93
+
94
+ /** Hook which encapsulates the logic needed to render an `AltDateWidget` with optional `time` elements. It contains
95
+ * the `state` of the current date(/time) selections in the widget. It returns a `UseAltDateWidgetResult` object
96
+ * that contains the `elements: DateElementProp[]` and three callbacks needed to change one of the rendered `elements`,
97
+ * and to handle the clicking of the `clear` and `setNow` buttons.
98
+ *
99
+ * @param props - The `WidgetProps` for the `AltDateWidget`
100
+ */
101
+ export default function useAltDateWidgetProps<
102
+ T = any,
103
+ S extends StrictRJSFSchema = RJSFSchema,
104
+ F extends FormContextType = any,
105
+ >(props: WidgetProps<T, S, F>): UseAltDateWidgetResult {
106
+ const { time = false, disabled = false, readonly = false, options, onChange, value } = props;
107
+ const [state, setState] = useState(parseDateString(value, time));
108
+
109
+ useEffect(() => {
110
+ setState(parseDateString(value, time));
111
+ }, [time, value]);
112
+
113
+ const handleChange = useCallback(
114
+ (property: keyof DateObject, value?: string) => {
115
+ const nextState = {
116
+ ...state,
117
+ [property]: typeof value === 'undefined' ? -1 : value,
118
+ };
119
+
120
+ if (readyForChange(nextState)) {
121
+ onChange(toDateString(nextState, time));
122
+ } else {
123
+ setState(nextState);
124
+ }
125
+ },
126
+ [state, onChange, time],
127
+ );
128
+
129
+ const handleClear = useCallback(
130
+ (event: MouseEvent) => {
131
+ event.preventDefault();
132
+ if (disabled || readonly) {
133
+ return;
134
+ }
135
+ onChange(undefined);
136
+ },
137
+ [disabled, readonly, onChange],
138
+ );
139
+
140
+ const handleSetNow = useCallback(
141
+ (event: MouseEvent) => {
142
+ event.preventDefault();
143
+ if (disabled || readonly) {
144
+ return;
145
+ }
146
+ const nextState = parseDateString(new Date().toJSON(), time);
147
+ onChange(toDateString(nextState, time));
148
+ },
149
+ [disabled, readonly, time, onChange],
150
+ );
151
+
152
+ const elements = useMemo(
153
+ () =>
154
+ getDateElementProps(
155
+ state,
156
+ time,
157
+ options.yearsRange as [number, number] | undefined,
158
+ options.format as DateElementFormat | undefined,
159
+ ),
160
+ [state, time, options.yearsRange, options.format],
161
+ );
162
+ return { elements, handleChange, handleClear, handleSetNow };
163
+ }