@springmicro/forms 0.1.3

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 (63) hide show
  1. package/.eslintrc.cjs +18 -0
  2. package/README.md +11 -0
  3. package/dist/index.d.ts +27 -0
  4. package/dist/index.js +24760 -0
  5. package/dist/index.umd.cjs +105 -0
  6. package/package.json +46 -0
  7. package/src/fields/ArrayField.tsx +875 -0
  8. package/src/fields/BooleanField.tsx +110 -0
  9. package/src/fields/MultiSchemaField.tsx +236 -0
  10. package/src/fields/NullField.tsx +22 -0
  11. package/src/fields/NumberField.tsx +87 -0
  12. package/src/fields/ObjectField.tsx +338 -0
  13. package/src/fields/SchemaField.tsx +402 -0
  14. package/src/fields/StringField.tsx +67 -0
  15. package/src/fields/index.ts +24 -0
  16. package/src/index.tsx +17 -0
  17. package/src/interfaces/MessagesProps.interface.ts +5 -0
  18. package/src/interfaces/Option.interface.ts +4 -0
  19. package/src/styles/select.styles.ts +28 -0
  20. package/src/templates/ArrayFieldDescriptionTemplate.tsx +42 -0
  21. package/src/templates/ArrayFieldItemTemplate.tsx +78 -0
  22. package/src/templates/ArrayFieldTemplate.tsx +90 -0
  23. package/src/templates/ArrayFieldTitleTemplate.tsx +44 -0
  24. package/src/templates/BaseInputTemplate.tsx +94 -0
  25. package/src/templates/ButtonTemplates/AddButton.tsx +29 -0
  26. package/src/templates/ButtonTemplates/IconButton.tsx +49 -0
  27. package/src/templates/ButtonTemplates/SubmitButton.tsx +29 -0
  28. package/src/templates/ButtonTemplates/index.ts +16 -0
  29. package/src/templates/DescriptionField.tsx +29 -0
  30. package/src/templates/ErrorList.tsx +25 -0
  31. package/src/templates/FieldTemplate/FieldTemplate.tsx +39 -0
  32. package/src/templates/FieldTemplate/Label.tsx +29 -0
  33. package/src/templates/FieldTemplate/WrapIfAdditional.tsx +85 -0
  34. package/src/templates/FieldTemplate/index.ts +3 -0
  35. package/src/templates/ObjectFieldTemplate.tsx +79 -0
  36. package/src/templates/TitleField.tsx +20 -0
  37. package/src/templates/UnsupportedField.tsx +29 -0
  38. package/src/templates/index.ts +32 -0
  39. package/src/types/Message.type.ts +6 -0
  40. package/src/types/RawMessage.type.ts +15 -0
  41. package/src/utils/processSelectValue.ts +50 -0
  42. package/src/widgets/AltDateTimeWidget.tsx +17 -0
  43. package/src/widgets/AltDateWidget.tsx +216 -0
  44. package/src/widgets/CheckboxWidget.tsx +80 -0
  45. package/src/widgets/CheckboxesWidget.tsx +74 -0
  46. package/src/widgets/ColorWidget.tsx +26 -0
  47. package/src/widgets/DateTimeWidget.tsx +28 -0
  48. package/src/widgets/DateWidget.tsx +36 -0
  49. package/src/widgets/EmailWidget.tsx +19 -0
  50. package/src/widgets/FileWidget.tsx +144 -0
  51. package/src/widgets/HiddenWidget.tsx +22 -0
  52. package/src/widgets/PasswordWidget.tsx +20 -0
  53. package/src/widgets/RadioWidget.tsx +87 -0
  54. package/src/widgets/RangeWidget.tsx +24 -0
  55. package/src/widgets/SelectWidget.tsx +99 -0
  56. package/src/widgets/TextWidget.tsx +19 -0
  57. package/src/widgets/TextareaWidget.tsx +64 -0
  58. package/src/widgets/URLWidget.tsx +19 -0
  59. package/src/widgets/UpDownWidget.tsx +20 -0
  60. package/src/widgets/index.ts +43 -0
  61. package/tsconfig.json +24 -0
  62. package/tsconfig.node.json +10 -0
  63. package/vite.config.ts +25 -0
