@springmicro/forms 0.6.4 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.cjs +22 -22
- package/README.md +11 -11
- package/dist/index.d.ts +0 -0
- package/dist/index.js +0 -0
- package/dist/index.umd.cjs +0 -0
- package/package.json +3 -3
- package/src/builder/bottom-drawer.tsx +429 -429
- package/src/builder/form-builder.tsx +256 -256
- package/src/builder/modal.tsx +39 -39
- package/src/builder/nodes/node-base.tsx +94 -94
- package/src/builder/nodes/node-child-helpers.tsx +273 -273
- package/src/builder/nodes/node-parent.tsx +187 -187
- package/src/builder/nodes/node-types/array-node.tsx +134 -134
- package/src/builder/nodes/node-types/date-node.tsx +60 -60
- package/src/builder/nodes/node-types/file-node.tsx +67 -67
- package/src/builder/nodes/node-types/integer-node.tsx +60 -60
- package/src/builder/nodes/node-types/object-node.tsx +67 -67
- package/src/builder/nodes/node-types/text-node.tsx +66 -66
- package/src/fields/ArrayField.tsx +875 -875
- package/src/fields/BooleanField.tsx +110 -110
- package/src/fields/MultiSchemaField.tsx +236 -236
- package/src/fields/NullField.tsx +22 -22
- package/src/fields/NumberField.tsx +87 -87
- package/src/fields/ObjectField.tsx +338 -338
- package/src/fields/SchemaField.tsx +402 -402
- package/src/fields/StringField.tsx +67 -67
- package/src/fields/index.ts +24 -24
- package/src/index.tsx +26 -26
- package/src/interfaces/MessagesProps.interface.ts +5 -5
- package/src/interfaces/Option.interface.ts +4 -4
- package/src/styles/select.styles.ts +28 -28
- package/src/templates/ArrayFieldDescriptionTemplate.tsx +42 -42
- package/src/templates/ArrayFieldItemTemplate.tsx +78 -78
- package/src/templates/ArrayFieldTemplate.tsx +90 -90
- package/src/templates/ArrayFieldTitleTemplate.tsx +44 -44
- package/src/templates/BaseInputTemplate.tsx +94 -94
- package/src/templates/ButtonTemplates/AddButton.tsx +29 -29
- package/src/templates/ButtonTemplates/IconButton.tsx +49 -49
- package/src/templates/ButtonTemplates/SubmitButton.tsx +29 -29
- package/src/templates/ButtonTemplates/index.ts +16 -16
- package/src/templates/DescriptionField.tsx +29 -29
- package/src/templates/ErrorList.tsx +25 -25
- package/src/templates/FieldTemplate/FieldTemplate.tsx +39 -39
- package/src/templates/FieldTemplate/Label.tsx +29 -29
- package/src/templates/FieldTemplate/WrapIfAdditional.tsx +85 -85
- package/src/templates/FieldTemplate/index.ts +3 -3
- package/src/templates/ObjectFieldTemplate.tsx +79 -79
- package/src/templates/TitleField.tsx +20 -20
- package/src/templates/UnsupportedField.tsx +29 -29
- package/src/templates/index.ts +32 -32
- package/src/types/Message.type.ts +6 -6
- package/src/types/RawMessage.type.ts +15 -15
- package/src/types/form-builder.ts +135 -135
- package/src/types/utils.type.ts +1 -1
- package/src/utils/form-builder.ts +424 -424
- package/src/utils/processSelectValue.ts +50 -50
- package/src/widgets/AltDateTimeWidget.tsx +17 -17
- package/src/widgets/AltDateWidget.tsx +216 -216
- package/src/widgets/CheckboxWidget.tsx +80 -80
- package/src/widgets/CheckboxesWidget.tsx +74 -74
- package/src/widgets/ColorWidget.tsx +26 -26
- package/src/widgets/DateTimeWidget.tsx +28 -28
- package/src/widgets/DateWidget.tsx +36 -36
- package/src/widgets/EmailWidget.tsx +19 -19
- package/src/widgets/FileWidget.tsx +144 -144
- package/src/widgets/HiddenWidget.tsx +22 -22
- package/src/widgets/PasswordWidget.tsx +20 -20
- package/src/widgets/RadioWidget.tsx +87 -87
- package/src/widgets/RangeWidget.tsx +24 -24
- package/src/widgets/SelectWidget.tsx +99 -99
- package/src/widgets/TextWidget.tsx +19 -19
- package/src/widgets/TextareaWidget.tsx +64 -64
- package/src/widgets/URLWidget.tsx +19 -19
- package/src/widgets/UpDownWidget.tsx +20 -20
- package/src/widgets/index.ts +43 -43
- package/tsconfig.json +24 -24
- package/tsconfig.node.json +10 -10
- package/vite.config.ts +25 -25
|
@@ -1,50 +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
|
-
}
|
|
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
|
+
}
|
|
@@ -1,17 +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;
|
|
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;
|
|
@@ -1,216 +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;
|
|
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;
|