@react-fabric/form 1.0.0-beta.6 → 1.0.0-beta.60
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/{src/input/ArrayInput.module.css → css/_arrayInput.css} +8 -8
- package/css/_colorPicker.css +65 -0
- package/{src/internal/Field.module.css → css/_field.css} +66 -30
- package/{src/input/File.module.css → css/_file.css} +21 -6
- package/css/styles.css +27 -0
- package/dist/cjs/builder/FileField.js +40 -28
- package/dist/cjs/builder/FormField.js +50 -34
- package/dist/cjs/builder/SchemaEditor.js +72 -0
- package/dist/cjs/builder/components/AddSchemaFields.js +62 -0
- package/dist/cjs/builder/components/SchemaArrayForm.js +145 -0
- package/dist/cjs/builder/components/SchemaField.js +414 -0
- package/dist/cjs/form/Controller.js +46 -1
- package/dist/cjs/form/Form.js +70 -9
- package/dist/cjs/hooks/useFileUploader.js +16 -0
- package/dist/cjs/hooks/useFormBuilder.js +57 -15
- package/dist/cjs/i18n/ar.json.js +5 -4
- package/dist/cjs/i18n/en.json.js +5 -5
- package/dist/cjs/index.js +12 -2
- package/dist/cjs/input/ArrayInput.js +126 -36
- package/dist/cjs/input/AvatarInput.js +43 -23
- package/dist/cjs/input/Checkbox.js +16 -6
- package/dist/cjs/input/ColorInput.js +113 -58
- package/dist/cjs/input/ColorPicker.js +60 -76
- package/dist/cjs/input/DateInput.js +197 -0
- package/dist/cjs/input/Field.js +10 -4
- package/dist/cjs/input/File.js +14 -7
- package/dist/cjs/input/Hidden.js +13 -2
- package/dist/cjs/input/Input.js +24 -10
- package/dist/cjs/input/Number.js +22 -10
- package/dist/cjs/input/Password.js +22 -12
- package/dist/cjs/input/Radio.js +12 -3
- package/dist/cjs/input/RadioGroup.js +10 -1
- package/dist/cjs/input/{Range.js → RangeSlider.js} +16 -8
- package/dist/cjs/input/Search.js +30 -19
- package/dist/cjs/input/Slider.js +12 -6
- package/dist/cjs/input/Switch.js +20 -9
- package/dist/cjs/input/Textarea.js +29 -8
- package/dist/cjs/internal/ErrorIcon.js +1 -1
- package/dist/cjs/internal/FieldWrapper.js +11 -4
- package/dist/cjs/internal/InputWrapper.js +27 -20
- package/dist/cjs/select/AutoComplete.js +21 -11
- package/dist/cjs/select/DualList.js +34 -12
- package/dist/cjs/select/List.js +68 -22
- package/dist/cjs/select/Options.js +49 -42
- package/dist/cjs/select/Select.js +40 -10
- package/dist/cjs/select/useSelect.js +22 -11
- package/dist/cjs/types/schema.js +43 -0
- package/dist/cjs/utils/eventHandlers.js +15 -5
- package/dist/esm/builder/FileField.js +42 -30
- package/dist/esm/builder/FormField.js +50 -34
- package/dist/esm/builder/SchemaEditor.js +72 -0
- package/dist/esm/builder/components/AddSchemaFields.js +62 -0
- package/dist/esm/builder/components/SchemaArrayForm.js +145 -0
- package/dist/esm/builder/components/SchemaField.js +414 -0
- package/dist/esm/form/Controller.js +46 -1
- package/dist/esm/form/Form.js +72 -12
- package/dist/esm/hooks/useFileUploader.js +16 -0
- package/dist/esm/hooks/useFormBuilder.js +55 -13
- package/dist/esm/i18n/ar.json.js +5 -4
- package/dist/esm/i18n/en.json.js +5 -5
- package/dist/esm/index.js +6 -2
- package/dist/esm/input/ArrayInput.js +126 -36
- package/dist/esm/input/AvatarInput.js +44 -24
- package/dist/esm/input/Checkbox.js +16 -6
- package/dist/esm/input/ColorInput.js +115 -60
- package/dist/esm/input/ColorPicker.js +60 -76
- package/dist/esm/input/DateInput.js +197 -0
- package/dist/esm/input/Field.js +11 -5
- package/dist/esm/input/File.js +16 -9
- package/dist/esm/input/Hidden.js +13 -2
- package/dist/esm/input/Input.js +26 -12
- package/dist/esm/input/Number.js +24 -12
- package/dist/esm/input/Password.js +24 -14
- package/dist/esm/input/Radio.js +12 -3
- package/dist/esm/input/RadioGroup.js +11 -2
- package/dist/esm/input/{Range.js → RangeSlider.js} +16 -8
- package/dist/esm/input/Search.js +32 -21
- package/dist/esm/input/Slider.js +12 -6
- package/dist/esm/input/Switch.js +20 -9
- package/dist/esm/input/Textarea.js +30 -9
- package/dist/esm/internal/ErrorIcon.js +1 -1
- package/dist/esm/internal/FieldWrapper.js +11 -4
- package/dist/esm/internal/InputWrapper.js +27 -20
- package/dist/esm/select/AutoComplete.js +21 -11
- package/dist/esm/select/DualList.js +34 -12
- package/dist/esm/select/List.js +69 -23
- package/dist/esm/select/Options.js +49 -42
- package/dist/esm/select/Select.js +40 -10
- package/dist/esm/select/useSelect.js +23 -12
- package/{src/input/ColorPicker.module.css → dist/esm/types/schema.js} +18 -27
- package/dist/esm/utils/eventHandlers.js +15 -5
- package/dist/types/builder/FileField.d.ts +4 -2
- package/dist/types/builder/FormField.d.ts +3 -1
- package/dist/types/builder/SchemaEditor.d.ts +9 -0
- package/dist/types/builder/components/AddSchemaFields.d.ts +2 -0
- package/dist/types/builder/components/SchemaArrayForm.d.ts +2 -0
- package/dist/types/builder/components/SchemaField.d.ts +10 -0
- package/dist/types/form/Controller.d.ts +47 -2
- package/dist/types/form/Form.d.ts +48 -2
- package/dist/types/hooks/useFileUploader.d.ts +16 -0
- package/dist/types/hooks/useFormBuilder.d.ts +32 -1
- package/dist/types/index.d.ts +6 -3
- package/dist/types/input/ArrayInput.d.ts +69 -12
- package/dist/types/input/AvatarInput.d.ts +23 -3
- package/dist/types/input/Checkbox.d.ts +13 -4
- package/dist/types/input/ColorInput.d.ts +15 -2
- package/dist/types/input/ColorPicker.d.ts +11 -1
- package/dist/types/input/DateInput.d.ts +28 -0
- package/dist/types/input/Field.d.ts +5 -1
- package/dist/types/input/File.d.ts +9 -0
- package/dist/types/input/Hidden.d.ts +13 -1
- package/dist/types/input/Input.d.ts +9 -1
- package/dist/types/input/Number.d.ts +9 -1
- package/dist/types/input/Password.d.ts +7 -1
- package/dist/types/input/Radio.d.ts +9 -1
- package/dist/types/input/RadioGroup.d.ts +8 -1
- package/dist/types/input/RangeSlider.d.ts +67 -0
- package/dist/types/input/Search.d.ts +14 -1
- package/dist/types/input/Slider.d.ts +8 -2
- package/dist/types/input/Switch.d.ts +10 -2
- package/dist/types/input/Textarea.d.ts +15 -1
- package/dist/types/internal/FieldWrapper.d.ts +1 -1
- package/dist/types/internal/InputWrapper.d.ts +1 -1
- package/dist/types/ranger/index.d.ts +2 -2
- package/dist/types/select/AutoComplete.d.ts +8 -1
- package/dist/types/select/DualList.d.ts +17 -2
- package/dist/types/select/List.d.ts +9 -1
- package/dist/types/select/Options.d.ts +4 -3
- package/dist/types/select/Select.d.ts +9 -1
- package/dist/types/select/useSelect.d.ts +10 -6
- package/dist/types/types/index.d.ts +34 -5
- package/dist/types/types/schema.d.ts +78 -33
- package/dist/types/utils/eventHandlers.d.ts +2 -2
- package/package.json +22 -20
- package/src/builder/FileField.tsx +62 -30
- package/src/builder/FormField.tsx +52 -32
- package/src/builder/SchemaEditor.tsx +79 -0
- package/src/builder/components/AddSchemaFields.tsx +74 -0
- package/src/builder/components/SchemaArrayForm.tsx +161 -0
- package/src/builder/components/SchemaField.tsx +436 -0
- package/src/form/Controller.tsx +48 -3
- package/src/form/Form.tsx +80 -8
- package/src/hooks/useFileUploader.ts +16 -0
- package/src/hooks/useFormBuilder.tsx +58 -15
- package/src/i18n/ar.json +5 -4
- package/src/i18n/en.json +5 -5
- package/src/index.ts +12 -3
- package/src/input/ArrayInput.tsx +199 -64
- package/src/input/AvatarInput.tsx +67 -19
- package/src/input/Checkbox.tsx +16 -6
- package/src/input/ColorInput.tsx +131 -71
- package/src/input/ColorPicker.tsx +54 -72
- package/src/input/DateInput.tsx +260 -0
- package/src/input/Field.tsx +15 -11
- package/src/input/File.tsx +16 -14
- package/src/input/Hidden.tsx +17 -2
- package/src/input/Input.tsx +27 -13
- package/src/input/Number.tsx +22 -13
- package/src/input/Password.tsx +24 -16
- package/src/input/Radio.tsx +12 -3
- package/src/input/RadioGroup.tsx +12 -3
- package/src/input/{Range.tsx → RangeSlider.tsx} +17 -9
- package/src/input/Search.tsx +33 -22
- package/src/input/Slider.tsx +20 -11
- package/src/input/Switch.tsx +17 -6
- package/src/input/Textarea.tsx +34 -9
- package/src/internal/ErrorIcon.tsx +1 -1
- package/src/internal/FieldWrapper.tsx +8 -4
- package/src/internal/InputWrapper.tsx +49 -27
- package/src/select/AutoComplete.tsx +14 -3
- package/src/select/DualList.tsx +31 -16
- package/src/select/List.tsx +52 -8
- package/src/select/Options.tsx +50 -46
- package/src/select/Select.tsx +25 -5
- package/src/select/useSelect.tsx +26 -9
- package/src/types/index.ts +37 -6
- package/src/types/schema.ts +96 -41
- package/src/utils/eventHandlers.ts +14 -4
- package/dist/cjs/input/ArrayInput.module.css +0 -1
- package/dist/cjs/input/ColorPicker.module.css +0 -1
- package/dist/cjs/input/File.module.css +0 -1
- package/dist/cjs/internal/Field.module.css +0 -1
- package/dist/esm/input/ArrayInput.module.css +0 -1
- package/dist/esm/input/ColorPicker.module.css +0 -1
- package/dist/esm/input/File.module.css +0 -1
- package/dist/esm/internal/Field.module.css +0 -1
- package/dist/types/input/Range.d.ts +0 -59
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* React Fabric
|
|
3
|
+
* @version: 1.0.0
|
|
4
|
+
*
|
|
5
|
+
*
|
|
6
|
+
* The MIT License (MIT)
|
|
7
|
+
* Copyright (c) 2025 Adarsh Pastakia
|
|
8
|
+
*
|
|
9
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
|
10
|
+
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
|
11
|
+
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
12
|
+
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
|
|
13
|
+
* subject to the following conditions:
|
|
14
|
+
*
|
|
15
|
+
* The above copyright notice and this permission notice shall be included in all copies or substantial
|
|
16
|
+
* portions of the Software.
|
|
17
|
+
*
|
|
18
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
19
|
+
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
20
|
+
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
21
|
+
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import {
|
|
25
|
+
arrow,
|
|
26
|
+
autoUpdate,
|
|
27
|
+
flip,
|
|
28
|
+
FloatingArrow,
|
|
29
|
+
offset,
|
|
30
|
+
shift,
|
|
31
|
+
useClick,
|
|
32
|
+
useDismiss,
|
|
33
|
+
useFloating,
|
|
34
|
+
useFocus,
|
|
35
|
+
useInteractions,
|
|
36
|
+
} from "@floating-ui/react";
|
|
37
|
+
import { type RefProp } from "@react-fabric/core/dist/types/types";
|
|
38
|
+
import { type DateLike, DatePanel, DateUtil } from "@react-fabric/date";
|
|
39
|
+
import { mergeRefs } from "@react-fabric/utilities";
|
|
40
|
+
import classNames from "classnames";
|
|
41
|
+
import {
|
|
42
|
+
type ChangeEvent,
|
|
43
|
+
startTransition,
|
|
44
|
+
useCallback,
|
|
45
|
+
useDeferredValue,
|
|
46
|
+
useEffect,
|
|
47
|
+
useMemo,
|
|
48
|
+
useRef,
|
|
49
|
+
useState,
|
|
50
|
+
} from "react";
|
|
51
|
+
import { InputWrapper } from "../internal/InputWrapper";
|
|
52
|
+
import { type InputProps } from "../types";
|
|
53
|
+
import { useHandleEnter } from "../utils";
|
|
54
|
+
|
|
55
|
+
export interface DateProps
|
|
56
|
+
extends InputProps<string>,
|
|
57
|
+
RefProp<HTMLInputElement> {
|
|
58
|
+
type?: "date" | "datetime";
|
|
59
|
+
/**
|
|
60
|
+
* date format
|
|
61
|
+
*/
|
|
62
|
+
format?: string;
|
|
63
|
+
/**
|
|
64
|
+
* minimum value
|
|
65
|
+
*/
|
|
66
|
+
min?: DateLike;
|
|
67
|
+
/**
|
|
68
|
+
* maximum value
|
|
69
|
+
*/
|
|
70
|
+
max?: DateLike;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Date input component that allows users to select a date or datetime.
|
|
75
|
+
* It uses the Floating UI library for positioning the date picker.
|
|
76
|
+
* It also provides a button to clear the selected date.
|
|
77
|
+
* The component supports both date and datetime types, with appropriate formatting.
|
|
78
|
+
*
|
|
79
|
+
* @param {DateProps} props - The properties for the DateInput component.
|
|
80
|
+
* @returns {JSX.Element} - The rendered DateInput component.
|
|
81
|
+
*/
|
|
82
|
+
export const DateInput = ({
|
|
83
|
+
ref,
|
|
84
|
+
name,
|
|
85
|
+
value,
|
|
86
|
+
invalid,
|
|
87
|
+
readOnly,
|
|
88
|
+
disabled,
|
|
89
|
+
required,
|
|
90
|
+
placeholder,
|
|
91
|
+
defaultValue,
|
|
92
|
+
autoFocus,
|
|
93
|
+
error,
|
|
94
|
+
type = "date",
|
|
95
|
+
onBlur,
|
|
96
|
+
onFocus,
|
|
97
|
+
onChange,
|
|
98
|
+
onEnterPressed,
|
|
99
|
+
format = "dd/MM/yyyy",
|
|
100
|
+
min,
|
|
101
|
+
max,
|
|
102
|
+
...rest
|
|
103
|
+
}: DateProps) => {
|
|
104
|
+
const [actualValue, setActualValue] = useState("");
|
|
105
|
+
const [inputValue, setInputValue] = useState("");
|
|
106
|
+
const deferred = useDeferredValue(value ?? defaultValue);
|
|
107
|
+
|
|
108
|
+
const inputFormat = useMemo(
|
|
109
|
+
() => (type === "datetime" ? "yyyy-MM-dd HH:mm" : "yyyy-MM-dd"),
|
|
110
|
+
[type],
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
setActualValue(deferred ?? "");
|
|
115
|
+
setInputValue(deferred ? DateUtil.format(deferred, inputFormat) : "");
|
|
116
|
+
}, [deferred, inputFormat]);
|
|
117
|
+
|
|
118
|
+
const handleEnterPressed = useHandleEnter(onEnterPressed);
|
|
119
|
+
|
|
120
|
+
const handleChange = useCallback(
|
|
121
|
+
(e?: ChangeEvent<HTMLInputElement>) => {
|
|
122
|
+
const value = e?.currentTarget.value ?? defaultValue;
|
|
123
|
+
if (value) {
|
|
124
|
+
const parsed = DateUtil.parseDate(value);
|
|
125
|
+
setActualValue(parsed?.toISOString?.());
|
|
126
|
+
setInputValue(DateUtil.format(parsed, inputFormat));
|
|
127
|
+
onChange != null &&
|
|
128
|
+
startTransition(() => onChange(parsed?.toISOString?.()));
|
|
129
|
+
} else {
|
|
130
|
+
setActualValue("");
|
|
131
|
+
setInputValue("");
|
|
132
|
+
onChange != null && startTransition(() => onChange(null));
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
[onChange, inputFormat, defaultValue],
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
const handleDateChange = useCallback(
|
|
139
|
+
(value?: string) => {
|
|
140
|
+
if (value) {
|
|
141
|
+
const parsed = DateUtil.parseDate(value);
|
|
142
|
+
setActualValue(parsed?.toISOString?.());
|
|
143
|
+
setInputValue(DateUtil.format(parsed, inputFormat));
|
|
144
|
+
onChange != null &&
|
|
145
|
+
startTransition(() => onChange(parsed?.toISOString?.()));
|
|
146
|
+
} else {
|
|
147
|
+
setActualValue("");
|
|
148
|
+
setInputValue("");
|
|
149
|
+
onChange != null && startTransition(() => onChange(null));
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
[onChange, inputFormat],
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
const arrowRef = useRef(null);
|
|
156
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
157
|
+
const { refs, floatingStyles, context } = useFloating({
|
|
158
|
+
open: isOpen,
|
|
159
|
+
onOpenChange: (open, _, reason) => {
|
|
160
|
+
console.log(reason);
|
|
161
|
+
if (reason === "reference-press") return;
|
|
162
|
+
setIsOpen(open);
|
|
163
|
+
},
|
|
164
|
+
strategy: "fixed",
|
|
165
|
+
placement: "bottom-start",
|
|
166
|
+
whileElementsMounted: autoUpdate,
|
|
167
|
+
middleware: [
|
|
168
|
+
shift({ padding: 8 }),
|
|
169
|
+
flip(),
|
|
170
|
+
offset(9),
|
|
171
|
+
arrow({
|
|
172
|
+
element: arrowRef,
|
|
173
|
+
}),
|
|
174
|
+
],
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const focus = useFocus(context, {
|
|
178
|
+
enabled: !disabled,
|
|
179
|
+
});
|
|
180
|
+
const click = useClick(context, {
|
|
181
|
+
enabled: !disabled,
|
|
182
|
+
toggle: false,
|
|
183
|
+
});
|
|
184
|
+
const dismiss = useDismiss(context, {
|
|
185
|
+
referencePress: true,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const { getReferenceProps, getFloatingProps } = useInteractions([
|
|
189
|
+
dismiss,
|
|
190
|
+
click,
|
|
191
|
+
focus,
|
|
192
|
+
]);
|
|
193
|
+
|
|
194
|
+
return (
|
|
195
|
+
<InputWrapper
|
|
196
|
+
showClear={!!actualValue && !disabled && !readOnly}
|
|
197
|
+
onClear={handleChange}
|
|
198
|
+
invalid={invalid}
|
|
199
|
+
readOnly={readOnly}
|
|
200
|
+
disabled={disabled}
|
|
201
|
+
required={required}
|
|
202
|
+
error={error}
|
|
203
|
+
{...rest}
|
|
204
|
+
>
|
|
205
|
+
<input
|
|
206
|
+
className={classNames(
|
|
207
|
+
"appearance-none bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0 peer",
|
|
208
|
+
!value && "empty",
|
|
209
|
+
)}
|
|
210
|
+
aria-invalid={invalid}
|
|
211
|
+
aria-disabled={disabled}
|
|
212
|
+
aria-readonly={readOnly}
|
|
213
|
+
aria-errormessage={error}
|
|
214
|
+
placeholder={placeholder}
|
|
215
|
+
disabled={disabled}
|
|
216
|
+
readOnly={readOnly}
|
|
217
|
+
data-testid={name}
|
|
218
|
+
name={name}
|
|
219
|
+
size={1}
|
|
220
|
+
type={type === "datetime" ? "datetime-local" : "date"}
|
|
221
|
+
autoComplete="off"
|
|
222
|
+
autoFocus={autoFocus}
|
|
223
|
+
value={inputValue}
|
|
224
|
+
ref={mergeRefs(ref, refs.setReference)}
|
|
225
|
+
{...getReferenceProps({
|
|
226
|
+
onBlur,
|
|
227
|
+
onFocus,
|
|
228
|
+
})}
|
|
229
|
+
onChange={handleChange}
|
|
230
|
+
onKeyDown={handleEnterPressed}
|
|
231
|
+
/>
|
|
232
|
+
{isOpen && (
|
|
233
|
+
<div
|
|
234
|
+
ref={refs.setFloating}
|
|
235
|
+
style={{
|
|
236
|
+
zIndex: "var(--z-popover)",
|
|
237
|
+
...floatingStyles,
|
|
238
|
+
}}
|
|
239
|
+
className="shadow-lg bg-base ring-1 ring-tint-100"
|
|
240
|
+
data-ref="dropdownBody"
|
|
241
|
+
{...getFloatingProps()}
|
|
242
|
+
>
|
|
243
|
+
<DatePanel
|
|
244
|
+
withTime={type === "datetime"}
|
|
245
|
+
value={actualValue}
|
|
246
|
+
max={max}
|
|
247
|
+
min={min}
|
|
248
|
+
onChange={handleDateChange}
|
|
249
|
+
/>
|
|
250
|
+
<FloatingArrow
|
|
251
|
+
ref={arrowRef}
|
|
252
|
+
context={context}
|
|
253
|
+
strokeWidth={0.5}
|
|
254
|
+
className="fill-base stroke-muted"
|
|
255
|
+
/>
|
|
256
|
+
</div>
|
|
257
|
+
)}
|
|
258
|
+
</InputWrapper>
|
|
259
|
+
);
|
|
260
|
+
};
|
package/src/input/Field.tsx
CHANGED
|
@@ -21,13 +21,13 @@
|
|
|
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 { cloneChildren } from "@react-fabric/core";
|
|
24
25
|
import {
|
|
25
26
|
type ChildrenProp,
|
|
26
27
|
type CssProp,
|
|
27
28
|
} from "@react-fabric/core/dist/types/types";
|
|
28
29
|
import classNames from "classnames";
|
|
29
|
-
import {
|
|
30
|
-
import classes from "../internal/Field.module.css";
|
|
30
|
+
import { cloneElement } from "react";
|
|
31
31
|
import { FieldWrapper } from "../internal/FieldWrapper";
|
|
32
32
|
|
|
33
33
|
export interface FieldProps extends ChildrenProp, CssProp {
|
|
@@ -72,6 +72,11 @@ export interface FieldProps extends ChildrenProp, CssProp {
|
|
|
72
72
|
vertical?: boolean;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Field component for rendering multiple form fields under a single label.
|
|
77
|
+
* It supports various properties for customization, including label, info, required state,
|
|
78
|
+
* and styling options.
|
|
79
|
+
*/
|
|
75
80
|
export const Field = ({
|
|
76
81
|
children,
|
|
77
82
|
vertical,
|
|
@@ -83,20 +88,19 @@ export const Field = ({
|
|
|
83
88
|
<FieldWrapper {...rest}>
|
|
84
89
|
<div
|
|
85
90
|
className={classNames(
|
|
86
|
-
|
|
91
|
+
"fabric-fieldGroup",
|
|
87
92
|
!plain && !noOutline && "outline",
|
|
88
93
|
!plain && "bg-base",
|
|
94
|
+
plain && "flex-wrap",
|
|
89
95
|
"flex-1 rounded",
|
|
90
|
-
!vertical && "flex
|
|
96
|
+
!vertical && "flex",
|
|
91
97
|
)}
|
|
98
|
+
data-vertical={!!vertical}
|
|
92
99
|
>
|
|
93
|
-
{
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
cloneElement(child as AnyObject, {
|
|
98
|
-
"data-inner": true,
|
|
99
|
-
}),
|
|
100
|
+
{cloneChildren(children, (child: AnyObject) =>
|
|
101
|
+
cloneElement(child, {
|
|
102
|
+
"data-inner": true,
|
|
103
|
+
}),
|
|
100
104
|
)}
|
|
101
105
|
</div>
|
|
102
106
|
</FieldWrapper>
|
package/src/input/File.tsx
CHANGED
|
@@ -36,15 +36,13 @@ import {
|
|
|
36
36
|
useCallback,
|
|
37
37
|
useDeferredValue,
|
|
38
38
|
useEffect,
|
|
39
|
-
useMemo,
|
|
40
39
|
useRef,
|
|
41
40
|
useState,
|
|
42
41
|
type ChangeEvent,
|
|
43
42
|
} from "react";
|
|
44
43
|
import { InputWrapper } from "../internal/InputWrapper";
|
|
45
44
|
import { type InputProps } from "../types";
|
|
46
|
-
import {
|
|
47
|
-
import classes from "./File.module.css";
|
|
45
|
+
import { useHandleEnter } from "../utils";
|
|
48
46
|
|
|
49
47
|
export interface FileInputProps
|
|
50
48
|
extends InputProps<AnyObject>,
|
|
@@ -77,6 +75,15 @@ const FileExists = (prop: AnyObject) => {
|
|
|
77
75
|
);
|
|
78
76
|
};
|
|
79
77
|
|
|
78
|
+
/**
|
|
79
|
+
* File input component that allows users to select files from their system.
|
|
80
|
+
* It supports multiple file selection and displays a list of selected files.
|
|
81
|
+
* The component also provides functionality to remove files from the list.
|
|
82
|
+
* It handles file changes, invalid states, and read-only or disabled states.
|
|
83
|
+
*
|
|
84
|
+
* @param {FileInputProps} props - The properties for the FileInput component.
|
|
85
|
+
* @returns {JSX.Element} - The rendered FileInput component.
|
|
86
|
+
*/
|
|
80
87
|
export const FileInput = ({
|
|
81
88
|
ref,
|
|
82
89
|
value,
|
|
@@ -101,10 +108,7 @@ export const FileInput = ({
|
|
|
101
108
|
const [actualValue, setActualValue] = useState<File | File[] | null>();
|
|
102
109
|
const deferred = useDeferredValue(value);
|
|
103
110
|
|
|
104
|
-
const handleEnterPressed =
|
|
105
|
-
() => handleEnter(onEnterPressed),
|
|
106
|
-
[onEnterPressed],
|
|
107
|
-
);
|
|
111
|
+
const handleEnterPressed = useHandleEnter(onEnterPressed);
|
|
108
112
|
|
|
109
113
|
useEffect(() => {
|
|
110
114
|
setActualValue(deferred);
|
|
@@ -134,7 +138,7 @@ export const FileInput = ({
|
|
|
134
138
|
}
|
|
135
139
|
} else {
|
|
136
140
|
setActualValue(e?.target.files?.item(0));
|
|
137
|
-
onChange?.(e?.target.files?.item(0) ??
|
|
141
|
+
onChange?.(e?.target.files?.item(0) ?? null);
|
|
138
142
|
if (!e && inputRef.current) inputRef.current.value = "";
|
|
139
143
|
}
|
|
140
144
|
},
|
|
@@ -152,7 +156,7 @@ export const FileInput = ({
|
|
|
152
156
|
|
|
153
157
|
return (
|
|
154
158
|
<InputWrapper
|
|
155
|
-
showClear={!isEmpty(actualValue)}
|
|
159
|
+
showClear={!isEmpty(actualValue) && !disabled && !readOnly}
|
|
156
160
|
onClear={handleChange}
|
|
157
161
|
invalid={invalid}
|
|
158
162
|
readOnly={readOnly}
|
|
@@ -194,11 +198,9 @@ export const FileInput = ({
|
|
|
194
198
|
>
|
|
195
199
|
<input
|
|
196
200
|
className={classNames(
|
|
197
|
-
"appearance-none bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0",
|
|
198
|
-
|
|
199
|
-
isArray(actualValue) &&
|
|
200
|
-
actualValue?.length &&
|
|
201
|
-
"text-[var(--fabric-bg)]",
|
|
201
|
+
"appearance-none bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0 peer",
|
|
202
|
+
"fabric-fileInput",
|
|
203
|
+
isArray(actualValue) && actualValue?.length && "text-(--fabric-bg)",
|
|
202
204
|
)}
|
|
203
205
|
aria-invalid={invalid}
|
|
204
206
|
aria-disabled={disabled}
|
package/src/input/Hidden.tsx
CHANGED
|
@@ -33,14 +33,27 @@ interface Props {
|
|
|
33
33
|
* hidden value will fire change for form update
|
|
34
34
|
*/
|
|
35
35
|
hiddenValue?: AnyObject;
|
|
36
|
+
/**
|
|
37
|
+
* default avatar image
|
|
38
|
+
*/
|
|
39
|
+
defaultValue?: string;
|
|
36
40
|
/**
|
|
37
41
|
* form onChange handler
|
|
38
42
|
*/
|
|
39
43
|
onChange?: (value: AnyObject) => void;
|
|
40
44
|
}
|
|
41
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Hidden input component to handle hidden values in forms.
|
|
48
|
+
* It will trigger the onChange handler with the hidden value after a short delay.
|
|
49
|
+
* This is useful for cases where you need to update the form state without user interaction.
|
|
50
|
+
*
|
|
51
|
+
* @param {Object} props - Component properties.
|
|
52
|
+
* @returns {JSX.Element} The rendered hidden input field and error icon.
|
|
53
|
+
*/
|
|
42
54
|
export const HiddenInput = ({
|
|
43
55
|
hiddenValue,
|
|
56
|
+
defaultValue,
|
|
44
57
|
onChange,
|
|
45
58
|
// @ts-expect-error ignore
|
|
46
59
|
// eslint-disable-next-line react/prop-types
|
|
@@ -54,8 +67,10 @@ export const HiddenInput = ({
|
|
|
54
67
|
name,
|
|
55
68
|
}: Props) => {
|
|
56
69
|
useEffect(() => {
|
|
57
|
-
|
|
58
|
-
|
|
70
|
+
setTimeout(() => {
|
|
71
|
+
onChange?.(hiddenValue ?? defaultValue);
|
|
72
|
+
}, 50);
|
|
73
|
+
}, [hiddenValue, defaultValue]);
|
|
59
74
|
return (
|
|
60
75
|
<Fragment>
|
|
61
76
|
<input type="hidden" onChange={onChange} value={value} name={name} />
|
package/src/input/Input.tsx
CHANGED
|
@@ -22,17 +22,17 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
import { type RefProp } from "@react-fabric/core/dist/types/types";
|
|
25
|
+
import classNames from "classnames";
|
|
25
26
|
import {
|
|
26
27
|
useCallback,
|
|
27
28
|
useDeferredValue,
|
|
28
29
|
useEffect,
|
|
29
|
-
useMemo,
|
|
30
30
|
useState,
|
|
31
31
|
type ChangeEvent,
|
|
32
32
|
} from "react";
|
|
33
33
|
import { InputWrapper } from "../internal/InputWrapper";
|
|
34
34
|
import { type InputProps } from "../types";
|
|
35
|
-
import {
|
|
35
|
+
import { useHandleEnter } from "../utils";
|
|
36
36
|
|
|
37
37
|
export interface TextInputProps extends InputProps, RefProp<HTMLInputElement> {
|
|
38
38
|
/**
|
|
@@ -41,6 +41,14 @@ export interface TextInputProps extends InputProps, RefProp<HTMLInputElement> {
|
|
|
41
41
|
type?: "email" | "file" | "search" | "tel" | "text" | "url";
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* This component is used to render a text input field with various properties such as name, value, invalid state, read-only, disabled, required, placeholder, autoFocus, error message, monospace font option, and event handlers for blur, focus, change, and enter key press.
|
|
46
|
+
* It also includes a clear button that appears when there is a value in the input field, allowing users to clear the input easily.
|
|
47
|
+
* The component uses a wrapper to handle the clear button and styling, and it supports deferred value updates to optimize performance.
|
|
48
|
+
*
|
|
49
|
+
* @param {TextInputProps} props - The properties for the text input component.
|
|
50
|
+
* @returns {JSX.Element} - A JSX element representing the text input field.
|
|
51
|
+
*/
|
|
44
52
|
export const Input = ({
|
|
45
53
|
ref,
|
|
46
54
|
name,
|
|
@@ -50,20 +58,21 @@ export const Input = ({
|
|
|
50
58
|
disabled,
|
|
51
59
|
required,
|
|
52
60
|
placeholder,
|
|
61
|
+
defaultValue,
|
|
53
62
|
autoFocus,
|
|
54
63
|
error,
|
|
64
|
+
monospace,
|
|
55
65
|
type = "text",
|
|
66
|
+
onBlur,
|
|
67
|
+
onFocus,
|
|
56
68
|
onChange,
|
|
57
69
|
onEnterPressed,
|
|
58
70
|
...rest
|
|
59
71
|
}: TextInputProps) => {
|
|
60
72
|
const [actualValue, setActualValue] = useState("");
|
|
61
|
-
const deferred = useDeferredValue(value);
|
|
73
|
+
const deferred = useDeferredValue(value ?? defaultValue);
|
|
62
74
|
|
|
63
|
-
const handleEnterPressed =
|
|
64
|
-
() => handleEnter(onEnterPressed),
|
|
65
|
-
[onEnterPressed],
|
|
66
|
-
);
|
|
75
|
+
const handleEnterPressed = useHandleEnter(onEnterPressed);
|
|
67
76
|
|
|
68
77
|
useEffect(() => {
|
|
69
78
|
setActualValue(deferred ?? "");
|
|
@@ -71,16 +80,16 @@ export const Input = ({
|
|
|
71
80
|
|
|
72
81
|
const handleChange = useCallback(
|
|
73
82
|
(e?: ChangeEvent<HTMLInputElement>) => {
|
|
74
|
-
const value = e?.target.value;
|
|
83
|
+
const value = e?.target.value ?? defaultValue;
|
|
75
84
|
setActualValue(value ?? "");
|
|
76
|
-
onChange?.(value ??
|
|
85
|
+
onChange?.(value ?? null);
|
|
77
86
|
},
|
|
78
|
-
[
|
|
87
|
+
[onChange, defaultValue],
|
|
79
88
|
);
|
|
80
89
|
|
|
81
90
|
return (
|
|
82
91
|
<InputWrapper
|
|
83
|
-
showClear={!!actualValue}
|
|
92
|
+
showClear={!!actualValue && !disabled && !readOnly}
|
|
84
93
|
onClear={handleChange}
|
|
85
94
|
invalid={invalid}
|
|
86
95
|
readOnly={readOnly}
|
|
@@ -90,7 +99,10 @@ export const Input = ({
|
|
|
90
99
|
{...rest}
|
|
91
100
|
>
|
|
92
101
|
<input
|
|
93
|
-
className=
|
|
102
|
+
className={classNames(
|
|
103
|
+
"appearance-none bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0 peer",
|
|
104
|
+
monospace && "font-mono text-sm",
|
|
105
|
+
)}
|
|
94
106
|
aria-invalid={invalid}
|
|
95
107
|
aria-disabled={disabled}
|
|
96
108
|
aria-readonly={readOnly}
|
|
@@ -104,8 +116,10 @@ export const Input = ({
|
|
|
104
116
|
type={type}
|
|
105
117
|
ref={ref}
|
|
106
118
|
autoComplete="off"
|
|
107
|
-
{
|
|
119
|
+
autoFocus={autoFocus}
|
|
108
120
|
value={actualValue}
|
|
121
|
+
onBlur={onBlur}
|
|
122
|
+
onFocus={onFocus}
|
|
109
123
|
onChange={handleChange}
|
|
110
124
|
onKeyDown={handleEnterPressed}
|
|
111
125
|
/>
|
package/src/input/Number.tsx
CHANGED
|
@@ -26,13 +26,12 @@ import {
|
|
|
26
26
|
useCallback,
|
|
27
27
|
useDeferredValue,
|
|
28
28
|
useEffect,
|
|
29
|
-
useMemo,
|
|
30
29
|
useState,
|
|
31
30
|
type ChangeEvent,
|
|
32
31
|
} from "react";
|
|
33
32
|
import { InputWrapper } from "../internal/InputWrapper";
|
|
34
33
|
import { type InputProps } from "../types";
|
|
35
|
-
import {
|
|
34
|
+
import { useHandleEnter } from "../utils";
|
|
36
35
|
|
|
37
36
|
export interface NumberProps
|
|
38
37
|
extends InputProps<number>,
|
|
@@ -51,6 +50,14 @@ export interface NumberProps
|
|
|
51
50
|
step?: number;
|
|
52
51
|
}
|
|
53
52
|
|
|
53
|
+
/**
|
|
54
|
+
* This component allows users to input numeric values with optional minimum, maximum, and step constraints.
|
|
55
|
+
* It provides a clear interface for entering numbers, with support for validation and accessibility features.
|
|
56
|
+
* It also includes a clear button to reset the input value.
|
|
57
|
+
*
|
|
58
|
+
* @param {NumberProps} props - The properties for the Number input component.
|
|
59
|
+
* @returns {JSX.Element} The rendered Number input component.
|
|
60
|
+
*/
|
|
54
61
|
export const Number = ({
|
|
55
62
|
ref,
|
|
56
63
|
value,
|
|
@@ -59,22 +66,22 @@ export const Number = ({
|
|
|
59
66
|
disabled,
|
|
60
67
|
required,
|
|
61
68
|
placeholder,
|
|
69
|
+
defaultValue,
|
|
62
70
|
autoFocus,
|
|
63
71
|
name,
|
|
64
72
|
min,
|
|
65
73
|
max,
|
|
66
74
|
step,
|
|
75
|
+
onBlur,
|
|
76
|
+
onFocus,
|
|
67
77
|
onChange,
|
|
68
78
|
onEnterPressed,
|
|
69
79
|
...rest
|
|
70
80
|
}: NumberProps) => {
|
|
71
81
|
const [actualValue, setActualValue] = useState<"" | number>("");
|
|
72
|
-
const deferred = useDeferredValue(value);
|
|
82
|
+
const deferred = useDeferredValue(value ?? defaultValue);
|
|
73
83
|
|
|
74
|
-
const handleEnterPressed =
|
|
75
|
-
() => handleEnter(onEnterPressed),
|
|
76
|
-
[onEnterPressed],
|
|
77
|
-
);
|
|
84
|
+
const handleEnterPressed = useHandleEnter(onEnterPressed);
|
|
78
85
|
|
|
79
86
|
useEffect(() => {
|
|
80
87
|
setActualValue(deferred ?? "");
|
|
@@ -82,16 +89,16 @@ export const Number = ({
|
|
|
82
89
|
|
|
83
90
|
const handleChange = useCallback(
|
|
84
91
|
(e?: ChangeEvent<HTMLInputElement>) => {
|
|
85
|
-
const number = e?.target.valueAsNumber;
|
|
92
|
+
const number = e?.target.valueAsNumber ?? defaultValue;
|
|
86
93
|
setActualValue(number ?? "");
|
|
87
|
-
onChange?.(number ??
|
|
94
|
+
onChange?.(number ?? null);
|
|
88
95
|
},
|
|
89
|
-
[onChange],
|
|
96
|
+
[onChange, defaultValue],
|
|
90
97
|
);
|
|
91
98
|
|
|
92
99
|
return (
|
|
93
100
|
<InputWrapper
|
|
94
|
-
showClear={!!actualValue}
|
|
101
|
+
showClear={!!actualValue && !disabled && !readOnly}
|
|
95
102
|
onClear={handleChange}
|
|
96
103
|
invalid={invalid}
|
|
97
104
|
readOnly={readOnly}
|
|
@@ -100,7 +107,7 @@ export const Number = ({
|
|
|
100
107
|
{...rest}
|
|
101
108
|
>
|
|
102
109
|
<input
|
|
103
|
-
className="appearance-none bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0"
|
|
110
|
+
className="appearance-none bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0 peer"
|
|
104
111
|
aria-invalid={invalid}
|
|
105
112
|
aria-disabled={disabled}
|
|
106
113
|
aria-readonly={readOnly}
|
|
@@ -116,8 +123,10 @@ export const Number = ({
|
|
|
116
123
|
step={step}
|
|
117
124
|
ref={ref}
|
|
118
125
|
autoComplete="off"
|
|
119
|
-
{
|
|
126
|
+
autoFocus={autoFocus}
|
|
120
127
|
value={actualValue}
|
|
128
|
+
onBlur={onBlur}
|
|
129
|
+
onFocus={onFocus}
|
|
121
130
|
onChange={handleChange}
|
|
122
131
|
onKeyDown={handleEnterPressed}
|
|
123
132
|
/>
|