@react-fabric/form 1.0.0-beta.61 → 1.0.0-beta.62
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/cjs/builder/FileField.js +8 -5
- package/dist/cjs/builder/components/SchemaArrayForm.js +1 -1
- package/dist/cjs/builder/components/SchemaField.js +2 -2
- package/dist/cjs/form/Form.js +40 -15
- package/dist/cjs/hooks/useFileUploader.js +40 -28
- package/dist/cjs/i18n/index.js +4 -4
- package/dist/cjs/input/AvatarInput.js +14 -9
- package/dist/cjs/input/Hidden.js +1 -10
- package/dist/cjs/input/RadioGroup.js +3 -1
- package/dist/cjs/select/DualList.js +1 -1
- package/dist/cjs/select/List.js +4 -2
- package/dist/cjs/select/Select.js +1 -1
- package/dist/esm/builder/FileField.js +8 -5
- package/dist/esm/builder/components/SchemaArrayForm.js +1 -1
- package/dist/esm/builder/components/SchemaField.js +2 -2
- package/dist/esm/form/Form.js +40 -15
- package/dist/esm/hooks/useFileUploader.js +41 -29
- package/dist/esm/i18n/index.js +2 -2
- package/dist/esm/input/AvatarInput.js +14 -9
- package/dist/esm/input/Hidden.js +2 -11
- package/dist/esm/input/RadioGroup.js +3 -1
- package/dist/esm/select/DualList.js +1 -1
- package/dist/esm/select/List.js +4 -2
- package/dist/esm/select/Select.js +1 -1
- package/dist/types/builder/FileField.d.ts +2 -1
- package/dist/types/form/Form.d.ts +6 -2
- package/dist/types/hooks/useFileUploader.d.ts +4 -2
- package/dist/types/input/AvatarInput.d.ts +1 -1
- package/dist/types/input/Hidden.d.ts +3 -11
- package/dist/types/input/RadioGroup.d.ts +5 -1
- package/package.json +5 -5
- package/src/builder/FileField.tsx +5 -3
- package/src/builder/components/SchemaArrayForm.tsx +1 -1
- package/src/builder/components/SchemaField.tsx +2 -2
- package/src/form/Form.tsx +20 -13
- package/src/hooks/useFileUploader.ts +41 -29
- package/src/i18n/index.ts +2 -2
- package/src/input/AvatarInput.tsx +11 -11
- package/src/input/Hidden.tsx +4 -22
- package/src/input/RadioGroup.tsx +6 -0
- package/src/select/DualList.tsx +1 -1
- package/src/select/List.tsx +2 -2
- package/src/select/Select.tsx +1 -1
- /package/dist/cjs/i18n/{ar.json.js → ar/form.json.js} +0 -0
- /package/dist/cjs/i18n/{en.json.js → en/form.json.js} +0 -0
- /package/dist/esm/i18n/{ar.json.js → ar/form.json.js} +0 -0
- /package/dist/esm/i18n/{en.json.js → en/form.json.js} +0 -0
- /package/src/i18n/{ar.json → ar/form.json} +0 -0
- /package/src/i18n/{en.json → en/form.json} +0 -0
package/src/form/Form.tsx
CHANGED
|
@@ -37,7 +37,6 @@ import {
|
|
|
37
37
|
useFormContext,
|
|
38
38
|
useForm as useFormHook,
|
|
39
39
|
useWatch,
|
|
40
|
-
type DefaultValues,
|
|
41
40
|
type FormState,
|
|
42
41
|
type Path,
|
|
43
42
|
type Resolver,
|
|
@@ -70,7 +69,11 @@ export interface FormProps<K extends KeyValue = KeyValue> {
|
|
|
70
69
|
/**
|
|
71
70
|
* default data values
|
|
72
71
|
*/
|
|
73
|
-
defaultValues?: K
|
|
72
|
+
defaultValues?: K | Promise<K>;
|
|
73
|
+
/**
|
|
74
|
+
* reset values on defaultValues change (workaround for using fetch result)
|
|
75
|
+
*/
|
|
76
|
+
resetOnChange?: boolean;
|
|
74
77
|
/**
|
|
75
78
|
* change callback
|
|
76
79
|
*/
|
|
@@ -115,6 +118,7 @@ export const Form = <K extends KeyValue>({
|
|
|
115
118
|
formRef,
|
|
116
119
|
resolver,
|
|
117
120
|
children,
|
|
121
|
+
resetOnChange,
|
|
118
122
|
defaultValues = EMPTY_OBJECT as K,
|
|
119
123
|
onSubmit = DEFAULT_SUBMIT,
|
|
120
124
|
onChange,
|
|
@@ -124,7 +128,7 @@ export const Form = <K extends KeyValue>({
|
|
|
124
128
|
const form = useFormHook<K>({
|
|
125
129
|
shouldFocusError: true,
|
|
126
130
|
resolver,
|
|
127
|
-
defaultValues:
|
|
131
|
+
defaultValues: async () => await Promise.resolve(defaultValues),
|
|
128
132
|
});
|
|
129
133
|
|
|
130
134
|
const [, startTransition] = useTransition();
|
|
@@ -136,16 +140,19 @@ export const Form = <K extends KeyValue>({
|
|
|
136
140
|
return () => subscription.unsubscribe();
|
|
137
141
|
}, [form.watch, changeHandler]);
|
|
138
142
|
|
|
139
|
-
const handleReset = useCallback(
|
|
140
|
-
(reset
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
143
|
+
const handleReset = useCallback((reset: AnyObject = {}) => {
|
|
144
|
+
form.reset(reset);
|
|
145
|
+
form.clearErrors();
|
|
146
|
+
ref.current
|
|
147
|
+
?.querySelector<HTMLElement>("[auto-focus], input, textarea")
|
|
148
|
+
?.focus();
|
|
149
|
+
}, []);
|
|
150
|
+
|
|
151
|
+
useEffect(() => {
|
|
152
|
+
if (resetOnChange) {
|
|
153
|
+
handleReset(defaultValues);
|
|
154
|
+
}
|
|
155
|
+
}, [defaultValues, resetOnChange]);
|
|
149
156
|
|
|
150
157
|
useImperativeHandle(
|
|
151
158
|
formRef,
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
import { isArray, isObject, isString, uuid } from "@react-fabric/utilities";
|
|
25
25
|
import { type AxiosRequestConfig } from "axios";
|
|
26
|
-
import { useCallback, useReducer } from "react";
|
|
26
|
+
import { useCallback, useEffect, useReducer } from "react";
|
|
27
27
|
|
|
28
28
|
export type UploadHandler = (
|
|
29
29
|
data: FormData,
|
|
@@ -54,6 +54,7 @@ type UploaderActions =
|
|
|
54
54
|
| { type: "stop"; key: string; path: string }
|
|
55
55
|
| { type: "error"; key: string; error: string }
|
|
56
56
|
| { type: "progress"; key: string; progress: number }
|
|
57
|
+
| { type: "init"; initialList?: string | FileInfo | FileInfo[] }
|
|
57
58
|
| { type: "remove"; key: string }
|
|
58
59
|
| { type: "abort"; key: string };
|
|
59
60
|
|
|
@@ -76,7 +77,13 @@ type UploaderActions =
|
|
|
76
77
|
export const useFileUploader = (
|
|
77
78
|
uploader: UploadHandler,
|
|
78
79
|
initialList?: string | FileInfo | FileInfo[],
|
|
79
|
-
|
|
80
|
+
{
|
|
81
|
+
multiple = true,
|
|
82
|
+
onChange,
|
|
83
|
+
}: {
|
|
84
|
+
multiple?: boolean;
|
|
85
|
+
onChange?: (files: FileInfo[] | FileInfo | null) => void;
|
|
86
|
+
} = {},
|
|
80
87
|
) => {
|
|
81
88
|
const [state, dispatch] = useReducer(
|
|
82
89
|
(state: UploaderState, action: UploaderActions) => {
|
|
@@ -119,6 +126,29 @@ export const useFileUploader = (
|
|
|
119
126
|
if (action.type === "remove") {
|
|
120
127
|
state.files.delete(action.key);
|
|
121
128
|
}
|
|
129
|
+
if (action.type === "init") {
|
|
130
|
+
state.files = new Map();
|
|
131
|
+
if (isString(action.initialList)) {
|
|
132
|
+
const key = uuid();
|
|
133
|
+
state.files.set(key, {
|
|
134
|
+
key,
|
|
135
|
+
path: action.initialList,
|
|
136
|
+
filename: "",
|
|
137
|
+
size: 0,
|
|
138
|
+
mime: "",
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
if (isObject(action.initialList)) {
|
|
142
|
+
const key = uuid();
|
|
143
|
+
state.files.set(key, { ...(action.initialList as AnyObject), key });
|
|
144
|
+
}
|
|
145
|
+
if (isArray(action.initialList)) {
|
|
146
|
+
action.initialList.forEach((file) => {
|
|
147
|
+
const key = uuid();
|
|
148
|
+
state.files.set(key, { ...file, key });
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
122
152
|
state.list = Array.from(state.files.values())
|
|
123
153
|
.map((file) => {
|
|
124
154
|
return (
|
|
@@ -134,40 +164,23 @@ export const useFileUploader = (
|
|
|
134
164
|
);
|
|
135
165
|
})
|
|
136
166
|
.filter(Boolean) as FileInfo[];
|
|
167
|
+
if (action.type === "remove" || action.type === "stop") {
|
|
168
|
+
setTimeout(() => {
|
|
169
|
+
onChange?.(multiple ? state.list : state.list[0] ?? null);
|
|
170
|
+
}, 100);
|
|
171
|
+
}
|
|
137
172
|
return { ...state };
|
|
138
173
|
},
|
|
139
174
|
{
|
|
140
175
|
files: new Map(),
|
|
141
176
|
count: 0,
|
|
142
177
|
} as UploaderState,
|
|
143
|
-
(state: UploaderState) => {
|
|
144
|
-
if (initialList) {
|
|
145
|
-
if (isString(initialList)) {
|
|
146
|
-
const key = uuid();
|
|
147
|
-
state.files.set(key, {
|
|
148
|
-
key,
|
|
149
|
-
path: initialList,
|
|
150
|
-
filename: "",
|
|
151
|
-
size: 0,
|
|
152
|
-
mime: "",
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
if (isObject(initialList)) {
|
|
156
|
-
const key = uuid();
|
|
157
|
-
state.files.set(key, { ...(initialList as AnyObject), key });
|
|
158
|
-
}
|
|
159
|
-
if (isArray(initialList)) {
|
|
160
|
-
initialList.forEach((file) => {
|
|
161
|
-
const key = uuid();
|
|
162
|
-
state.files.set(key, { ...file, key });
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
state.list = Array.from(state.files.values());
|
|
167
|
-
return state;
|
|
168
|
-
},
|
|
169
178
|
);
|
|
170
179
|
|
|
180
|
+
useEffect(() => {
|
|
181
|
+
dispatch({ type: "init", initialList });
|
|
182
|
+
}, [initialList]);
|
|
183
|
+
|
|
171
184
|
const doUpload = useCallback(async (key: string, file: File) => {
|
|
172
185
|
const data = new FormData();
|
|
173
186
|
data.append("file", file);
|
|
@@ -244,7 +257,6 @@ export const useFileUploader = (
|
|
|
244
257
|
remove,
|
|
245
258
|
retry,
|
|
246
259
|
pending: state.count,
|
|
247
|
-
list: state.list,
|
|
248
260
|
files: Array.from(state.files.values()),
|
|
249
261
|
};
|
|
250
262
|
};
|
package/src/i18n/index.ts
CHANGED
|
@@ -88,17 +88,21 @@ export const AvatarInput = ({
|
|
|
88
88
|
variant,
|
|
89
89
|
fileUrl,
|
|
90
90
|
uploadHandler,
|
|
91
|
+
onChange,
|
|
91
92
|
...rest
|
|
92
93
|
}: Props) => {
|
|
93
|
-
const { files,
|
|
94
|
+
const { files, upload, remove } = useFileUploader(
|
|
94
95
|
async (data, config) => await uploadHandler?.(data, config),
|
|
95
|
-
value,
|
|
96
|
-
|
|
96
|
+
value ?? defaultValue,
|
|
97
|
+
{
|
|
98
|
+
multiple: false,
|
|
99
|
+
onChange: (files: AnyObject) => onChange?.(files?.path ?? null),
|
|
100
|
+
},
|
|
97
101
|
);
|
|
98
102
|
|
|
99
103
|
const [base64, setBase64] = useState("");
|
|
100
104
|
useEffect(() => {
|
|
101
|
-
if (
|
|
105
|
+
if (files?.length) {
|
|
102
106
|
const file = files?.[0]?.file;
|
|
103
107
|
void (
|
|
104
108
|
file &&
|
|
@@ -116,7 +120,7 @@ export const AvatarInput = ({
|
|
|
116
120
|
} else {
|
|
117
121
|
setBase64(fileUrl?.(defaultValue ?? "") ?? defaultValue ?? "");
|
|
118
122
|
}
|
|
119
|
-
}, [files,
|
|
123
|
+
}, [files, defaultValue]);
|
|
120
124
|
|
|
121
125
|
useEffect(() => {
|
|
122
126
|
!value &&
|
|
@@ -126,7 +130,7 @@ export const AvatarInput = ({
|
|
|
126
130
|
}, [value, defaultValue]);
|
|
127
131
|
|
|
128
132
|
return (
|
|
129
|
-
<div className="flex flex-nowrap items-end overflow-hidden">
|
|
133
|
+
<div className="flex flex-nowrap items-end overflow-hidden p-px">
|
|
130
134
|
<div
|
|
131
135
|
className={classNames(
|
|
132
136
|
"inline-block leading-none relative outline overflow-hidden",
|
|
@@ -153,11 +157,7 @@ export const AvatarInput = ({
|
|
|
153
157
|
/>
|
|
154
158
|
</div>
|
|
155
159
|
</div>
|
|
156
|
-
<HiddenInput
|
|
157
|
-
hiddenValue={list?.[0]?.path}
|
|
158
|
-
defaultValue={defaultValue}
|
|
159
|
-
{...rest}
|
|
160
|
-
/>
|
|
160
|
+
<HiddenInput value={files?.[0]?.path} {...rest} />
|
|
161
161
|
{files.map((file, idx) => (
|
|
162
162
|
<div key={idx} className="px-2 py-1 flex-1 overflow-hidden">
|
|
163
163
|
<div className="flex flex-nowrap gap-1 items-center">
|
package/src/input/Hidden.tsx
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import { Fragment
|
|
24
|
+
import { Fragment } from "react";
|
|
25
25
|
import { ErrorIcon } from "../internal/ErrorIcon";
|
|
26
26
|
|
|
27
27
|
interface Props {
|
|
@@ -30,17 +30,9 @@ interface Props {
|
|
|
30
30
|
*/
|
|
31
31
|
name?: string;
|
|
32
32
|
/**
|
|
33
|
-
* hidden value
|
|
33
|
+
* hidden input value
|
|
34
34
|
*/
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* default avatar image
|
|
38
|
-
*/
|
|
39
|
-
defaultValue?: string;
|
|
40
|
-
/**
|
|
41
|
-
* form onChange handler
|
|
42
|
-
*/
|
|
43
|
-
onChange?: (value: AnyObject) => void;
|
|
35
|
+
value?: AnyObject;
|
|
44
36
|
}
|
|
45
37
|
|
|
46
38
|
/**
|
|
@@ -52,28 +44,18 @@ interface Props {
|
|
|
52
44
|
* @returns {JSX.Element} The rendered hidden input field and error icon.
|
|
53
45
|
*/
|
|
54
46
|
export const HiddenInput = ({
|
|
55
|
-
hiddenValue,
|
|
56
|
-
defaultValue,
|
|
57
|
-
onChange,
|
|
58
47
|
// @ts-expect-error ignore
|
|
59
48
|
// eslint-disable-next-line react/prop-types
|
|
60
49
|
invalid,
|
|
61
50
|
// @ts-expect-error ignore
|
|
62
51
|
// eslint-disable-next-line react/prop-types
|
|
63
52
|
error,
|
|
64
|
-
// @ts-expect-error ignore
|
|
65
|
-
// eslint-disable-next-line react/prop-types
|
|
66
53
|
value = "",
|
|
67
54
|
name,
|
|
68
55
|
}: Props) => {
|
|
69
|
-
useEffect(() => {
|
|
70
|
-
setTimeout(() => {
|
|
71
|
-
onChange?.(hiddenValue ?? defaultValue);
|
|
72
|
-
}, 50);
|
|
73
|
-
}, [hiddenValue, defaultValue]);
|
|
74
56
|
return (
|
|
75
57
|
<Fragment>
|
|
76
|
-
<input type="hidden"
|
|
58
|
+
<input type="hidden" value={value} name={name} />
|
|
77
59
|
<ErrorIcon invalid={invalid} error={error} />
|
|
78
60
|
</Fragment>
|
|
79
61
|
);
|
package/src/input/RadioGroup.tsx
CHANGED
|
@@ -68,6 +68,10 @@ export interface RadioGroupProps extends ChildrenProp<typeof Radio>, CssProp {
|
|
|
68
68
|
* vertical orientation
|
|
69
69
|
*/
|
|
70
70
|
vertical?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* value
|
|
73
|
+
*/
|
|
74
|
+
value?: string | number;
|
|
71
75
|
/**
|
|
72
76
|
* change handler
|
|
73
77
|
*/
|
|
@@ -87,6 +91,7 @@ export const RadioGroup = ({
|
|
|
87
91
|
name,
|
|
88
92
|
className,
|
|
89
93
|
optionWidth,
|
|
94
|
+
value,
|
|
90
95
|
onChange,
|
|
91
96
|
...rest
|
|
92
97
|
}: RadioGroupProps) => {
|
|
@@ -96,6 +101,7 @@ export const RadioGroup = ({
|
|
|
96
101
|
{cloneChildren(children, (child: AnyObject) =>
|
|
97
102
|
cloneElement(child, {
|
|
98
103
|
name,
|
|
104
|
+
checked: child.props.value === value,
|
|
99
105
|
onChange,
|
|
100
106
|
width: optionWidth,
|
|
101
107
|
}),
|
package/src/select/DualList.tsx
CHANGED
|
@@ -105,7 +105,7 @@ export const DualList = <T extends AnyObject = string>({
|
|
|
105
105
|
groupProperty,
|
|
106
106
|
height = "18rem",
|
|
107
107
|
labelProperty = "label" as AnyObject,
|
|
108
|
-
valueProperty = "
|
|
108
|
+
valueProperty = "value" as AnyObject,
|
|
109
109
|
renderer,
|
|
110
110
|
onChange,
|
|
111
111
|
// @ts-expect-error ignore
|
package/src/select/List.tsx
CHANGED
|
@@ -59,8 +59,8 @@ export const List = <T extends AnyObject = string>({
|
|
|
59
59
|
options,
|
|
60
60
|
height,
|
|
61
61
|
groupProperty,
|
|
62
|
-
labelProperty,
|
|
63
|
-
valueProperty,
|
|
62
|
+
labelProperty = "label" as AnyObject,
|
|
63
|
+
valueProperty = "value" as AnyObject,
|
|
64
64
|
infoProperty,
|
|
65
65
|
sortProperty,
|
|
66
66
|
multiple,
|
package/src/select/Select.tsx
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|