@react-fabric/form 1.0.0-beta.5 → 1.0.0-beta.51

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 (174) 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} +64 -29
  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 +37 -25
  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 +143 -0
  11. package/dist/cjs/builder/components/SchemaField.js +414 -0
  12. package/dist/cjs/form/Controller.js +1 -1
  13. package/dist/cjs/form/Form.js +11 -6
  14. package/dist/cjs/hooks/useFormBuilder.js +27 -15
  15. package/dist/cjs/index.js +11 -2
  16. package/dist/cjs/input/ArrayInput.js +61 -33
  17. package/dist/cjs/input/AvatarInput.js +9 -8
  18. package/dist/cjs/input/Checkbox.js +6 -5
  19. package/dist/cjs/input/ColorInput.js +95 -56
  20. package/dist/cjs/input/ColorPicker.js +1 -35
  21. package/dist/cjs/input/DateInput.js +186 -0
  22. package/dist/cjs/input/Field.js +5 -4
  23. package/dist/cjs/input/File.js +4 -7
  24. package/dist/cjs/input/Hidden.js +3 -1
  25. package/dist/cjs/input/Input.js +11 -7
  26. package/dist/cjs/input/Number.js +9 -7
  27. package/dist/cjs/input/Password.js +10 -8
  28. package/dist/cjs/input/Radio.js +2 -2
  29. package/dist/cjs/input/RadioGroup.js +6 -3
  30. package/dist/cjs/input/{Range.js → RangeSlider.js} +5 -5
  31. package/dist/cjs/input/Search.js +16 -16
  32. package/dist/cjs/input/Slider.js +3 -3
  33. package/dist/cjs/input/Switch.js +30 -7
  34. package/dist/cjs/input/Textarea.js +13 -4
  35. package/dist/cjs/internal/ErrorIcon.js +1 -1
  36. package/dist/cjs/internal/FieldWrapper.js +11 -4
  37. package/dist/cjs/internal/InputWrapper.js +19 -16
  38. package/dist/cjs/select/AutoComplete.js +12 -8
  39. package/dist/cjs/select/DualList.js +4 -1
  40. package/dist/cjs/select/List.js +45 -20
  41. package/dist/cjs/select/Options.js +2 -2
  42. package/dist/cjs/select/Select.js +14 -4
  43. package/dist/cjs/select/useSelect.js +10 -6
  44. package/dist/cjs/types/schema.js +43 -0
  45. package/dist/cjs/utils/eventHandlers.js +11 -5
  46. package/dist/cjs/utils/getColor.js +6 -0
  47. package/dist/esm/builder/FileField.js +39 -27
  48. package/dist/esm/builder/FormField.js +50 -34
  49. package/dist/esm/builder/SchemaEditor.js +72 -0
  50. package/dist/esm/builder/components/AddSchemaFields.js +62 -0
  51. package/dist/esm/builder/components/SchemaArrayForm.js +143 -0
  52. package/dist/esm/builder/components/SchemaField.js +414 -0
  53. package/dist/esm/form/Controller.js +1 -1
  54. package/dist/esm/form/Form.js +11 -6
  55. package/dist/esm/hooks/useFormBuilder.js +25 -13
  56. package/dist/esm/index.js +5 -1
  57. package/dist/esm/input/ArrayInput.js +61 -33
  58. package/dist/esm/input/AvatarInput.js +9 -8
  59. package/dist/esm/input/Checkbox.js +6 -5
  60. package/dist/esm/input/ColorInput.js +97 -58
  61. package/dist/esm/input/ColorPicker.js +1 -35
  62. package/dist/esm/input/DateInput.js +186 -0
  63. package/dist/esm/input/Field.js +6 -5
  64. package/dist/esm/input/File.js +6 -9
  65. package/dist/esm/input/Hidden.js +3 -1
  66. package/dist/esm/input/Input.js +13 -9
  67. package/dist/esm/input/Number.js +11 -9
  68. package/dist/esm/input/Password.js +12 -10
  69. package/dist/esm/input/Radio.js +2 -2
  70. package/dist/esm/input/RadioGroup.js +7 -4
  71. package/dist/esm/input/{Range.js → RangeSlider.js} +5 -5
  72. package/dist/esm/input/Search.js +18 -18
  73. package/dist/esm/input/Slider.js +3 -3
  74. package/dist/esm/input/Switch.js +32 -9
  75. package/dist/esm/input/Textarea.js +14 -5
  76. package/dist/esm/internal/ErrorIcon.js +1 -1
  77. package/dist/esm/internal/FieldWrapper.js +11 -4
  78. package/dist/esm/internal/InputWrapper.js +19 -16
  79. package/dist/esm/select/AutoComplete.js +12 -8
  80. package/dist/esm/select/DualList.js +4 -1
  81. package/dist/esm/select/List.js +46 -21
  82. package/dist/esm/select/Options.js +2 -2
  83. package/dist/esm/select/Select.js +14 -4
  84. package/dist/esm/select/useSelect.js +11 -7
  85. package/{src/input/ColorPicker.module.css → dist/esm/types/schema.js} +18 -27
  86. package/dist/esm/utils/eventHandlers.js +11 -5
  87. package/dist/esm/utils/getColor.js +6 -1
  88. package/dist/types/builder/FileField.d.ts +4 -2
  89. package/dist/types/builder/FormField.d.ts +3 -1
  90. package/dist/types/builder/SchemaEditor.d.ts +2 -0
  91. package/dist/types/builder/components/AddSchemaFields.d.ts +2 -0
  92. package/dist/types/builder/components/SchemaArrayForm.d.ts +2 -0
  93. package/dist/types/builder/components/SchemaField.d.ts +10 -0
  94. package/dist/types/form/Controller.d.ts +2 -2
  95. package/dist/types/form/Form.d.ts +5 -1
  96. package/dist/types/hooks/useFormBuilder.d.ts +3 -1
  97. package/dist/types/index.d.ts +5 -2
  98. package/dist/types/input/ArrayInput.d.ts +39 -6
  99. package/dist/types/input/AvatarInput.d.ts +6 -3
  100. package/dist/types/input/Checkbox.d.ts +4 -4
  101. package/dist/types/input/ColorInput.d.ts +2 -2
  102. package/dist/types/input/ColorPicker.d.ts +1 -1
  103. package/dist/types/input/DateInput.d.ts +19 -0
  104. package/dist/types/input/Field.d.ts +0 -1
  105. package/dist/types/input/Input.d.ts +1 -1
  106. package/dist/types/input/Number.d.ts +1 -1
  107. package/dist/types/input/Password.d.ts +1 -1
  108. package/dist/types/input/Radio.d.ts +0 -1
  109. package/dist/types/input/RadioGroup.d.ts +18 -3
  110. package/dist/types/input/{Range.d.ts → RangeSlider.d.ts} +1 -1
  111. package/dist/types/input/Search.d.ts +5 -1
  112. package/dist/types/input/Switch.d.ts +7 -4
  113. package/dist/types/input/Textarea.d.ts +5 -1
  114. package/dist/types/internal/FieldWrapper.d.ts +1 -1
  115. package/dist/types/internal/InputWrapper.d.ts +1 -1
  116. package/dist/types/ranger/index.d.ts +2 -2
  117. package/dist/types/select/AutoComplete.d.ts +1 -1
  118. package/dist/types/select/DualList.d.ts +6 -2
  119. package/dist/types/select/List.d.ts +1 -1
  120. package/dist/types/select/Select.d.ts +1 -1
  121. package/dist/types/select/useSelect.d.ts +7 -15
  122. package/dist/types/types/index.d.ts +28 -7
  123. package/dist/types/types/schema.d.ts +78 -33
  124. package/dist/types/utils/eventHandlers.d.ts +1 -1
  125. package/package.json +15 -12
  126. package/src/builder/FileField.tsx +53 -27
  127. package/src/builder/FormField.tsx +52 -32
  128. package/src/builder/SchemaEditor.tsx +72 -0
  129. package/src/builder/components/AddSchemaFields.tsx +74 -0
  130. package/src/builder/components/SchemaArrayForm.tsx +158 -0
  131. package/src/builder/components/SchemaField.tsx +436 -0
  132. package/src/form/Controller.tsx +3 -3
  133. package/src/form/Form.tsx +14 -5
  134. package/src/hooks/useFormBuilder.tsx +29 -15
  135. package/src/index.ts +11 -2
  136. package/src/input/ArrayInput.tsx +130 -54
  137. package/src/input/AvatarInput.tsx +12 -8
  138. package/src/input/Checkbox.tsx +6 -5
  139. package/src/input/ColorInput.tsx +113 -69
  140. package/src/input/ColorPicker.tsx +2 -36
  141. package/src/input/DateInput.tsx +250 -0
  142. package/src/input/Field.tsx +10 -11
  143. package/src/input/File.tsx +7 -14
  144. package/src/input/Hidden.tsx +4 -1
  145. package/src/input/Input.tsx +15 -10
  146. package/src/input/Number.tsx +10 -10
  147. package/src/input/Password.tsx +13 -12
  148. package/src/input/Radio.tsx +2 -2
  149. package/src/input/RadioGroup.tsx +27 -4
  150. package/src/input/{Range.tsx → RangeSlider.tsx} +4 -4
  151. package/src/input/Search.tsx +20 -19
  152. package/src/input/Slider.tsx +3 -3
  153. package/src/input/Switch.tsx +43 -7
  154. package/src/input/Textarea.tsx +19 -5
  155. package/src/internal/ErrorIcon.tsx +1 -1
  156. package/src/internal/FieldWrapper.tsx +8 -4
  157. package/src/internal/InputWrapper.tsx +36 -20
  158. package/src/select/AutoComplete.tsx +5 -1
  159. package/src/select/DualList.tsx +8 -0
  160. package/src/select/List.tsx +41 -8
  161. package/src/select/Options.tsx +1 -1
  162. package/src/select/Select.tsx +13 -3
  163. package/src/select/useSelect.tsx +13 -5
  164. package/src/types/index.ts +34 -8
  165. package/src/types/schema.ts +96 -41
  166. package/src/utils/eventHandlers.ts +9 -3
  167. package/dist/cjs/input/ArrayInput.module.css +0 -1
  168. package/dist/cjs/input/ColorPicker.module.css +0 -1
  169. package/dist/cjs/input/File.module.css +0 -1
  170. package/dist/cjs/internal/Field.module.css +0 -1
  171. package/dist/esm/input/ArrayInput.module.css +0 -1
  172. package/dist/esm/input/ColorPicker.module.css +0 -1
  173. package/dist/esm/input/File.module.css +0 -1
  174. package/dist/esm/internal/Field.module.css +0 -1
