@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.
Files changed (187) hide show
  1. package/{src/input/ArrayInput.module.css → css/_arrayInput.css} +8 -8
  2. package/css/_colorPicker.css +65 -0
  3. package/{src/internal/Field.module.css → css/_field.css} +66 -30
  4. package/{src/input/File.module.css → css/_file.css} +21 -6
  5. package/css/styles.css +27 -0
  6. package/dist/cjs/builder/FileField.js +40 -28
  7. package/dist/cjs/builder/FormField.js +50 -34
  8. package/dist/cjs/builder/SchemaEditor.js +72 -0
  9. package/dist/cjs/builder/components/AddSchemaFields.js +62 -0
  10. package/dist/cjs/builder/components/SchemaArrayForm.js +145 -0
  11. package/dist/cjs/builder/components/SchemaField.js +414 -0
  12. package/dist/cjs/form/Controller.js +46 -1
  13. package/dist/cjs/form/Form.js +70 -9
  14. package/dist/cjs/hooks/useFileUploader.js +16 -0
  15. package/dist/cjs/hooks/useFormBuilder.js +57 -15
  16. package/dist/cjs/i18n/ar.json.js +5 -4
  17. package/dist/cjs/i18n/en.json.js +5 -5
  18. package/dist/cjs/index.js +12 -2
  19. package/dist/cjs/input/ArrayInput.js +126 -36
  20. package/dist/cjs/input/AvatarInput.js +43 -23
  21. package/dist/cjs/input/Checkbox.js +16 -6
  22. package/dist/cjs/input/ColorInput.js +113 -58
  23. package/dist/cjs/input/ColorPicker.js +60 -76
  24. package/dist/cjs/input/DateInput.js +197 -0
  25. package/dist/cjs/input/Field.js +10 -4
  26. package/dist/cjs/input/File.js +14 -7
  27. package/dist/cjs/input/Hidden.js +13 -2
  28. package/dist/cjs/input/Input.js +24 -10
  29. package/dist/cjs/input/Number.js +22 -10
  30. package/dist/cjs/input/Password.js +22 -12
  31. package/dist/cjs/input/Radio.js +12 -3
  32. package/dist/cjs/input/RadioGroup.js +10 -1
  33. package/dist/cjs/input/{Range.js → RangeSlider.js} +16 -8
  34. package/dist/cjs/input/Search.js +30 -19
  35. package/dist/cjs/input/Slider.js +12 -6
  36. package/dist/cjs/input/Switch.js +20 -9
  37. package/dist/cjs/input/Textarea.js +29 -8
  38. package/dist/cjs/internal/ErrorIcon.js +1 -1
  39. package/dist/cjs/internal/FieldWrapper.js +11 -4
  40. package/dist/cjs/internal/InputWrapper.js +27 -20
  41. package/dist/cjs/select/AutoComplete.js +21 -11
  42. package/dist/cjs/select/DualList.js +34 -12
  43. package/dist/cjs/select/List.js +68 -22
  44. package/dist/cjs/select/Options.js +49 -42
  45. package/dist/cjs/select/Select.js +40 -10
  46. package/dist/cjs/select/useSelect.js +22 -11
  47. package/dist/cjs/types/schema.js +43 -0
  48. package/dist/cjs/utils/eventHandlers.js +15 -5
  49. package/dist/esm/builder/FileField.js +42 -30
  50. package/dist/esm/builder/FormField.js +50 -34
  51. package/dist/esm/builder/SchemaEditor.js +72 -0
  52. package/dist/esm/builder/components/AddSchemaFields.js +62 -0
  53. package/dist/esm/builder/components/SchemaArrayForm.js +145 -0
  54. package/dist/esm/builder/components/SchemaField.js +414 -0
  55. package/dist/esm/form/Controller.js +46 -1
  56. package/dist/esm/form/Form.js +72 -12
  57. package/dist/esm/hooks/useFileUploader.js +16 -0
  58. package/dist/esm/hooks/useFormBuilder.js +55 -13
  59. package/dist/esm/i18n/ar.json.js +5 -4
  60. package/dist/esm/i18n/en.json.js +5 -5
  61. package/dist/esm/index.js +6 -2
  62. package/dist/esm/input/ArrayInput.js +126 -36
  63. package/dist/esm/input/AvatarInput.js +44 -24
  64. package/dist/esm/input/Checkbox.js +16 -6
  65. package/dist/esm/input/ColorInput.js +115 -60
  66. package/dist/esm/input/ColorPicker.js +60 -76
  67. package/dist/esm/input/DateInput.js +197 -0
  68. package/dist/esm/input/Field.js +11 -5
  69. package/dist/esm/input/File.js +16 -9
  70. package/dist/esm/input/Hidden.js +13 -2
  71. package/dist/esm/input/Input.js +26 -12
  72. package/dist/esm/input/Number.js +24 -12
  73. package/dist/esm/input/Password.js +24 -14
  74. package/dist/esm/input/Radio.js +12 -3
  75. package/dist/esm/input/RadioGroup.js +11 -2
  76. package/dist/esm/input/{Range.js → RangeSlider.js} +16 -8
  77. package/dist/esm/input/Search.js +32 -21
  78. package/dist/esm/input/Slider.js +12 -6
  79. package/dist/esm/input/Switch.js +20 -9
  80. package/dist/esm/input/Textarea.js +30 -9
  81. package/dist/esm/internal/ErrorIcon.js +1 -1
  82. package/dist/esm/internal/FieldWrapper.js +11 -4
  83. package/dist/esm/internal/InputWrapper.js +27 -20
  84. package/dist/esm/select/AutoComplete.js +21 -11
  85. package/dist/esm/select/DualList.js +34 -12
  86. package/dist/esm/select/List.js +69 -23
  87. package/dist/esm/select/Options.js +49 -42
  88. package/dist/esm/select/Select.js +40 -10
  89. package/dist/esm/select/useSelect.js +23 -12
  90. package/{src/input/ColorPicker.module.css → dist/esm/types/schema.js} +18 -27
  91. package/dist/esm/utils/eventHandlers.js +15 -5
  92. package/dist/types/builder/FileField.d.ts +4 -2
  93. package/dist/types/builder/FormField.d.ts +3 -1
  94. package/dist/types/builder/SchemaEditor.d.ts +9 -0
  95. package/dist/types/builder/components/AddSchemaFields.d.ts +2 -0
  96. package/dist/types/builder/components/SchemaArrayForm.d.ts +2 -0
  97. package/dist/types/builder/components/SchemaField.d.ts +10 -0
  98. package/dist/types/form/Controller.d.ts +47 -2
  99. package/dist/types/form/Form.d.ts +48 -2
  100. package/dist/types/hooks/useFileUploader.d.ts +16 -0
  101. package/dist/types/hooks/useFormBuilder.d.ts +32 -1
  102. package/dist/types/index.d.ts +6 -3
  103. package/dist/types/input/ArrayInput.d.ts +69 -12
  104. package/dist/types/input/AvatarInput.d.ts +23 -3
  105. package/dist/types/input/Checkbox.d.ts +13 -4
  106. package/dist/types/input/ColorInput.d.ts +15 -2
  107. package/dist/types/input/ColorPicker.d.ts +11 -1
  108. package/dist/types/input/DateInput.d.ts +28 -0
  109. package/dist/types/input/Field.d.ts +5 -1
  110. package/dist/types/input/File.d.ts +9 -0
  111. package/dist/types/input/Hidden.d.ts +13 -1
  112. package/dist/types/input/Input.d.ts +9 -1
  113. package/dist/types/input/Number.d.ts +9 -1
  114. package/dist/types/input/Password.d.ts +7 -1
  115. package/dist/types/input/Radio.d.ts +9 -1
  116. package/dist/types/input/RadioGroup.d.ts +8 -1
  117. package/dist/types/input/RangeSlider.d.ts +67 -0
  118. package/dist/types/input/Search.d.ts +14 -1
  119. package/dist/types/input/Slider.d.ts +8 -2
  120. package/dist/types/input/Switch.d.ts +10 -2
  121. package/dist/types/input/Textarea.d.ts +15 -1
  122. package/dist/types/internal/FieldWrapper.d.ts +1 -1
  123. package/dist/types/internal/InputWrapper.d.ts +1 -1
  124. package/dist/types/ranger/index.d.ts +2 -2
  125. package/dist/types/select/AutoComplete.d.ts +8 -1
  126. package/dist/types/select/DualList.d.ts +17 -2
  127. package/dist/types/select/List.d.ts +9 -1
  128. package/dist/types/select/Options.d.ts +4 -3
  129. package/dist/types/select/Select.d.ts +9 -1
  130. package/dist/types/select/useSelect.d.ts +10 -6
  131. package/dist/types/types/index.d.ts +34 -5
  132. package/dist/types/types/schema.d.ts +78 -33
  133. package/dist/types/utils/eventHandlers.d.ts +2 -2
  134. package/package.json +22 -20
  135. package/src/builder/FileField.tsx +62 -30
  136. package/src/builder/FormField.tsx +52 -32
  137. package/src/builder/SchemaEditor.tsx +79 -0
  138. package/src/builder/components/AddSchemaFields.tsx +74 -0
  139. package/src/builder/components/SchemaArrayForm.tsx +161 -0
  140. package/src/builder/components/SchemaField.tsx +436 -0
  141. package/src/form/Controller.tsx +48 -3
  142. package/src/form/Form.tsx +80 -8
  143. package/src/hooks/useFileUploader.ts +16 -0
  144. package/src/hooks/useFormBuilder.tsx +58 -15
  145. package/src/i18n/ar.json +5 -4
  146. package/src/i18n/en.json +5 -5
  147. package/src/index.ts +12 -3
  148. package/src/input/ArrayInput.tsx +199 -64
  149. package/src/input/AvatarInput.tsx +67 -19
  150. package/src/input/Checkbox.tsx +16 -6
  151. package/src/input/ColorInput.tsx +131 -71
  152. package/src/input/ColorPicker.tsx +54 -72
  153. package/src/input/DateInput.tsx +260 -0
  154. package/src/input/Field.tsx +15 -11
  155. package/src/input/File.tsx +16 -14
  156. package/src/input/Hidden.tsx +17 -2
  157. package/src/input/Input.tsx +27 -13
  158. package/src/input/Number.tsx +22 -13
  159. package/src/input/Password.tsx +24 -16
  160. package/src/input/Radio.tsx +12 -3
  161. package/src/input/RadioGroup.tsx +12 -3
  162. package/src/input/{Range.tsx → RangeSlider.tsx} +17 -9
  163. package/src/input/Search.tsx +33 -22
  164. package/src/input/Slider.tsx +20 -11
  165. package/src/input/Switch.tsx +17 -6
  166. package/src/input/Textarea.tsx +34 -9
  167. package/src/internal/ErrorIcon.tsx +1 -1
  168. package/src/internal/FieldWrapper.tsx +8 -4
  169. package/src/internal/InputWrapper.tsx +49 -27
  170. package/src/select/AutoComplete.tsx +14 -3
  171. package/src/select/DualList.tsx +31 -16
  172. package/src/select/List.tsx +52 -8
  173. package/src/select/Options.tsx +50 -46
  174. package/src/select/Select.tsx +25 -5
  175. package/src/select/useSelect.tsx +26 -9
  176. package/src/types/index.ts +37 -6
  177. package/src/types/schema.ts +96 -41
  178. package/src/utils/eventHandlers.ts +14 -4
  179. package/dist/cjs/input/ArrayInput.module.css +0 -1
  180. package/dist/cjs/input/ColorPicker.module.css +0 -1
  181. package/dist/cjs/input/File.module.css +0 -1
  182. package/dist/cjs/internal/Field.module.css +0 -1
  183. package/dist/esm/input/ArrayInput.module.css +0 -1
  184. package/dist/esm/input/ColorPicker.module.css +0 -1
  185. package/dist/esm/input/File.module.css +0 -1
  186. package/dist/esm/internal/Field.module.css +0 -1
  187. 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
