@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.
- package/.eslintrc.cjs +18 -0
- package/README.md +11 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +24760 -0
- package/dist/index.umd.cjs +105 -0
- package/package.json +46 -0
- package/src/fields/ArrayField.tsx +875 -0
- package/src/fields/BooleanField.tsx +110 -0
- package/src/fields/MultiSchemaField.tsx +236 -0
- package/src/fields/NullField.tsx +22 -0
- package/src/fields/NumberField.tsx +87 -0
- package/src/fields/ObjectField.tsx +338 -0
- package/src/fields/SchemaField.tsx +402 -0
- package/src/fields/StringField.tsx +67 -0
- package/src/fields/index.ts +24 -0
- package/src/index.tsx +17 -0
- package/src/interfaces/MessagesProps.interface.ts +5 -0
- package/src/interfaces/Option.interface.ts +4 -0
- package/src/styles/select.styles.ts +28 -0
- package/src/templates/ArrayFieldDescriptionTemplate.tsx +42 -0
- package/src/templates/ArrayFieldItemTemplate.tsx +78 -0
- package/src/templates/ArrayFieldTemplate.tsx +90 -0
- package/src/templates/ArrayFieldTitleTemplate.tsx +44 -0
- package/src/templates/BaseInputTemplate.tsx +94 -0
- package/src/templates/ButtonTemplates/AddButton.tsx +29 -0
- package/src/templates/ButtonTemplates/IconButton.tsx +49 -0
- package/src/templates/ButtonTemplates/SubmitButton.tsx +29 -0
- package/src/templates/ButtonTemplates/index.ts +16 -0
- package/src/templates/DescriptionField.tsx +29 -0
- package/src/templates/ErrorList.tsx +25 -0
- package/src/templates/FieldTemplate/FieldTemplate.tsx +39 -0
- package/src/templates/FieldTemplate/Label.tsx +29 -0
- package/src/templates/FieldTemplate/WrapIfAdditional.tsx +85 -0
- package/src/templates/FieldTemplate/index.ts +3 -0
- package/src/templates/ObjectFieldTemplate.tsx +79 -0
- package/src/templates/TitleField.tsx +20 -0
- package/src/templates/UnsupportedField.tsx +29 -0
- package/src/templates/index.ts +32 -0
- package/src/types/Message.type.ts +6 -0
- package/src/types/RawMessage.type.ts +15 -0
- package/src/utils/processSelectValue.ts +50 -0
- package/src/widgets/AltDateTimeWidget.tsx +17 -0
- package/src/widgets/AltDateWidget.tsx +216 -0
- package/src/widgets/CheckboxWidget.tsx +80 -0
- package/src/widgets/CheckboxesWidget.tsx +74 -0
- package/src/widgets/ColorWidget.tsx +26 -0
- package/src/widgets/DateTimeWidget.tsx +28 -0
- package/src/widgets/DateWidget.tsx +36 -0
- package/src/widgets/EmailWidget.tsx +19 -0
- package/src/widgets/FileWidget.tsx +144 -0
- package/src/widgets/HiddenWidget.tsx +22 -0
- package/src/widgets/PasswordWidget.tsx +20 -0
- package/src/widgets/RadioWidget.tsx +87 -0
- package/src/widgets/RangeWidget.tsx +24 -0
- package/src/widgets/SelectWidget.tsx +99 -0
- package/src/widgets/TextWidget.tsx +19 -0
- package/src/widgets/TextareaWidget.tsx +64 -0
- package/src/widgets/URLWidget.tsx +19 -0
- package/src/widgets/UpDownWidget.tsx +20 -0
- package/src/widgets/index.ts +43 -0
- package/tsconfig.json +24 -0
- package/tsconfig.node.json +10 -0
- package/vite.config.ts +25 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import React, { ChangeEvent, useCallback, useMemo, useState } from "react";
|
|
2
|
+
|
|
3
|
+
import type { WidgetProps, GenericObjectType } from "@rjsf/utils";
|
|
4
|
+
import { dataURItoBlob } from "@rjsf/utils";
|
|
5
|
+
|
|
6
|
+
function addNameToDataURL(dataURL: string, name: string) {
|
|
7
|
+
if (dataURL === null) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
return dataURL.replace(";base64", `;name=${encodeURIComponent(name)};base64`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type FileInfoType = {
|
|
14
|
+
dataURL?: string | null;
|
|
15
|
+
name: string;
|
|
16
|
+
size: number;
|
|
17
|
+
type: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function processFile(file: File): Promise<FileInfoType> {
|
|
21
|
+
const { name, size, type } = file;
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
const reader = new window.FileReader();
|
|
24
|
+
reader.onerror = reject;
|
|
25
|
+
reader.onload = (event) => {
|
|
26
|
+
if (typeof event.target?.result === "string") {
|
|
27
|
+
resolve({
|
|
28
|
+
dataURL: addNameToDataURL(event.target.result, name),
|
|
29
|
+
name,
|
|
30
|
+
size,
|
|
31
|
+
type,
|
|
32
|
+
});
|
|
33
|
+
} else {
|
|
34
|
+
resolve({
|
|
35
|
+
dataURL: null,
|
|
36
|
+
name,
|
|
37
|
+
size,
|
|
38
|
+
type,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
reader.readAsDataURL(file);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function processFiles(files: FileList) {
|
|
47
|
+
return Promise.all(Array.from(files).map(processFile));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function FilesInfo({
|
|
51
|
+
filesInfo,
|
|
52
|
+
}: {
|
|
53
|
+
filesInfo: { name: string; size: number; type: string }[];
|
|
54
|
+
}) {
|
|
55
|
+
if (filesInfo.length === 0) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return (
|
|
59
|
+
<ul className="file-info">
|
|
60
|
+
{filesInfo.map((fileInfo, key) => {
|
|
61
|
+
const { name, size, type } = fileInfo;
|
|
62
|
+
return (
|
|
63
|
+
<li key={key}>
|
|
64
|
+
<strong>{name}</strong> ({type}, {size} bytes)
|
|
65
|
+
</li>
|
|
66
|
+
);
|
|
67
|
+
})}
|
|
68
|
+
</ul>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function extractFileInfo(dataURLs: string[]) {
|
|
73
|
+
return dataURLs
|
|
74
|
+
.filter((dataURL) => typeof dataURL !== "undefined")
|
|
75
|
+
.map((dataURL) => {
|
|
76
|
+
const { blob, name } = dataURItoBlob(dataURL);
|
|
77
|
+
return {
|
|
78
|
+
name: name,
|
|
79
|
+
size: blob.size,
|
|
80
|
+
type: blob.type,
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The `FileWidget` is a widget for rendering file upload fields.
|
|
87
|
+
* It is typically used with a string property with data-url format.
|
|
88
|
+
*/
|
|
89
|
+
function FileWidget<T, F extends GenericObjectType = any>({
|
|
90
|
+
multiple,
|
|
91
|
+
id,
|
|
92
|
+
readonly,
|
|
93
|
+
disabled,
|
|
94
|
+
onChange,
|
|
95
|
+
value,
|
|
96
|
+
autofocus = false,
|
|
97
|
+
options,
|
|
98
|
+
}: WidgetProps<T, F>) {
|
|
99
|
+
const extractedFilesInfo = useMemo(
|
|
100
|
+
() =>
|
|
101
|
+
Array.isArray(value) ? extractFileInfo(value) : extractFileInfo([value]),
|
|
102
|
+
[value]
|
|
103
|
+
);
|
|
104
|
+
const [filesInfo, setFilesInfo] =
|
|
105
|
+
useState<FileInfoType[]>(extractedFilesInfo);
|
|
106
|
+
|
|
107
|
+
const handleChange = useCallback(
|
|
108
|
+
(event: ChangeEvent<HTMLInputElement>) => {
|
|
109
|
+
if (!event.target.files) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
processFiles(event.target.files).then((filesInfoEvent) => {
|
|
113
|
+
setFilesInfo(filesInfoEvent);
|
|
114
|
+
const newValue = filesInfoEvent.map((fileInfo) => fileInfo.dataURL);
|
|
115
|
+
if (multiple) {
|
|
116
|
+
onChange(newValue);
|
|
117
|
+
} else {
|
|
118
|
+
onChange(newValue[0]);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
},
|
|
122
|
+
[multiple, onChange]
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<div>
|
|
127
|
+
<p>
|
|
128
|
+
<input
|
|
129
|
+
id={id}
|
|
130
|
+
type="file"
|
|
131
|
+
disabled={readonly || disabled}
|
|
132
|
+
onChange={handleChange}
|
|
133
|
+
defaultValue=""
|
|
134
|
+
autoFocus={autofocus}
|
|
135
|
+
multiple={multiple}
|
|
136
|
+
accept={options.accept ? String(options.accept) : undefined}
|
|
137
|
+
/>
|
|
138
|
+
</p>
|
|
139
|
+
<FilesInfo filesInfo={filesInfo} />
|
|
140
|
+
</div>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export default FileWidget;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { WidgetProps, GenericObjectType } from "@rjsf/utils";
|
|
3
|
+
|
|
4
|
+
/** The `HiddenWidget` is a widget for rendering a hidden input field.
|
|
5
|
+
* It is typically used by setting type to "hidden".
|
|
6
|
+
*
|
|
7
|
+
* @param props - The `WidgetProps` for this component
|
|
8
|
+
*/
|
|
9
|
+
function HiddenWidget<T = any, F extends GenericObjectType = any>({
|
|
10
|
+
id,
|
|
11
|
+
value,
|
|
12
|
+
}: WidgetProps<T, F>) {
|
|
13
|
+
return (
|
|
14
|
+
<input
|
|
15
|
+
type="hidden"
|
|
16
|
+
id={id}
|
|
17
|
+
value={typeof value === "undefined" ? "" : value}
|
|
18
|
+
/>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default HiddenWidget;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { WidgetProps, GenericObjectType } from "@rjsf/utils";
|
|
3
|
+
import { getTemplate } from "@rjsf/utils";
|
|
4
|
+
|
|
5
|
+
/** The `PasswordWidget` component uses the `BaseInputTemplate` changing the type to `password`.
|
|
6
|
+
*
|
|
7
|
+
* @param props - The `WidgetProps` for this component
|
|
8
|
+
*/
|
|
9
|
+
export default function PasswordWidget<
|
|
10
|
+
T = any,
|
|
11
|
+
F extends GenericObjectType = any
|
|
12
|
+
>(props: WidgetProps<T, F>) {
|
|
13
|
+
const { options, registry } = props;
|
|
14
|
+
const BaseInputTemplate = getTemplate<"BaseInputTemplate", T, F>(
|
|
15
|
+
"BaseInputTemplate",
|
|
16
|
+
registry,
|
|
17
|
+
options
|
|
18
|
+
);
|
|
19
|
+
return <BaseInputTemplate type="password" {...props} />;
|
|
20
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import React, { FocusEvent, useCallback } from "react";
|
|
2
|
+
import type { WidgetProps, GenericObjectType } from "@rjsf/utils";
|
|
3
|
+
|
|
4
|
+
/** The `RadioWidget` is a widget for rendering a radio group.
|
|
5
|
+
* It is typically used with a string property constrained with enum options.
|
|
6
|
+
*
|
|
7
|
+
* @param props - The `WidgetProps` for this component
|
|
8
|
+
*/
|
|
9
|
+
function RadioWidget<T = any, F extends GenericObjectType = any>({
|
|
10
|
+
options,
|
|
11
|
+
value,
|
|
12
|
+
required,
|
|
13
|
+
disabled,
|
|
14
|
+
readonly,
|
|
15
|
+
autofocus = false,
|
|
16
|
+
onBlur,
|
|
17
|
+
onFocus,
|
|
18
|
+
onChange,
|
|
19
|
+
id,
|
|
20
|
+
}: WidgetProps<T, F>) {
|
|
21
|
+
// Generating a unique field name to identify this set of radio buttons
|
|
22
|
+
const name = Math.random().toString();
|
|
23
|
+
const { enumOptions, enumDisabled, inline } = options;
|
|
24
|
+
// checked={checked} has been moved above name={name}, As mentioned in #349;
|
|
25
|
+
// this is a temporary fix for radio button rendering bug in React, facebook/react#7630.
|
|
26
|
+
|
|
27
|
+
const handleBlur = useCallback(
|
|
28
|
+
(event: FocusEvent<HTMLInputElement>) => onBlur(id, event.target.value),
|
|
29
|
+
[onBlur, id]
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const handleFocus = useCallback(
|
|
33
|
+
(event: FocusEvent<HTMLInputElement>) => onFocus(id, event.target.value),
|
|
34
|
+
[onFocus, id]
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div
|
|
39
|
+
className={`flex field-radio-group ${
|
|
40
|
+
inline ? "flex-row gap-2" : "flex-col"
|
|
41
|
+
}`}
|
|
42
|
+
id={id}
|
|
43
|
+
>
|
|
44
|
+
{Array.isArray(enumOptions) &&
|
|
45
|
+
enumOptions.map((option, i) => {
|
|
46
|
+
const checked = option.value === value;
|
|
47
|
+
const itemDisabled =
|
|
48
|
+
enumDisabled && enumDisabled.indexOf(option.value) != -1;
|
|
49
|
+
const disabledCls =
|
|
50
|
+
disabled || itemDisabled || readonly ? "disabled" : "";
|
|
51
|
+
|
|
52
|
+
const handleChange = () => onChange(option.value);
|
|
53
|
+
|
|
54
|
+
const radio = (
|
|
55
|
+
<>
|
|
56
|
+
<input
|
|
57
|
+
type="radio"
|
|
58
|
+
className="radio radio-secondary"
|
|
59
|
+
id={`${id}_${i}`}
|
|
60
|
+
checked={checked}
|
|
61
|
+
name={id}
|
|
62
|
+
required={required}
|
|
63
|
+
value={option.value}
|
|
64
|
+
disabled={disabled || itemDisabled || readonly}
|
|
65
|
+
autoFocus={autofocus && i === 0}
|
|
66
|
+
onChange={handleChange}
|
|
67
|
+
onBlur={handleBlur}
|
|
68
|
+
onFocus={handleFocus}
|
|
69
|
+
/>
|
|
70
|
+
<span className="label-text">{option.label}</span>
|
|
71
|
+
</>
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<label
|
|
76
|
+
key={i}
|
|
77
|
+
className={`label justify-start gap-2 ${disabledCls}`}
|
|
78
|
+
>
|
|
79
|
+
{radio}
|
|
80
|
+
</label>
|
|
81
|
+
);
|
|
82
|
+
})}
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export default RadioWidget;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { WidgetProps, GenericObjectType } from "@rjsf/utils";
|
|
3
|
+
|
|
4
|
+
/** The `RangeWidget` component uses the `BaseInputTemplate` changing the type to `range` and wrapping the result
|
|
5
|
+
* in a div, with the value along side it.
|
|
6
|
+
*
|
|
7
|
+
* @param props - The `WidgetProps` for this component
|
|
8
|
+
*/
|
|
9
|
+
export default function RangeWidget<T = any, F extends GenericObjectType = any>(
|
|
10
|
+
props: WidgetProps<T, F>
|
|
11
|
+
) {
|
|
12
|
+
const {
|
|
13
|
+
value,
|
|
14
|
+
registry: {
|
|
15
|
+
templates: { BaseInputTemplate },
|
|
16
|
+
},
|
|
17
|
+
} = props;
|
|
18
|
+
return (
|
|
19
|
+
<div className="field-range-wrapper">
|
|
20
|
+
<BaseInputTemplate type="range" {...props} />
|
|
21
|
+
<span className="range-view">{value}</span>
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
ChangeEvent,
|
|
3
|
+
FocusEvent,
|
|
4
|
+
FocusEventHandler,
|
|
5
|
+
useCallback,
|
|
6
|
+
} from "react";
|
|
7
|
+
import type {
|
|
8
|
+
EnumOptionsType,
|
|
9
|
+
GenericObjectType,
|
|
10
|
+
WidgetProps,
|
|
11
|
+
} from "@rjsf/utils";
|
|
12
|
+
import Select, { MultiValue, SingleValue } from "react-select";
|
|
13
|
+
import { selectStyles } from "../styles/select.styles";
|
|
14
|
+
import { Option } from "../interfaces/Option.interface";
|
|
15
|
+
import processSelectValue from "../utils/processSelectValue";
|
|
16
|
+
|
|
17
|
+
/** The `SelectWidget` is a widget for rendering dropdowns.
|
|
18
|
+
* It is typically used with string properties constrained with enum options.
|
|
19
|
+
*
|
|
20
|
+
* @param props - The `WidgetProps` for this component
|
|
21
|
+
*/
|
|
22
|
+
function SelectWidget<T = any, F extends GenericObjectType = any>({
|
|
23
|
+
schema,
|
|
24
|
+
id,
|
|
25
|
+
options,
|
|
26
|
+
name,
|
|
27
|
+
value,
|
|
28
|
+
disabled,
|
|
29
|
+
readonly,
|
|
30
|
+
multiple = false,
|
|
31
|
+
autofocus = false,
|
|
32
|
+
onBlur,
|
|
33
|
+
onFocus,
|
|
34
|
+
onChange,
|
|
35
|
+
}: WidgetProps<T, F>) {
|
|
36
|
+
const { enumOptions, enumDisabled } = options;
|
|
37
|
+
const emptyValue = multiple ? [] : "";
|
|
38
|
+
|
|
39
|
+
const handleFocus: FocusEventHandler<HTMLInputElement> = useCallback(
|
|
40
|
+
() => onFocus(id, processSelectValue(schema, value, options)),
|
|
41
|
+
[onFocus, id, schema, multiple, options]
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const handleBlur: FocusEventHandler<HTMLInputElement> = useCallback(
|
|
45
|
+
() => onBlur(id, processSelectValue(schema, value, options)),
|
|
46
|
+
[onBlur, id, schema, multiple, options]
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const handleChange = useCallback(
|
|
50
|
+
(event: MultiValue<EnumOptionsType> | SingleValue<EnumOptionsType>) => {
|
|
51
|
+
let newValue: string | string[] | undefined;
|
|
52
|
+
if (multiple && Array.isArray(event)) {
|
|
53
|
+
newValue = event.map((option) => option.value);
|
|
54
|
+
} else {
|
|
55
|
+
const singleEvent = event as SingleValue<EnumOptionsType>;
|
|
56
|
+
newValue = singleEvent?.value;
|
|
57
|
+
}
|
|
58
|
+
return onChange(processSelectValue(schema, newValue, options));
|
|
59
|
+
},
|
|
60
|
+
[onChange, schema, multiple, options]
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const getEnumObjectFromVal = (val: string) => {
|
|
64
|
+
return (
|
|
65
|
+
enumOptions?.find((option: Option) => option.value === val) || {
|
|
66
|
+
label: "",
|
|
67
|
+
value: "",
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
const getSelectValue = () => {
|
|
72
|
+
let selectValue: Option | Option[] | undefined = undefined;
|
|
73
|
+
if (multiple && Array.isArray(value)) {
|
|
74
|
+
selectValue = value.map((val) => getEnumObjectFromVal(val.toString()));
|
|
75
|
+
} else if (value) {
|
|
76
|
+
selectValue = getEnumObjectFromVal(value.toString());
|
|
77
|
+
}
|
|
78
|
+
return selectValue;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<Select
|
|
83
|
+
id={id}
|
|
84
|
+
instanceId={id}
|
|
85
|
+
isMulti={multiple}
|
|
86
|
+
name={name}
|
|
87
|
+
value={getSelectValue()}
|
|
88
|
+
isDisabled={disabled || readonly}
|
|
89
|
+
autoFocus={autofocus}
|
|
90
|
+
options={enumOptions}
|
|
91
|
+
onChange={handleChange}
|
|
92
|
+
onBlur={handleBlur}
|
|
93
|
+
onFocus={handleFocus}
|
|
94
|
+
styles={selectStyles}
|
|
95
|
+
/>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export default SelectWidget;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { GenericObjectType, WidgetProps } from "@rjsf/utils";
|
|
3
|
+
import { getTemplate } from "@rjsf/utils";
|
|
4
|
+
|
|
5
|
+
/** The `TextWidget` component uses the `BaseInputTemplate`.
|
|
6
|
+
*
|
|
7
|
+
* @param props - The `WidgetProps` for this component
|
|
8
|
+
*/
|
|
9
|
+
export default function TextWidget<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 {...props} />;
|
|
19
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React, { FocusEvent, useCallback } from "react";
|
|
2
|
+
import type { WidgetProps, GenericObjectType } from "@rjsf/utils";
|
|
3
|
+
|
|
4
|
+
/** The `TextareaWidget` is a widget for rendering input fields as textarea.
|
|
5
|
+
*
|
|
6
|
+
* @param props - The `WidgetProps` for this component
|
|
7
|
+
*/
|
|
8
|
+
function TextareaWidget<T = any, F extends GenericObjectType = any>({
|
|
9
|
+
id,
|
|
10
|
+
name,
|
|
11
|
+
options = {},
|
|
12
|
+
placeholder,
|
|
13
|
+
value,
|
|
14
|
+
required,
|
|
15
|
+
disabled,
|
|
16
|
+
readonly,
|
|
17
|
+
autofocus = false,
|
|
18
|
+
onChange,
|
|
19
|
+
onBlur,
|
|
20
|
+
onFocus,
|
|
21
|
+
}: WidgetProps<T, F>) {
|
|
22
|
+
const handleChange = useCallback(
|
|
23
|
+
({ target: { value } }: React.ChangeEvent<HTMLTextAreaElement>) =>
|
|
24
|
+
onChange(value === "" ? options.emptyValue : value),
|
|
25
|
+
[onChange, options.emptyValue]
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const handleBlur = useCallback(
|
|
29
|
+
({ target: { value } }: FocusEvent<HTMLTextAreaElement>) =>
|
|
30
|
+
onBlur(id, value),
|
|
31
|
+
[onBlur, id]
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const handleFocus = useCallback(
|
|
35
|
+
({ target: { value } }: FocusEvent<HTMLTextAreaElement>) =>
|
|
36
|
+
onFocus(id, value),
|
|
37
|
+
[id, onFocus]
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<textarea
|
|
42
|
+
id={id}
|
|
43
|
+
name={name}
|
|
44
|
+
className="textarea textarea-bordered"
|
|
45
|
+
value={value ? value : ""}
|
|
46
|
+
placeholder={placeholder}
|
|
47
|
+
required={required}
|
|
48
|
+
disabled={disabled}
|
|
49
|
+
readOnly={readonly}
|
|
50
|
+
autoFocus={autofocus}
|
|
51
|
+
rows={options.rows}
|
|
52
|
+
onBlur={handleBlur}
|
|
53
|
+
onFocus={handleFocus}
|
|
54
|
+
onChange={handleChange}
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
TextareaWidget.defaultProps = {
|
|
60
|
+
autofocus: false,
|
|
61
|
+
options: {},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export default TextareaWidget;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { GenericObjectType, WidgetProps } from "@rjsf/utils";
|
|
3
|
+
import { getTemplate } from "@rjsf/utils";
|
|
4
|
+
|
|
5
|
+
/** The `URLWidget` component uses the `BaseInputTemplate` changing the type to `url`.
|
|
6
|
+
*
|
|
7
|
+
* @param props - The `WidgetProps` for this component
|
|
8
|
+
*/
|
|
9
|
+
export default function URLWidget<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="url" {...props} />;
|
|
19
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { GenericObjectType, WidgetProps } from "@rjsf/utils";
|
|
3
|
+
import { getTemplate } from "@rjsf/utils";
|
|
4
|
+
|
|
5
|
+
/** The `UpDownWidget` component uses the `BaseInputTemplate` changing the type to `number`.
|
|
6
|
+
*
|
|
7
|
+
* @param props - The `WidgetProps` for this component
|
|
8
|
+
*/
|
|
9
|
+
export default function UpDownWidget<
|
|
10
|
+
T = any,
|
|
11
|
+
F extends GenericObjectType = any
|
|
12
|
+
>(props: WidgetProps<T, F>) {
|
|
13
|
+
const { options, registry } = props;
|
|
14
|
+
const BaseInputTemplate = getTemplate<"BaseInputTemplate", T, F>(
|
|
15
|
+
"BaseInputTemplate",
|
|
16
|
+
registry,
|
|
17
|
+
options
|
|
18
|
+
);
|
|
19
|
+
return <BaseInputTemplate type="number" {...props} />;
|
|
20
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { RegistryWidgetsType } from "@rjsf/utils";
|
|
2
|
+
|
|
3
|
+
import AltDateWidget from "./AltDateWidget";
|
|
4
|
+
import AltDateTimeWidget from "./AltDateTimeWidget";
|
|
5
|
+
import CheckboxWidget from "./CheckboxWidget";
|
|
6
|
+
import CheckboxesWidget from "./CheckboxesWidget";
|
|
7
|
+
import ColorWidget from "./ColorWidget";
|
|
8
|
+
import DateWidget from "./DateWidget";
|
|
9
|
+
import DateTimeWidget from "./DateTimeWidget";
|
|
10
|
+
import EmailWidget from "./EmailWidget";
|
|
11
|
+
import FileWidget from "./FileWidget";
|
|
12
|
+
import HiddenWidget from "./HiddenWidget";
|
|
13
|
+
import PasswordWidget from "./PasswordWidget";
|
|
14
|
+
import RadioWidget from "./RadioWidget";
|
|
15
|
+
import RangeWidget from "./RangeWidget";
|
|
16
|
+
import SelectWidget from "./SelectWidget";
|
|
17
|
+
import TextareaWidget from "./TextareaWidget";
|
|
18
|
+
import TextWidget from "./TextWidget";
|
|
19
|
+
import URLWidget from "./URLWidget";
|
|
20
|
+
import UpDownWidget from "./UpDownWidget";
|
|
21
|
+
|
|
22
|
+
const widgets: RegistryWidgetsType = {
|
|
23
|
+
PasswordWidget,
|
|
24
|
+
RadioWidget,
|
|
25
|
+
UpDownWidget,
|
|
26
|
+
RangeWidget,
|
|
27
|
+
SelectWidget,
|
|
28
|
+
TextWidget,
|
|
29
|
+
DateWidget,
|
|
30
|
+
DateTimeWidget,
|
|
31
|
+
AltDateWidget,
|
|
32
|
+
AltDateTimeWidget,
|
|
33
|
+
EmailWidget,
|
|
34
|
+
URLWidget,
|
|
35
|
+
TextareaWidget,
|
|
36
|
+
HiddenWidget,
|
|
37
|
+
ColorWidget,
|
|
38
|
+
FileWidget,
|
|
39
|
+
CheckboxWidget,
|
|
40
|
+
CheckboxesWidget,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default widgets;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
|
|
9
|
+
/* Bundler mode */
|
|
10
|
+
"moduleResolution": "bundler",
|
|
11
|
+
"allowImportingTsExtensions": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"noEmit": true,
|
|
15
|
+
"jsx": "react-jsx",
|
|
16
|
+
|
|
17
|
+
/* Linting */
|
|
18
|
+
"strict": false,
|
|
19
|
+
"noFallthroughCasesInSwitch": true
|
|
20
|
+
},
|
|
21
|
+
"include": ["src"],
|
|
22
|
+
"exclude": ["src/main.tsx"],
|
|
23
|
+
"references": [{ "path": "./tsconfig.node.json" }]
|
|
24
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import react from "@vitejs/plugin-react";
|
|
2
|
+
import { resolve } from "path";
|
|
3
|
+
import { defineConfig } from "vite";
|
|
4
|
+
import dts from "vite-plugin-dts";
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
plugins: [react(), dts({ rollupTypes: true })],
|
|
8
|
+
build: {
|
|
9
|
+
lib: {
|
|
10
|
+
entry: resolve(__dirname, "src/index.tsx"),
|
|
11
|
+
name: "@springmicro/forms",
|
|
12
|
+
fileName: "index",
|
|
13
|
+
},
|
|
14
|
+
rollupOptions: {
|
|
15
|
+
external: ["react", "react-dom", "nanoid"],
|
|
16
|
+
output: {
|
|
17
|
+
globals: {
|
|
18
|
+
react: "React",
|
|
19
|
+
"react-dom": "ReactDOM",
|
|
20
|
+
nanoid: "Nanoid",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
});
|