@@ -32,7 +32,6 @@ import {
32
32
  type FC,
33
33
  } from "react";
34
34
  import { SketchPicker } from "react-color";
35
- import classes from "./ColorPicker.module.css";
36
35
 
37
36
  export interface ColorProps {
38
37
  /**
@@ -50,7 +49,7 @@ export interface ColorProps {
50
49
  /**
51
50
  * color value
52
51
  */
53
- value?: string;
52
+ value?: string | null;
54
53
  /**
55
54
  * change handler
56
55
  */
@@ -130,17 +129,14 @@ export const ColorPicker: FC<ColorProps> = memo(
130
129
  return (
131
130
  <SketchPicker
132
131
  width="16rem"
133
- className={classes.colorPicker}
132
+ className={"fabric-colorPicker"}
134
133
  styles={
135
134
  {
136
135
  default: {
137
136
  hue: {
138
- borderRadius: "99px",
139
137
  boxShadow: "0 0 1px 1px var(--color-shadow)",
140
138
  },
141
139
  alpha: {
142
- height: "1rem",
143
- borderRadius: "99px",
144
140
  backgroundColor: "var(--fabric-bg)",
145
141
  boxShadow: "0 0 1px 1px var(--color-shadow)",
146
142
  },
@@ -158,36 +154,6 @@ export const ColorPicker: FC<ColorProps> = memo(
158
154
  boxShadow:
159
155
  "0 0 0 1px var(--color-shadow), 0 0 8px var(--fabric-shadow)",
160
156
  },
161
- Alpha: {
162
- alpha: {
163
- paddingRight: "calc(1rem - 2px)",
164
- },
165
- slider: {
166
- width: "calc(1rem - 2px)",
167
- borderRadius: "99px",
168
- height: "calc(1rem - 2px)",
169
- transform: "none",
170
- },
171
- },
172
- Hue: {
173
- hue: {
174
- paddingRight: "calc(1rem - 2px)",
175
- },
176
- slider: {
177
- width: "calc(1rem - 2px)",
178
- borderRadius: "99px",
179
- height: "calc(1rem - 2px)",
180
- transform: "none",
181
- },
182
- },
183
- Saturation: {
184
- circle: {
185
- width: "0.5rem",
186
- borderRadius: "99px",
187
- height: "0.5rem",
188
- transform: "translate(-0.25rem, -0.25rem)",
189
- },
190
- },
191
157
  checkboard: {
192
158
  white: "transparent",
193
159
  black: "red",
@@ -0,0 +1,250 @@
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
+ export const DateInput = ({
74
+ ref,
75
+ name,
76
+ value,
77
+ invalid,
78
+ readOnly,
79
+ disabled,
80
+ required,
81
+ placeholder,
82
+ autoFocus,
83
+ error,
84
+ type = "date",
85
+ onBlur,
86
+ onFocus,
87
+ onChange,
88
+ onEnterPressed,
89
+ format = "dd/MM/yyyy",
90
+ min,
91
+ max,
92
+ ...rest
93
+ }: DateProps) => {
94
+ const [actualValue, setActualValue] = useState("");
95
+ const [inputValue, setInputValue] = useState("");
96
+ const deferred = useDeferredValue(value);
97
+
98
+ const inputFormat = useMemo(
99
+ () => (type === "datetime" ? "yyyy-MM-dd HH:mm" : "yyyy-MM-dd"),
100
+ [type],
101
+ );
102
+
103
+ useEffect(() => {
104
+ setActualValue(deferred ?? "");
105
+ setInputValue(deferred ? DateUtil.format(deferred, inputFormat) : "");
106
+ }, [deferred, inputFormat]);
107
+
108
+ const handleEnterPressed = useHandleEnter(onEnterPressed);
109
+
110
+ const handleChange = useCallback(
111
+ (e?: ChangeEvent<HTMLInputElement>) => {
112
+ const value = e?.currentTarget.value;
113
+ if (value) {
114
+ const parsed = DateUtil.parseDate(value);
115
+ setActualValue(parsed?.toISOString?.());
116
+ setInputValue(DateUtil.format(parsed, inputFormat));
117
+ onChange != null &&
118
+ startTransition(() => onChange(parsed?.toISOString?.()));
119
+ } else {
120
+ setActualValue("");
121
+ setInputValue("");
122
+ onChange != null && startTransition(() => onChange(null));
123
+ }
124
+ },
125
+ [onChange, inputFormat],
126
+ );
127
+
128
+ const handleDateChange = useCallback(
129
+ (value?: string) => {
130
+ if (value) {
131
+ const parsed = DateUtil.parseDate(value);
132
+ setActualValue(parsed?.toISOString?.());
133
+ setInputValue(DateUtil.format(parsed, inputFormat));
134
+ onChange != null &&
135
+ startTransition(() => onChange(parsed?.toISOString?.()));
136
+ } else {
137
+ setActualValue("");
138
+ setInputValue("");
139
+ onChange != null && startTransition(() => onChange(null));
140
+ }
141
+ },
142
+ [onChange, inputFormat],
143
+ );
144
+
145
+ const arrowRef = useRef(null);
146
+ const [isOpen, setIsOpen] = useState(false);
147
+ const { refs, floatingStyles, context } = useFloating({
148
+ open: isOpen,
149
+ onOpenChange: (open, _, reason) => {
150
+ console.log(reason);
151
+ if (reason === "reference-press") return;
152
+ setIsOpen(open);
153
+ },
154
+ strategy: "fixed",
155
+ placement: "bottom-start",
156
+ whileElementsMounted: autoUpdate,
157
+ middleware: [
158
+ shift({ padding: 8 }),
159
+ flip(),
160
+ offset(9),
161
+ arrow({
162
+ element: arrowRef,
163
+ }),
164
+ ],
165
+ });
166
+
167
+ const focus = useFocus(context, {
168
+ enabled: !disabled,
169
+ });
170
+ const click = useClick(context, {
171
+ enabled: !disabled,
172
+ toggle: false,
173
+ });
174
+ const dismiss = useDismiss(context, {
175
+ referencePress: true,
176
+ });
177
+
178
+ const { getReferenceProps, getFloatingProps } = useInteractions([
179
+ dismiss,
180
+ click,
181
+ focus,
182
+ ]);
183
+
184
+ return (
185
+ <InputWrapper
186
+ showClear={!!actualValue && !disabled && !readOnly}
187
+ onClear={handleChange}
188
+ invalid={invalid}
189
+ readOnly={readOnly}
190
+ disabled={disabled}
191
+ required={required}
192
+ error={error}
193
+ {...rest}
194
+ >
195
+ <input
196
+ className={classNames(
197
+ "appearance-none bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0 peer",
198
+ !value && "empty",
199
+ )}
200
+ aria-invalid={invalid}
201
+ aria-disabled={disabled}
202
+ aria-readonly={readOnly}
203
+ aria-errormessage={error}
204
+ placeholder={placeholder}
205
+ disabled={disabled}
206
+ readOnly={readOnly}
207
+ data-testid={name}
208
+ name={name}
209
+ size={1}
210
+ type={type === "datetime" ? "datetime-local" : "date"}
211
+ autoComplete="off"
212
+ autoFocus={autoFocus}
213
+ value={inputValue}
214
+ ref={mergeRefs(ref, refs.setReference)}
215
+ {...getReferenceProps({
216
+ onBlur,
217
+ onFocus,
218
+ })}
219
+ onChange={handleChange}
220
+ onKeyDown={handleEnterPressed}
221
+ />
222
+ {isOpen && (
223
+ <div
224
+ ref={refs.setFloating}
225
+ style={{
226
+ zIndex: "var(--z-popover)",
227
+ ...floatingStyles,
228
+ }}
229
+ className="shadow-lg bg-base ring-1 ring-tint-100"
230
+ data-ref="dropdownBody"
231
+ {...getFloatingProps()}
232
+ >
233
+ <DatePanel
234
+ withTime={type === "datetime"}
235
+ value={actualValue}
236
+ max={max}
237
+ min={min}
238
+ onChange={handleDateChange}
239
+ />
240
+ <FloatingArrow
241
+ ref={arrowRef}
242
+ context={context}
243
+ strokeWidth={0.5}
244
+ className="fill-base stroke-muted"
245
+ />
246
+ </div>
247
+ )}
248
+ </InputWrapper>
249
+ );
250
+ };
@@ -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 {
@@ -83,20 +83,19 @@ export const Field = ({
83
83
  <FieldWrapper {...rest}>
84
84
  <div
85
85
  className={classNames(
86
- classes.fieldGroup,
86
+ "fabric-fieldGroup",
87
87
  !plain && !noOutline && "outline",
88
88
  !plain && "bg-base",
89
+ plain && "flex-wrap",
89
90
  "flex-1 rounded",
90
- !vertical && "flex flex-nowrap",
91
+ !vertical && "flex",
91
92
  )}
93
+ data-vertical={!!vertical}
92
94
  >
93
- {Children.map(
94
- children,
95
- (child) =>
96
- child &&
97
- cloneElement(child as AnyObject, {
98
- "data-inner": true,
99
- }),
95
+ {cloneChildren(children, (child: AnyObject) =>
96
+ cloneElement(child, {
97
+ "data-inner": true,
98
+ }),
100
99
  )}
101
100
  </div>
102
101
  </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>,
@@ -101,10 +99,7 @@ export const FileInput = ({
101
99
  const [actualValue, setActualValue] = useState<File | File[] | null>();
102
100
  const deferred = useDeferredValue(value);
103
101
 
104
- const handleEnterPressed = useMemo(
105
- () => handleEnter(onEnterPressed),
106
- [onEnterPressed],
107
- );
102
+ const handleEnterPressed = useHandleEnter(onEnterPressed);
108
103
 
109
104
  useEffect(() => {
110
105
  setActualValue(deferred);
@@ -134,7 +129,7 @@ export const FileInput = ({
134
129
  }
135
130
  } else {
136
131
  setActualValue(e?.target.files?.item(0));
137
- onChange?.(e?.target.files?.item(0) ?? undefined);
132
+ onChange?.(e?.target.files?.item(0) ?? null);
138
133
  if (!e && inputRef.current) inputRef.current.value = "";
139
134
  }
140
135
  },
@@ -152,7 +147,7 @@ export const FileInput = ({
152
147
 
153
148
  return (
154
149
  <InputWrapper
155
- showClear={!isEmpty(actualValue)}
150
+ showClear={!isEmpty(actualValue) && !disabled && !readOnly}
156
151
  onClear={handleChange}
157
152
  invalid={invalid}
158
153
  readOnly={readOnly}
@@ -194,11 +189,9 @@ export const FileInput = ({
194
189
  >
195
190
  <input
196
191
  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)]",
192
+ "appearance-none bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0 peer",
193
+ "fabric-fileInput",
194
+ isArray(actualValue) && actualValue?.length && "text-(--fabric-bg)",
202
195
  )}
203
196
  aria-invalid={invalid}
204
197
  aria-disabled={disabled}
@@ -54,7 +54,10 @@ export const HiddenInput = ({
54
54
  name,
55
55
  }: Props) => {
56
56
  useEffect(() => {
57
- onChange?.(hiddenValue);
57
+ hiddenValue &&
58
+ setTimeout(() => {
59
+ onChange?.(hiddenValue);
60
+ }, 50);
58
61
  }, [hiddenValue]);
59
62
  return (
60
63
  <Fragment>
@@ -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
  /**
@@ -52,7 +52,10 @@ export const Input = ({
52
52
  placeholder,
53
53
  autoFocus,
54
54
  error,
55
+ monospace,
55
56
  type = "text",
57
+ onBlur,
58
+ onFocus,
56
59
  onChange,
57
60
  onEnterPressed,
58
61
  ...rest
@@ -60,10 +63,7 @@ export const Input = ({
60
63
  const [actualValue, setActualValue] = useState("");
61
64
  const deferred = useDeferredValue(value);
62
65
 
63
- const handleEnterPressed = useMemo(
64
- () => handleEnter(onEnterPressed),
65
- [onEnterPressed],
66
- );
66
+ const handleEnterPressed = useHandleEnter(onEnterPressed);
67
67
 
68
68
  useEffect(() => {
69
69
  setActualValue(deferred ?? "");
@@ -73,14 +73,14 @@ export const Input = ({
73
73
  (e?: ChangeEvent<HTMLInputElement>) => {
74
74
  const value = e?.target.value;
75
75
  setActualValue(value ?? "");
76
- onChange?.(value ?? undefined);
76
+ onChange?.(value ?? null);
77
77
  },
78
78
  [onchange],
79
79
  );
80
80
 
81
81
  return (
82
82
  <InputWrapper
83
- showClear={!!actualValue}
83
+ showClear={!!actualValue && !disabled && !readOnly}
84
84
  onClear={handleChange}
85
85
  invalid={invalid}
86
86
  readOnly={readOnly}
@@ -90,7 +90,10 @@ export const Input = ({
90
90
  {...rest}
91
91
  >
92
92
  <input
93
- className="appearance-none bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0 peer"
93
+ className={classNames(
94
+ "appearance-none bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0 peer",
95
+ monospace && "font-mono text-sm",
96
+ )}
94
97
  aria-invalid={invalid}
95
98
  aria-disabled={disabled}
96
99
  aria-readonly={readOnly}
@@ -104,8 +107,10 @@ export const Input = ({
104
107
  type={type}
105
108
  ref={ref}
106
109
  autoComplete="off"
107
- {...{ autoFocus }}
110
+ autoFocus={autoFocus}
108
111
  value={actualValue}
112
+ onBlur={onBlur}
113
+ onFocus={onFocus}
109
114
  onChange={handleChange}
110
115
  onKeyDown={handleEnterPressed}
111
116
  />
@@ -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>,
@@ -64,6 +63,8 @@ export const Number = ({
64
63
  min,
65
64
  max,
66
65
  step,
66
+ onBlur,
67
+ onFocus,
67
68
  onChange,
68
69
  onEnterPressed,
69
70
  ...rest
@@ -71,10 +72,7 @@ export const Number = ({
71
72
  const [actualValue, setActualValue] = useState<"" | number>("");
72
73
  const deferred = useDeferredValue(value);
73
74
 
74
- const handleEnterPressed = useMemo(
75
- () => handleEnter(onEnterPressed),
76
- [onEnterPressed],
77
- );
75
+ const handleEnterPressed = useHandleEnter(onEnterPressed);
78
76
 
79
77
  useEffect(() => {
80
78
  setActualValue(deferred ?? "");
@@ -84,14 +82,14 @@ export const Number = ({
84
82
  (e?: ChangeEvent<HTMLInputElement>) => {
85
83
  const number = e?.target.valueAsNumber;
86
84
  setActualValue(number ?? "");
87
- onChange?.(number ?? undefined);
85
+ onChange?.(number ?? null);
88
86
  },
89
87
  [onChange],
90
88
  );
91
89
 
92
90
  return (
93
91
  <InputWrapper
94
- showClear={!!actualValue}
92
+ showClear={!!actualValue && !disabled && !readOnly}
95
93
  onClear={handleChange}
96
94
  invalid={invalid}
97
95
  readOnly={readOnly}
@@ -100,7 +98,7 @@ export const Number = ({
100
98
  {...rest}
101
99
  >
102
100
  <input
103
- className="appearance-none bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0"
101
+ className="appearance-none bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0 peer"
104
102
  aria-invalid={invalid}
105
103
  aria-disabled={disabled}
106
104
  aria-readonly={readOnly}
@@ -116,8 +114,10 @@ export const Number = ({
116
114
  step={step}
117
115
  ref={ref}
118
116
  autoComplete="off"
119
- {...{ autoFocus }}
117
+ autoFocus={autoFocus}
120
118
  value={actualValue}
119
+ onBlur={onBlur}
120
+ onFocus={onFocus}
121
121
  onChange={handleChange}
122
122
  onKeyDown={handleEnterPressed}
123
123
  />
@@ -28,14 +28,13 @@ import {
28
28
  useCallback,
29
29
  useDeferredValue,
30
30
  useEffect,
31
- useMemo,
32
31
  useState,
33
32
  type ChangeEvent,
34
33
  } from "react";
35
34
  import { useTranslation } from "react-i18next";
36
35
  import { InputWrapper } from "../internal/InputWrapper";
37
36
  import { type InputProps } from "../types";
38
- import { handleEnter } from "../utils";
37
+ import { useHandleEnter } from "../utils";
39
38
 
40
39
  export interface PasswordProps extends InputProps, RefProp<HTMLInputElement> {
41
40
  /**
@@ -60,6 +59,8 @@ export const Password = ({
60
59
  placeholder,
61
60
  autoFocus,
62
61
  showToggle,
62
+ onBlur,
63
+ onFocus,
63
64
  onChange,
64
65
  onEnterPressed,
65
66
  ...rest
@@ -70,10 +71,7 @@ export const Password = ({
70
71
  const [actualValue, setActualValue] = useState("");
71
72
  const deferred = useDeferredValue(value);
72
73
 
73
- const handleEnterPressed = useMemo(
74
- () => handleEnter(onEnterPressed),
75
- [onEnterPressed],
76
- );
74
+ const handleEnterPressed = useHandleEnter(onEnterPressed);
77
75
 
78
76
  useEffect(() => {
79
77
  setActualValue(deferred ?? "");
@@ -83,22 +81,23 @@ export const Password = ({
83
81
  (e?: ChangeEvent<HTMLInputElement>) => {
84
82
  const value = e?.target.value;
85
83
  setActualValue(value ?? "");
86
- onChange?.(value ?? undefined);
84
+ onChange?.(value ?? null);
87
85
  },
88
86
  [onChange],
89
87
  );
90
88
 
91
89
  return (
92
90
  <InputWrapper
93
- showClear={!!actualValue}
91
+ showClear={!!actualValue && !disabled && !readOnly}
94
92
  onClear={handleChange}
95
93
  invalid={invalid}
96
94
  readOnly={readOnly}
97
95
  disabled={disabled}
98
96
  required={required}
99
97
  {...rest}
100
- decorateEnd={
101
- showToggle && (
98
+ actionButton={
99
+ showToggle &&
100
+ actualValue && (
102
101
  <Tooltip
103
102
  color="warning"
104
103
  content={t(show ? "password.hide" : "password.show")}
@@ -116,7 +115,7 @@ export const Password = ({
116
115
  }
117
116
  >
118
117
  <input
119
- className="appearance-none bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0"
118
+ className="appearance-none bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0 peer"
120
119
  aria-invalid={invalid}
121
120
  aria-disabled={disabled}
122
121
  aria-readonly={readOnly}
@@ -129,8 +128,10 @@ export const Password = ({
129
128
  type={show ? "text" : "password"}
130
129
  ref={ref}
131
130
  autoComplete="off"
132
- {...{ autoFocus }}
131
+ autoFocus={autoFocus}
133
132
  value={actualValue}
133
+ onBlur={onBlur}
134
+ onFocus={onFocus}
134
135
  onChange={handleChange}
135
136
  onKeyDown={handleEnterPressed}
136
137
  />
@@ -126,7 +126,7 @@ export const Radio = ({
126
126
  <Icon
127
127
  className={classNames(
128
128
  "p-0.5 hidden peer-checked:inline-block",
129
- icon && !iconChecked && "bg-primary-500 text-white",
129
+ icon && !iconChecked && "bg-primary-500 text-white!",
130
130
  )}
131
131
  color={invalid ? "danger" : "primary"}
132
132
  size="1.25em"
@@ -139,7 +139,7 @@ export const Radio = ({
139
139
  icon={iconOff}
140
140
  />
141
141
  {label && (
142
- <span className="flex-initial leading-none truncate">{label}</span>
142
+ <span className="flex-initial leading-tight truncate">{label}</span>
143
143
  )}
144
144
  </label>
145
145
  );