+ };
@@ -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 { Children, cloneElement } from "react";
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
- classes.fieldGroup,
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 flex-nowrap",
96
+ !vertical && "flex",
91
97
  )}
98
+ data-vertical={!!vertical}
92
99
  >
93
- {Children.map(
94
- children,
95
- (child) =>
96
- child &&
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>
@@ -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 { handleEnter } from "../utils";
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 = useMemo(
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) ?? undefined);
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
- classes.fileInput,
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}
@@ -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
- onChange?.(hiddenValue);
58
- }, [hiddenValue]);
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} />
@@ -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 { handleEnter } from "../utils";
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 = useMemo(
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 ?? undefined);
85
+ onChange?.(value ?? null);
77
86
  },
78
- [onchange],
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="appearance-none bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0 peer"
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
- {...{ autoFocus }}
119
+ autoFocus={autoFocus}
108
120
  value={actualValue}
121
+ onBlur={onBlur}
122
+ onFocus={onFocus}
109
123
  onChange={handleChange}
110
124
  onKeyDown={handleEnterPressed}
111
125
  />
@@ -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 { handleEnter } from "../utils";
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 = useMemo(
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 ?? undefined);
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
- {...{ autoFocus }}
126
+ autoFocus={autoFocus}
120
127
  value={actualValue}
128
+ onBlur={onBlur}
129
+ onFocus={onFocus}
121
130
  onChange={handleChange}
122
131
  onKeyDown={handleEnterPressed}
123
132
  />