@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/dist/index.cjs +199 -2
- package/dist/index.cjs.map +4 -4
- package/dist/utils.esm.js +197 -0
- package/dist/utils.esm.js.map +4 -4
- package/dist/utils.umd.js +190 -0
- package/lib/enums.d.ts +4 -0
- package/lib/enums.js +4 -0
- package/lib/enums.js.map +1 -1
- package/lib/getDateElementProps.d.ts +1 -2
- package/lib/index.d.ts +6 -4
- package/lib/index.js +3 -1
- package/lib/index.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/types.d.ts +20 -0
- package/lib/useAltDateWidgetProps.d.ts +39 -0
- package/lib/useAltDateWidgetProps.js +71 -0
- package/lib/useAltDateWidgetProps.js.map +1 -0
- package/lib/useFileWidgetProps.d.ts +29 -0
- package/lib/useFileWidgetProps.js +119 -0
- package/lib/useFileWidgetProps.js.map +1 -0
- package/package.json +1 -1
- package/src/enums.ts +4 -0
- package/src/getDateElementProps.ts +1 -1
- package/src/index.ts +17 -5
- package/src/types.ts +30 -0
- package/src/useAltDateWidgetProps.tsx +163 -0
- package/src/useFileWidgetProps.ts +155 -0
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":";AAAA,OAAO,EAAc,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE9E,OAAO,gBAAgB,MAAM,oBAAoB,CAAC;AAClD,OAAO,mBAA2D,MAAM,uBAAuB,CAAC;AAChG,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,eAAe,MAAM,mBAAmB,CAAC;AAChD,OAAO,YAAY,MAAM,gBAAgB,CAAC;AAG1C;;;;GAIG;AACH,SAAS,cAAc,CAAC,KAAiB;IACvC,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC;AAC7D,CAAC;AAiBD;;;;GAIG;AACH,MAAM,UAAU,WAAW,CACzB,KAAgC;IAEhC,MAAM,EACJ,SAAS,GAAG,cAAc,EAC1B,IAAI,EACJ,KAAK,EACL,KAAK,EACL,MAAM,EACN,MAAM,EACN,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,MAAM,EACN,OAAO,GACR,GAAG,KAAK,CAAC;IACV,MAAM,EAAE,GAAG,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC;IAC/B,MAAM,EAAE,YAAY,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC;IAC1C,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,KAAU,EAAE,EAAE,CAAC,MAAM,CAAC,IAAwB,EAAE,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;IACtG,OAAO,CACL,KAAC,YAAY,IACX,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAO,EAChC,EAAE,EAAE,EAAE,EACN,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,EAAE,WAAW,EAAE,gBAAgB,CAAI,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,EACjE,WAAW,EAAE,IAAI,EACjB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAC,EAAE,sBACU,kBAAkB,CAAC,MAAM,CAAC,GAC5C,CACH,CAAC;AACJ,CAAC;AAcD;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,qBAAqB,CAI3C,KAA2B;IAC3B,MAAM,EAAE,IAAI,GAAG,KAAK,EAAE,QAAQ,GAAG,KAAK,EAAE,QAAQ,GAAG,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAC7F,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;IAEjE,SAAS,CAAC,GAAG,EAAE;QACb,QAAQ,CAAC,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;IACzC,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;IAElB,MAAM,YAAY,GAAG,WAAW,CAC9B,CAAC,QAA0B,EAAE,KAAc,EAAE,EAAE;QAC7C,MAAM,SAAS,GAAG;YAChB,GAAG,KAAK;YACR,CAAC,QAAQ,CAAC,EAAE,OAAO,KAAK,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK;SACtD,CAAC;QAEF,IAAI,cAAc,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9B,QAAQ,CAAC,YAAY,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;QAC1C,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,SAAS,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,EACD,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,CACxB,CAAC;IAEF,MAAM,WAAW,GAAG,WAAW,CAC7B,CAAC,KAAiB,EAAE,EAAE;QACpB,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,IAAI,QAAQ,IAAI,QAAQ,EAAE,CAAC;YACzB,OAAO;QACT,CAAC;QACD,QAAQ,CAAC,SAAS,CAAC,CAAC;IACtB,CAAC,EACD,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAC/B,CAAC;IAEF,MAAM,YAAY,GAAG,WAAW,CAC9B,CAAC,KAAiB,EAAE,EAAE;QACpB,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,IAAI,QAAQ,IAAI,QAAQ,EAAE,CAAC;YACzB,OAAO;QACT,CAAC;QACD,MAAM,SAAS,GAAG,eAAe,CAAC,IAAI,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,CAAC;QAC7D,QAAQ,CAAC,YAAY,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;IAC1C,CAAC,EACD,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC,CACrC,CAAC;IAEF,MAAM,QAAQ,GAAG,OAAO,CACtB,GAAG,EAAE,CACH,mBAAmB,CACjB,KAAK,EACL,IAAI,EACJ,OAAO,CAAC,UAA0C,EAClD,OAAO,CAAC,MAAuC,CAChD,EACH,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,CAClD,CAAC;IACF,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC;AAC/D,CAAC"}
|
|
@@ -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
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, {
|
|
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
|
-
|
|
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
|
+
}
|