@@ -0,0 +1,29 @@
1
+ import React from "react";
2
+ import { GenericObjectType, UnsupportedFieldProps } from "@rjsf/utils";
3
+
4
+ /** The `UnsupportedField` component is used to render a field in the schema is one that is not supported by
5
+ * react-jsonschema-form.
6
+ *
7
+ * @param props - The `FieldProps` for this template
8
+ */
9
+ function UnsupportedField<T = any, F extends GenericObjectType = any>(
10
+ props: UnsupportedFieldProps<T, F>
11
+ ) {
12
+ const { schema, idSchema, reason } = props;
13
+ return (
14
+ <div className="unsupported-field">
15
+ <p>
16
+ Unsupported field schema
17
+ {idSchema && idSchema.$id && (
18
+ <span>
19
+ {" for"} field <code>{idSchema.$id}</code>
20
+ </span>
21
+ )}
22
+ {reason && <em>: {reason}</em>}.
23
+ </p>
24
+ {schema && <pre>{JSON.stringify(schema, null, 2)}</pre>}
25
+ </div>
26
+ );
27
+ }
28
+
29
+ export default UnsupportedField;
@@ -0,0 +1,32 @@
1
+ import { TemplatesType } from "@rjsf/utils";
2
+
3
+ import ArrayFieldDescriptionTemplate from "./ArrayFieldDescriptionTemplate";
4
+ import ArrayFieldItemTemplate from "./ArrayFieldItemTemplate";
5
+ import ArrayFieldTemplate from "./ArrayFieldTemplate";
6
+ import ArrayFieldTitleTemplate from "./ArrayFieldTitleTemplate";
7
+ import BaseInputTemplate from "./BaseInputTemplate";
8
+ import ButtonTemplates from "./ButtonTemplates";
9
+ import DescriptionField from "./DescriptionField";
10
+ import ErrorList from "./ErrorList";
11
+ import FieldTemplate from "./FieldTemplate";
12
+ import ObjectFieldTemplate from "./ObjectFieldTemplate";
13
+ import TitleField from "./TitleField";
14
+ import UnsupportedField from "./UnsupportedField";
15
+
16
+ // @ts-ignore
17
+ const templates: TemplatesType = {
18
+ ArrayFieldDescriptionTemplate,
19
+ ArrayFieldItemTemplate,
20
+ ArrayFieldTemplate,
21
+ ArrayFieldTitleTemplate,
22
+ ButtonTemplates,
23
+ BaseInputTemplate,
24
+ DescriptionFieldTemplate: DescriptionField,
25
+ ErrorListTemplate: ErrorList,
26
+ FieldTemplate,
27
+ ObjectFieldTemplate,
28
+ TitleFieldTemplate: TitleField,
29
+ UnsupportedFieldTemplate: UnsupportedField,
30
+ };
31
+
32
+ export default templates;
@@ -0,0 +1,6 @@
1
+ export type Message = {
2
+ actions: string;
3
+ name: string;
4
+ body: string;
5
+ date_created: Date;
6
+ };
@@ -0,0 +1,15 @@
1
+ export type RawMessage = {
2
+ sid: string;
3
+ date_created: string;
4
+ date_updated: string;
5
+ date_sent?: string;
6
+ account_sid: string;
7
+ to: string;
8
+ from: string;
9
+ body: string;
10
+ status: string;
11
+ flags: string[];
12
+ api_version: string;
13
+ price?: string;
14
+ uri: string;
15
+ };
@@ -0,0 +1,50 @@
1
+ import type { GenericObjectType, RJSFSchema, UIOptionsType } from "@rjsf/utils";
2
+ import { asNumber, guessType } from "@rjsf/utils";
3
+ import get from "lodash/get";
4
+
5
+ const nums = new Set<any>(["number", "integer"]);
6
+
7
+ /** DEPRECATED in V5, copied from V4.
8
+ * https://raw.githubusercontent.com/rjsf-team/react-jsonschema-form/97bda160aeca8684770da6f3fed5b01b3828a1fa/packages/utils/src/processSelectValue.ts
9
+ * Returns the real value for a select widget due to a silly limitation in the DOM which causes option change event
10
+ * values to always be retrieved as strings. Uses the `schema` to help determine the value's true type. If the value is
11
+ * an empty string, then the `emptyValue` from the `options` is returned, falling back to undefined.
12
+ *
13
+ * @param schema - The schema to used to determine the value's true type
14
+ * @param [value] - The value to convert
15
+ * @param [options] - The UIOptionsType from which to potentially extract the emptyValue
16
+ * @returns - The `value` converted to the proper type
17
+ */
18
+ export default function processSelectValue<
19
+ T = any,
20
+ F extends GenericObjectType = any
21
+ >(schema: RJSFSchema, value?: any, options?: UIOptionsType<T, F>) {
22
+ const { enum: schemaEnum, type, items } = schema;
23
+ if (value === "") {
24
+ return options && options.emptyValue !== undefined
25
+ ? options.emptyValue
26
+ : undefined;
27
+ }
28
+ if (type === "array" && items && nums.has(get(items, "type"))) {
29
+ return value.map(asNumber);
30
+ }
31
+ if (type === "boolean") {
32
+ return value === "true";
33
+ }
34
+ if (nums.has(type)) {
35
+ return asNumber(value);
36
+ }
37
+
38
+ // If type is undefined, but an enum is present, try and infer the type from
39
+ // the enum values
40
+ if (Array.isArray(schemaEnum)) {
41
+ if (schemaEnum.every((x: any) => nums.has(guessType(x)))) {
42
+ return asNumber(value);
43
+ }
44
+ if (schemaEnum.every((x: any) => guessType(x) === "boolean")) {
45
+ return value === "true";
46
+ }
47
+ }
48
+
49
+ return value;
50
+ }
@@ -0,0 +1,17 @@
1
+ import { WidgetProps, GenericObjectType } from "@rjsf/utils";
2
+ import React from "react";
3
+
4
+ /** The `AltDateTimeWidget` is an alternative widget for rendering datetime properties.
5
+ * It uses the AltDateWidget for rendering, with the `time` prop set to true by default.
6
+ *
7
+ * @param props - The `WidgetProps` for this component
8
+ */
9
+ function AltDateTimeWidget<T = any, F extends GenericObjectType = any>({
10
+ time = true,
11
+ ...props
12
+ }: WidgetProps<T, F>) {
13
+ const { AltDateWidget } = props.registry.widgets;
14
+ return <AltDateWidget time={time} {...props} />;
15
+ }
16
+
17
+ export default AltDateTimeWidget;
@@ -0,0 +1,216 @@
1
+ import React, { MouseEvent, useCallback, useEffect, useReducer } from "react";
2
+ import { parseDateString, toDateString, pad } from "@rjsf/utils";
3
+ import type { WidgetProps, DateObject, GenericObjectType } from "@rjsf/utils";
4
+
5
+ function rangeOptions(start: number, stop: number) {
6
+ const options = [];
7
+ for (let i = start; i <= stop; i++) {
8
+ options.push({ value: i, label: pad(i, 2) });
9
+ }
10
+ return options;
11
+ }
12
+
13
+ function readyForChange(state: DateObject) {
14
+ return Object.values(state).every((value) => value !== -1);
15
+ }
16
+
17
+ function dateElementProps(
18
+ state: DateObject,
19
+ time: boolean,
20
+ yearsRange: [number, number] = [1900, new Date().getFullYear() + 2]
21
+ ) {
22
+ const { year, month, day, hour, minute, second } = state;
23
+ const data = [
24
+ {
25
+ type: "year",
26
+ range: yearsRange,
27
+ value: year,
28
+ },
29
+ { type: "month", range: [1, 12], value: month },
30
+ { type: "day", range: [1, 31], value: day },
31
+ ] as { type: string; range: [number, number]; value: number | undefined }[];
32
+ if (time) {
33
+ data.push(
34
+ { type: "hour", range: [0, 23], value: hour },
35
+ { type: "minute", range: [0, 59], value: minute },
36
+ { type: "second", range: [0, 59], value: second }
37
+ );
38
+ }
39
+ return data;
40
+ }
41
+
42
+ type DateElementProps<T, F extends GenericObjectType = any> = Pick<
43
+ WidgetProps<T, F>,
44
+ | "value"
45
+ | "disabled"
46
+ | "readonly"
47
+ | "autofocus"
48
+ | "registry"
49
+ | "onBlur"
50
+ | "onFocus"
51
+ > & {
52
+ name: string;
53
+ rootId: string;
54
+ select: (property: keyof DateObject, value: any) => void;
55
+ type: string;
56
+ range: [number, number];
57
+ };
58
+
59
+ function DateElement<T, F extends GenericObjectType = any>({
60
+ type,
61
+ range,
62
+ name,
63
+ value,
64
+ select,
65
+ rootId,
66
+ disabled,
67
+ readonly,
68
+ autofocus,
69
+ registry,
70
+ onBlur,
71
+ onFocus,
72
+ }: DateElementProps<T, F>) {
73
+ const id = rootId + "_" + type;
74
+ const { SelectWidget } = registry.widgets;
75
+ return (
76
+ <SelectWidget
77
+ schema={{ type: "integer" } as unknown as F}
78
+ name={name}
79
+ id={id}
80
+ className="form-control"
81
+ options={{ enumOptions: rangeOptions(range[0], range[1]) }}
82
+ placeholder={type}
83
+ value={value}
84
+ disabled={disabled}
85
+ readonly={readonly}
86
+ autofocus={autofocus}
87
+ onChange={(value: any) => select(type as keyof DateObject, value)}
88
+ onBlur={onBlur}
89
+ onFocus={onFocus}
90
+ registry={registry}
91
+ label=""
92
+ />
93
+ );
94
+ }
95
+
96
+ /** The `AltDateWidget` is an alternative widget for rendering date properties.
97
+ * @param props - The `WidgetProps` for this component
98
+ */
99
+ function AltDateWidget<T = any, F extends GenericObjectType = any>({
100
+ time = false,
101
+ disabled = false,
102
+ readonly = false,
103
+ autofocus = false,
104
+ options,
105
+ id,
106
+ name,
107
+ registry,
108
+ onBlur,
109
+ onFocus,
110
+ onChange,
111
+ value,
112
+ }: WidgetProps<T, F>) {
113
+ const [state, setState] = useReducer(
114
+ (state: DateObject, action: Partial<DateObject>) => {
115
+ return { ...state, ...action };
116
+ },
117
+ parseDateString(value, time)
118
+ );
119
+
120
+ useEffect(() => {
121
+ if (value && value !== toDateString(state, time)) {
122
+ setState(parseDateString(value, time));
123
+ }
124
+ }, [value, state, time]);
125
+
126
+ useEffect(() => {
127
+ if (readyForChange(state)) {
128
+ // Only propagate to parent state if we have a complete date{time}
129
+ onChange(toDateString(state, time));
130
+ }
131
+ }, [state, time, onChange]);
132
+
133
+ const handleChange = useCallback(
134
+ (property: keyof DateObject, value: string) => {
135
+ setState({ [property]: value });
136
+ },
137
+ []
138
+ );
139
+
140
+ const handleSetNow = useCallback(
141
+ (event: MouseEvent<HTMLAnchorElement>) => {
142
+ event.preventDefault();
143
+ if (disabled || readonly) {
144
+ return;
145
+ }
146
+ const nowDateObj = parseDateString(new Date().toJSON(), time);
147
+ setState(nowDateObj);
148
+ },
149
+ [disabled, readonly, time]
150
+ );
151
+
152
+ const handleClear = useCallback(
153
+ (event: MouseEvent<HTMLAnchorElement>) => {
154
+ event.preventDefault();
155
+ if (disabled || readonly) {
156
+ return;
157
+ }
158
+ setState(parseDateString("", time));
159
+ onChange(undefined);
160
+ },
161
+ [disabled, readonly, time, onChange]
162
+ );
163
+
164
+ return (
165
+ <ul className="list-inline">
166
+ {dateElementProps(
167
+ state,
168
+ time,
169
+ options.yearsRange as [number, number] | undefined
170
+ ).map((elemProps, i) => (
171
+ <li key={i}>
172
+ <DateElement
173
+ name={name}
174
+ rootId={id}
175
+ select={handleChange}
176
+ {...elemProps}
177
+ disabled={disabled}
178
+ readonly={readonly}
179
+ registry={registry}
180
+ onBlur={onBlur}
181
+ onFocus={onFocus}
182
+ autofocus={autofocus && i === 0}
183
+ />
184
+ </li>
185
+ ))}
186
+ {(options.hideNowButton !== "undefined"
187
+ ? !options.hideNowButton
188
+ : true) && (
189
+ <li>
190
+ <a
191
+ href="#"
192
+ className="btn btn-primary btn-now"
193
+ onClick={handleSetNow}
194
+ >
195
+ Now
196
+ </a>
197
+ </li>
198
+ )}
199
+ {(options.hideClearButton !== "undefined"
200
+ ? !options.hideClearButton
201
+ : true) && (
202
+ <li>
203
+ <a
204
+ href="#"
205
+ className="btn btn-warning btn-clear"
206
+ onClick={handleClear}
207
+ >
208
+ Clear
209
+ </a>
210
+ </li>
211
+ )}
212
+ </ul>
213
+ );
214
+ }
215
+
216
+ export default AltDateWidget;
@@ -0,0 +1,80 @@
1
+ import React, { useCallback } from "react";
2
+ import type { GenericObjectType, WidgetProps } from "@rjsf/utils";
3
+ import { getTemplate, schemaRequiresTrueValue } from "@rjsf/utils";
4
+
5
+ /** The `CheckBoxWidget` is a widget for rendering boolean properties.
6
+ * It is typically used to represent a boolean.
7
+ *
8
+ * @param props - The `WidgetProps` for this component
9
+ */
10
+ function CheckboxWidget<T = any, F extends GenericObjectType = any>({
11
+ schema,
12
+ options,
13
+ id,
14
+ value,
15
+ disabled,
16
+ readonly,
17
+ label,
18
+ autofocus = false,
19
+ onBlur,
20
+ onFocus,
21
+ onChange,
22
+ registry,
23
+ }: WidgetProps<T, F>) {
24
+ const DescriptionFieldTemplate = getTemplate<
25
+ "DescriptionFieldTemplate",
26
+ T,
27
+ F
28
+ >("DescriptionFieldTemplate", registry, options);
29
+ // Because an unchecked checkbox will cause html5 validation to fail, only add
30
+ // the "required" attribute if the field value must be "true", due to the
31
+ // "const" or "enum" keywords
32
+ const required = schemaRequiresTrueValue(schema);
33
+
34
+ const handleChange = useCallback(
35
+ (event: React.ChangeEvent<HTMLInputElement>) =>
36
+ onChange(event.target.checked),
37
+ [onChange]
38
+ );
39
+
40
+ const handleBlur = useCallback(
41
+ (event: React.FocusEvent<HTMLInputElement>) =>
42
+ onBlur(id, event.target.checked),
43
+ [onBlur, id]
44
+ );
45
+
46
+ const handleFocus = useCallback(
47
+ (event: React.FocusEvent<HTMLInputElement>) =>
48
+ onFocus(id, event.target.checked),
49
+ [onFocus, id]
50
+ );
51
+
52
+ return (
53
+ <div className={`checkbox ${disabled || readonly ? "disabled" : ""}`}>
54
+ {schema.description && (
55
+ <DescriptionFieldTemplate
56
+ schema={schema}
57
+ id={id + "__description"}
58
+ description={schema.description}
59
+ registry={registry}
60
+ />
61
+ )}
62
+ <label>
63
+ <input
64
+ type="checkbox"
65
+ id={id}
66
+ checked={typeof value === "undefined" ? false : value}
67
+ required={required}
68
+ disabled={disabled || readonly}
69
+ autoFocus={autofocus}
70
+ onChange={handleChange}
71
+ onBlur={handleBlur}
72
+ onFocus={handleFocus}
73
+ />
74
+ <span>{label}</span>
75
+ </label>
76
+ </div>
77
+ );
78
+ }
79
+
80
+ export default CheckboxWidget;
@@ -0,0 +1,74 @@
1
+ import React, { ChangeEvent } from "react";
2
+ import { WidgetProps, GenericObjectType } from "@rjsf/utils";
3
+
4
+ function selectValue(value: any, selected: any[], all: any[]) {
5
+ const at = all.indexOf(value);
6
+ const updated = selected.slice(0, at).concat(value, selected.slice(at));
7
+ // As inserting values at predefined index positions doesn't work with empty
8
+ // arrays, we need to reorder the updated selection to match the initial order
9
+ return updated.sort((a, b) => Number(all.indexOf(a) > all.indexOf(b)));
10
+ }
11
+
12
+ function deselectValue(value: any, selected: any[]) {
13
+ return selected.filter((v) => v !== value);
14
+ }
15
+
16
+ /** The `CheckboxesWidget` is a widget for rendering checkbox groups.
17
+ * It is typically used to represent an array of enums.
18
+ *
19
+ * @param props - The `WidgetProps` for this component
20
+ */
21
+ function CheckboxesWidget<T = any, F extends GenericObjectType = any>({
22
+ id,
23
+ disabled,
24
+ options: { inline = false, enumOptions, enumDisabled },
25
+ value,
26
+ autofocus = false,
27
+ readonly,
28
+ onChange,
29
+ }: WidgetProps<T, F>) {
30
+ return (
31
+ <div
32
+ className={`flex checkboxes ${inline ? "flex-row gap-2" : "flex-col"}`}
33
+ id={id}
34
+ >
35
+ {Array.isArray(enumOptions) &&
36
+ enumOptions.map((option, index) => {
37
+ const checked = value.indexOf(option.value) !== -1;
38
+ const itemDisabled =
39
+ enumDisabled && enumDisabled.indexOf(option.value) != -1;
40
+ const disabledCls =
41
+ disabled || itemDisabled || readonly ? "disabled" : "";
42
+
43
+ const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
44
+ const all = enumOptions.map(({ value }) => value);
45
+ if (event.target.checked) {
46
+ onChange(selectValue(option.value, value, all));
47
+ } else {
48
+ onChange(deselectValue(option.value, value));
49
+ }
50
+ };
51
+
52
+ return (
53
+ <div key={`${id}_${index}_container`} className="form-control">
54
+ <label className="justify-start gap-2 cursor-pointer label">
55
+ <input
56
+ key={`${id}_${index}`}
57
+ className="checkbox checkbox-primary"
58
+ type="checkbox"
59
+ id={`${id}_${index}`}
60
+ checked={checked}
61
+ disabled={disabled || itemDisabled || readonly}
62
+ autoFocus={autofocus && index === 0}
63
+ onChange={handleChange}
64
+ />
65
+ <span className="label-text">{option.label}</span>
66
+ </label>
67
+ </div>
68
+ );
69
+ })}
70
+ </div>
71
+ );
72
+ }
73
+
74
+ export default CheckboxesWidget;
@@ -0,0 +1,26 @@
1
+ import React from "react";
2
+ import type { WidgetProps, GenericObjectType } from "@rjsf/utils";
3
+ import { getTemplate } from "@rjsf/utils";
4
+
5
+ /** The `ColorWidget` component uses the `BaseInputTemplate` changing the type to `color` and disables it when it is
6
+ * either disabled or readonly.
7
+ *
8
+ * @param props - The `WidgetProps` for this component
9
+ */
10
+ export default function ColorWidget<T = any, F extends GenericObjectType = any>(
11
+ props: WidgetProps<T, F>
12
+ ) {
13
+ const { disabled, readonly, options, registry } = props;
14
+ const BaseInputTemplate = getTemplate<"BaseInputTemplate", T, F>(
15
+ "BaseInputTemplate",
16
+ registry,
17
+ options
18
+ );
19
+ return (
20
+ <BaseInputTemplate
21
+ type="color"
22
+ {...props}
23
+ disabled={disabled || readonly}
24
+ />
25
+ );
26
+ }
@@ -0,0 +1,28 @@
1
+ import React from "react";
2
+ import type { WidgetProps, GenericObjectType } from "@rjsf/utils";
3
+ import { getTemplate, localToUTC, utcToLocal } from "@rjsf/utils";
4
+
5
+ /** The `DateTimeWidget` component uses the `BaseInputTemplate` changing the type to `datetime-local` and transforms
6
+ * the value to/from utc using the appropriate utility functions.
7
+ *
8
+ * @param props - The `WidgetProps` for this component
9
+ */
10
+ export default function DateTimeWidget<
11
+ T = any,
12
+ F extends GenericObjectType = any
13
+ >(props: WidgetProps<T, F>) {
14
+ const { onChange, value, options, registry } = props;
15
+ const BaseInputTemplate = getTemplate<"BaseInputTemplate", T, F>(
16
+ "BaseInputTemplate",
17
+ registry,
18
+ options
19
+ );
20
+ return (
21
+ <BaseInputTemplate
22
+ type="datetime-local"
23
+ {...props}
24
+ value={utcToLocal(value)}
25
+ onChange={(value) => onChange(localToUTC(value))}
26
+ />
27
+ );
28
+ }
@@ -0,0 +1,36 @@
1
+ import React, { useCallback, useEffect } from "react";
2
+ import type { WidgetProps, GenericObjectType } from "@rjsf/utils";
3
+
4
+ // @ts-ignore
5
+ import DatePicker from "react-datepicker";
6
+
7
+ /** The `DateWidget` component uses the `BaseInputTemplate` changing the type to `date` and transforms
8
+ * the value to undefined when it is falsy during the `onChange` handling.
9
+ *
10
+ * @param props - The `WidgetProps` for this component
11
+ */
12
+ export default function DateWidget<T = any, F extends GenericObjectType = any>(
13
+ props: WidgetProps<T, F>
14
+ ) {
15
+ const { onChange } = props;
16
+
17
+ const handleChange = useCallback(
18
+ (selectedDate: Date) => {
19
+ onChange(selectedDate?.toISOString().split("T")[0] || undefined);
20
+ },
21
+ [onChange]
22
+ );
23
+
24
+ const getValue = () => {
25
+ const offset = new Date().getTimezoneOffset();
26
+ return props.value ? new Date(`${props.value}T19:00:00.${offset}Z`) : null;
27
+ };
28
+
29
+ return (
30
+ <DatePicker
31
+ selected={getValue()}
32
+ className="w-full input input-bordered"
33
+ onChange={handleChange}
34
+ />
35
+ );
36
+ }
@@ -0,0 +1,19 @@
1
+ import React from "react";
2
+ import type { WidgetProps, GenericObjectType } from "@rjsf/utils";
3
+ import { getTemplate } from "@rjsf/utils";
4
+
5
+ /** The `EmailWidget` component uses the `BaseInputTemplate` changing the type to `email`.
6
+ *
7
+ * @param props - The `WidgetProps` for this component
8
+ */
9
+ export default function EmailWidget<T = any, F extends GenericObjectType = any>(
10
+ props: WidgetProps<T, F>
11
+ ) {
12
+ const { options, registry } = props;
13
+ const BaseInputTemplate = getTemplate<"BaseInputTemplate", T, F>(
14
+ "BaseInputTemplate",
15
+ registry,
16
+ options
17
+ );
18
+ return <BaseInputTemplate type="email" {...props} />;
19
+ }