@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
@@ -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
  /**
@@ -48,6 +47,12 @@ export interface PasswordProps extends InputProps, RefProp<HTMLInputElement> {
48
47
  strength?: number;
49
48
  }
50
49
 
50
+ /**
51
+ * Password input component with optional strength meter and toggle visibility.
52
+ *
53
+ * @param {PasswordProps} props - The properties for the Password component.
54
+ * @returns {JSX.Element} The rendered Password input component.
55
+ */
51
56
  export const Password = ({
52
57
  ref,
53
58
  value,
@@ -58,8 +63,11 @@ export const Password = ({
58
63
  disabled,
59
64
  required,
60
65
  placeholder,
66
+ defaultValue,
61
67
  autoFocus,
62
68
  showToggle,
69
+ onBlur,
70
+ onFocus,
63
71
  onChange,
64
72
  onEnterPressed,
65
73
  ...rest
@@ -68,12 +76,9 @@ export const Password = ({
68
76
  const [show, setShow] = useState(false);
69
77
 
70
78
  const [actualValue, setActualValue] = useState("");
71
- const deferred = useDeferredValue(value);
79
+ const deferred = useDeferredValue(value ?? defaultValue);
72
80
 
73
- const handleEnterPressed = useMemo(
74
- () => handleEnter(onEnterPressed),
75
- [onEnterPressed],
76
- );
81
+ const handleEnterPressed = useHandleEnter(onEnterPressed);
77
82
 
78
83
  useEffect(() => {
79
84
  setActualValue(deferred ?? "");
@@ -81,24 +86,25 @@ export const Password = ({
81
86
 
82
87
  const handleChange = useCallback(
83
88
  (e?: ChangeEvent<HTMLInputElement>) => {
84
- const value = e?.target.value;
89
+ const value = e?.target.value ?? defaultValue;
85
90
  setActualValue(value ?? "");
86
- onChange?.(value ?? undefined);
91
+ onChange?.(value ?? null);
87
92
  },
88
- [onChange],
93
+ [onChange, defaultValue],
89
94
  );
90
95
 
91
96
  return (
92
97
  <InputWrapper
93
- showClear={!!actualValue}
98
+ showClear={!!actualValue && !disabled && !readOnly}
94
99
  onClear={handleChange}
95
100
  invalid={invalid}
96
101
  readOnly={readOnly}
97
102
  disabled={disabled}
98
103
  required={required}
99
104
  {...rest}
100
- decorateEnd={
101
- showToggle && (
105
+ actionButton={
106
+ showToggle &&
107
+ actualValue && (
102
108
  <Tooltip
103
109
  color="warning"
104
110
  content={t(show ? "password.hide" : "password.show")}
@@ -116,7 +122,7 @@ export const Password = ({
116
122
  }
117
123
  >
118
124
  <input
119
- className="appearance-none bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0"
125
+ className="appearance-none bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0 peer"
120
126
  aria-invalid={invalid}
121
127
  aria-disabled={disabled}
122
128
  aria-readonly={readOnly}
@@ -129,8 +135,10 @@ export const Password = ({
129
135
  type={show ? "text" : "password"}
130
136
  ref={ref}
131
137
  autoComplete="off"
132
- {...{ autoFocus }}
138
+ autoFocus={autoFocus}
133
139
  value={actualValue}
140
+ onBlur={onBlur}
141
+ onFocus={onFocus}
134
142
  onChange={handleChange}
135
143
  onKeyDown={handleEnterPressed}
136
144
  />
@@ -138,7 +146,7 @@ export const Password = ({
138
146
  <div
139
147
  className={classNames(
140
148
  "absolute bottom-px h-[3px] inset-x-0 after:block after:h-full after:bg-dimmed",
141
- "ltr:bg-gradient-to-r rtl:bg-gradient-to-l from-danger-500 via-marigold-500 to-jade-500",
149
+ "ltr:bg-linear-to-r rtl:bg-linear-to-l from-danger-500 via-marigold-500 to-jade-500",
142
150
  )}
143
151
  style={{
144
152
  paddingInlineStart: `${strength > 1 ? 100 : strength * 100}%`,
@@ -69,6 +69,15 @@ export interface RadioProps extends RefProp<HTMLInputElement> {
69
69
  iconChecked?: string;
70
70
  }
71
71
 
72
+ /**
73
+ * Radio input component with custom icons and label.
74
+ *
75
+ * This component renders a radio button with an optional label and custom icons for checked and unchecked states.
76
+ * It supports disabled and invalid states, and allows for custom width.
77
+ *
78
+ * @param {RadioProps} props - The properties for the Radio component.
79
+ * @returns {JSX.Element} The rendered Radio input component.
80
+ */
72
81
  export const Radio = ({
73
82
  ref,
74
83
  name,
@@ -104,7 +113,7 @@ export const Radio = ({
104
113
  disabled
105
114
  ? "opacity-50 cursor-not-allowed"
106
115
  : "hover:opacity-100 cursor-pointer",
107
- "inline-flex gap-1 py-1 items-center relative cursor-pointer opacity-85 rounded ring-offset-2 has-[:focus-visible]:ring-1",
116
+ "inline-flex gap-1 py-1 items-center relative cursor-pointer opacity-85 rounded ring-offset-2 has-focus-visible:ring-1",
108
117
  )}
109
118
  style={{ width }}
110
119
  >
@@ -126,7 +135,7 @@ export const Radio = ({
126
135
  <Icon
127
136
  className={classNames(
128
137
  "p-0.5 hidden peer-checked:inline-block",
129
- icon && !iconChecked && "bg-primary-500 text-white",
138
+ icon && !iconChecked && "bg-primary-500 text-white!",
130
139
  )}
131
140
  color={invalid ? "danger" : "primary"}
132
141
  size="1.25em"
@@ -139,7 +148,7 @@ export const Radio = ({
139
148
  icon={iconOff}
140
149
  />
141
150
  {label && (
142
- <span className="flex-initial leading-none truncate">{label}</span>
151
+ <span className="flex-initial leading-tight truncate">{label}</span>
143
152
  )}
144
153
  </label>
145
154
  );
@@ -21,11 +21,12 @@
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
- import { Children, cloneElement } from "react";
29
+ import { cloneElement } from "react";
29
30
  import { FieldWrapper } from "../internal/FieldWrapper";
30
31
  import { type Radio } from "./Radio";
31
32
 
@@ -73,6 +74,14 @@ export interface RadioGroupProps extends ChildrenProp<typeof Radio>, CssProp {
73
74
  onChange?: (value: AnyObject) => void;
74
75
  }
75
76
 
77
+ /**
78
+ * RadioGroup component for rendering a group of radio buttons.
79
+ * It allows for customization of the label, options, and layout.
80
+ * The component supports inline and vertical orientations, and can handle change events.
81
+ *
82
+ * @param {RadioGroupProps} props - The properties for the RadioGroup component.
83
+ * @returns {JSX.Element} The rendered RadioGroup component.
84
+ */
76
85
  export const RadioGroup = ({
77
86
  children,
78
87
  name,
@@ -84,8 +93,8 @@ export const RadioGroup = ({
84
93
  return (
85
94
  <FieldWrapper {...rest}>
86
95
  <div className={className}>
87
- {Children.map(children, (child) =>
88
- cloneElement(child as AnyObject, {
96
+ {cloneChildren(children, (child: AnyObject) =>
97
+ cloneElement(child, {
89
98
  name,
90
99
  onChange,
91
100
  width: optionWidth,
@@ -38,7 +38,7 @@ import { type InputProps } from "../types";
38
38
  export interface RangeProps
39
39
  extends Omit<
40
40
  InputProps<[number, number]>,
41
- "placeholder" | "allowClear" | "children"
41
+ "placeholder" | "allowClear" | "children" | "defaultValue"
42
42
  > {
43
43
  /**
44
44
  * min value
@@ -95,9 +95,17 @@ export interface RangeProps
95
95
  }
96
96
 
97
97
  /**
98
- * Number Range input
98
+ * RangeSlider component for rendering a range slider input.
99
+ * It supports vertical orientation, custom colors, step increments,
100
+ * min/max values, and display of labels and value tooltips.
101
+ * The component uses the Ranger library for handling the slider functionality.
102
+ * It also provides error handling and supports disabled and read-only states.
103
+ * The component is designed to be flexible and customizable, allowing for various configurations.
104
+ *
105
+ * @param {RangeProps} props - The properties for the RangeSlider component.
106
+ * @returns {JSX.Element} The rendered RangeSlider component.
99
107
  */
100
- export const Range = ({
108
+ export const RangeSlider = ({
101
109
  error,
102
110
  name,
103
111
  value,
@@ -141,7 +149,7 @@ export const Range = ({
141
149
  const handleChange = useCallback(
142
150
  (val: [number, number]) => {
143
151
  setActualValue(val ?? [min, max]);
144
- onChange?.(val ?? undefined);
152
+ onChange?.(val ?? null);
145
153
  },
146
154
  [onChange, min, max],
147
155
  );
@@ -190,7 +198,7 @@ export const Range = ({
190
198
  >
191
199
  <div
192
200
  className={classNames(
193
- "flex-auto order-3 flex items-center select-none z-0",
201
+ "flex-1 order-3 flex items-center select-none z-0",
194
202
  vertical && "flex-col w-8",
195
203
  invalid ? "text-danger-500" : "text-primary-600",
196
204
  )}
@@ -201,7 +209,7 @@ export const Range = ({
201
209
  <div
202
210
  ref={rangerInstance.trackElRef}
203
211
  className={classNames(
204
- "flex-auto relative flex items-center",
212
+ "flex-1 relative flex items-center",
205
213
  vertical ? "my-2 w-8" : "mx-2 h-8",
206
214
  )}
207
215
  style={{ height: vertical ? height : undefined }}
@@ -210,7 +218,7 @@ export const Range = ({
210
218
  <div
211
219
  key={key}
212
220
  className={classNames(
213
- "absolute inline-block h-1 w-1 rounded-sm bg-invert opacity-30 leading-[0] z-1",
221
+ "absolute inline-block h-1 w-1 rounded-sm bg-invert opacity-30 leading-0 z-1",
214
222
  vertical && "left-1/2 -ml-0.5",
215
223
  )}
216
224
  style={styles}
@@ -256,8 +264,8 @@ export const Range = ({
256
264
  : "top-1/2 -translate-y-1/2",
257
265
  !vertical &&
258
266
  (key === 0
259
- ? "-ms-3 ltr:-translate-x-full rtl:translate-x-full ltr:before:translate-x-[0.125rem] rtl:before:-translate-x-[0.125rem] before:end-0"
260
- : "ms-3 ltr:before:-translate-x-[0.125rem] rtl:before:translate-x-[0.125rem] before:start-0"),
267
+ ? "-ms-3 ltr:-translate-x-full rtl:translate-x-full ltr:before:translate-x-0.5 rtl:before:-translate-x-0.5 before:end-0"
268
+ : "ms-3 ltr:before:-translate-x-0.5 rtl:before:translate-x-0.5 before:start-0"),
261
269
  )}
262
270
  data-align={key === 0 ? "start" : "end"}
263
271
  style={valueStyles}
@@ -28,14 +28,13 @@ import {
28
28
  useCallback,
29
29
  useDeferredValue,
30
30
  useEffect,
31
- useMemo,
32
31
  useRef,
33
32
  useState,
34
33
  type ChangeEvent,
35
34
  } from "react";
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 SearchProps extends InputProps, RefProp<HTMLInputElement> {
41
40
  /**
@@ -46,12 +45,25 @@ export interface SearchProps extends InputProps, RefProp<HTMLInputElement> {
46
45
  * search on value change
47
46
  */
48
47
  searchOnChange?: boolean;
48
+ /**
49
+ * hide search action handle
50
+ */
51
+ hideHandle?: boolean;
49
52
  /**
50
53
  * search handler
51
54
  */
52
55
  onSearch?: (query?: string) => void;
53
56
  }
54
57
 
58
+ /**
59
+ * Search input component with optional searching state and search handler.
60
+ * This component allows users to input search queries and trigger a search action.
61
+ * It supports features like searching on value change, showing a search handle,
62
+ * and handling search actions with a debounce mechanism.
63
+ *
64
+ * @param {SearchProps} props - The properties for the Search component.
65
+ * @returns {JSX.Element} The rendered Search input component.
66
+ */
55
67
  export const Search = ({
56
68
  ref,
57
69
  value,
@@ -62,18 +74,20 @@ export const Search = ({
62
74
  required,
63
75
  searching,
64
76
  placeholder,
77
+ defaultValue,
65
78
  autoFocus,
79
+ hideHandle,
66
80
  allowClear = true,
67
- searchOnChange = true,
68
- decorateEnd,
69
- decorateStart,
81
+ searchOnChange = false,
82
+ onBlur,
83
+ onFocus,
70
84
  onSearch,
71
85
  onChange,
72
86
  onEnterPressed,
73
87
  ...rest
74
88
  }: SearchProps) => {
75
89
  const [actualValue, setActualValue] = useState("");
76
- const deferred = useDeferredValue(value);
90
+ const deferred = useDeferredValue(value ?? defaultValue);
77
91
 
78
92
  const refValue = useRef("");
79
93
  const handleSearch = useDebounce(
@@ -82,10 +96,7 @@ export const Search = ({
82
96
  250,
83
97
  );
84
98
 
85
- const handleEnterPressed = useMemo(
86
- () => handleEnter(handleSearch),
87
- [handleSearch],
88
- );
99
+ const handleEnterPressed = useHandleEnter(handleSearch);
89
100
 
90
101
  useEffect(() => {
91
102
  setActualValue(deferred ?? "");
@@ -93,36 +104,34 @@ export const Search = ({
93
104
 
94
105
  const handleChange = useCallback(
95
106
  (e?: ChangeEvent<HTMLInputElement>) => {
96
- const value = e?.target.value;
107
+ const value = e?.target.value ?? defaultValue;
97
108
  refValue.current = value ?? "";
98
109
  setActualValue(value ?? "");
99
- onChange?.(value ?? undefined);
110
+ onChange?.(value ?? null);
100
111
 
101
- // value was cleared so force fire onSearch
102
- if (searchOnChange) {
112
+ // when e is undefined value was cleared so force fire onSearch
113
+ if (!e || searchOnChange) {
103
114
  handleSearch();
104
115
  }
105
116
  },
106
- [onChange, searchOnChange, handleSearch],
117
+ [onChange, searchOnChange, handleSearch, defaultValue],
107
118
  );
108
119
 
109
120
  return (
110
121
  <InputWrapper
111
- showClear={!!actualValue}
122
+ showClear={!!actualValue && !disabled && !readOnly}
112
123
  onClear={handleChange}
113
124
  allowClear={allowClear}
114
125
  invalid={invalid}
115
126
  readOnly={readOnly}
116
127
  disabled={disabled}
117
128
  required={required}
118
- decorateStart={decorateStart}
119
- decorateEnd={
129
+ actionButton={
120
130
  <Fragment>
121
- {decorateEnd}
122
131
  {searching && (
123
132
  <Icon className="p-2" icon={CoreIcons.spinner} animate="spin" />
124
133
  )}
125
- {!searching && (
134
+ {!searching && !hideHandle && (
126
135
  <Button
127
136
  tabIndex={-1}
128
137
  variant="link"
@@ -137,7 +146,7 @@ export const Search = ({
137
146
  {...rest}
138
147
  >
139
148
  <input
140
- className="appearance-none bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0"
149
+ className="appearance-none bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0 peer"
141
150
  aria-invalid={invalid}
142
151
  aria-disabled={disabled}
143
152
  aria-readonly={readOnly}
@@ -150,8 +159,10 @@ export const Search = ({
150
159
  type="search"
151
160
  ref={ref}
152
161
  autoComplete="off"
153
- {...{ autoFocus }}
162
+ autoFocus={autoFocus}
154
163
  value={actualValue}
164
+ onBlur={onBlur}
165
+ onFocus={onFocus}
155
166
  onChange={handleChange}
156
167
  onKeyDown={handleEnterPressed}
157
168
  />
@@ -36,7 +36,10 @@ import { useRanger } from "../ranger";
36
36
  import { type InputProps } from "../types";
37
37
 
38
38
  export interface SliderProps
39
- extends Omit<InputProps<number>, "placeholder" | "allowClear" | "children"> {
39
+ extends Omit<
40
+ InputProps<number>,
41
+ "placeholder" | "allowClear" | "children" | "defaultValue"
42
+ > {
40
43
  /**
41
44
  * min value
42
45
  */
@@ -96,7 +99,13 @@ export interface SliderProps
96
99
  }
97
100
 
98
101
  /**
99
- * Number slider input
102
+ * Slider component for rendering a slider input with labels and value display.
103
+ * It supports vertical orientation, custom min/max labels, and step ranges.
104
+ * The component handles value changes, displays tooltips, and integrates with ranger for slider functionality.
105
+ * It also supports deferred value updates for smoother interactions.
106
+ *
107
+ * @param {SliderProps} props - The properties for the Slider component.
108
+ * @returns {JSX.Element} The rendered Slider component.
100
109
  */
101
110
  export const Slider = ({
102
111
  error,
@@ -146,7 +155,7 @@ export const Slider = ({
146
155
  (value: number) => {
147
156
  const val = useInfinity && value === max ? Infinity : value;
148
157
  setActualValue(val ?? 0);
149
- onChange?.(val ?? undefined);
158
+ onChange?.(val ?? null);
150
159
  },
151
160
  [onChange, max, useInfinity],
152
161
  );
@@ -194,7 +203,7 @@ export const Slider = ({
194
203
  >
195
204
  <div
196
205
  className={classNames(
197
- "flex-auto order-3 flex items-center select-none relative z-0",
206
+ "flex-1 order-3 flex items-center select-none relative z-0",
198
207
  vertical && "flex-col w-8",
199
208
  invalid ? "text-danger-500" : "text-primary-600",
200
209
  )}
@@ -205,8 +214,8 @@ export const Slider = ({
205
214
  <div
206
215
  ref={rangerInstance.trackElRef}
207
216
  className={classNames(
208
- "flex-auto relative flex items-center",
209
- vertical ? "my-2 w-8" : "mx-2 h-8",
217
+ "relative flex items-center",
218
+ vertical ? "my-2 w-8" : "flex-1 mx-2 h-8",
210
219
  )}
211
220
  style={{ height: vertical ? height : undefined }}
212
221
  >
@@ -214,7 +223,7 @@ export const Slider = ({
214
223
  <div
215
224
  key={key}
216
225
  className={classNames(
217
- "absolute inline-block h-1 w-1 rounded-sm bg-invert opacity-30 leading-[0] z-1",
226
+ "absolute inline-block h-1 w-1 rounded-sm bg-invert opacity-30 leading-0 z-1",
218
227
  vertical && "left-1/2 -ml-0.5",
219
228
  )}
220
229
  style={styles}
@@ -253,15 +262,15 @@ export const Slider = ({
253
262
  {displayValue && (
254
263
  <div
255
264
  className={classNames(
256
- "bg-invert text-invert py-px px-2 text-xs absolute rounded z-[5] whitespace-nowrap",
265
+ "bg-invert text-invert py-px px-2 text-xs absolute rounded z-5 whitespace-nowrap pointer-events-none",
257
266
  "before:absolute before:h-1.5 before:w-1.5 before:z-[-1] before:bg-invert before:top-1/2 before:-translate-y-1/2 before:rotate-45",
258
267
  vertical
259
- ? "end-0 ltr:translate-x-full rtl:-translate-x-full translate-y-2"
268
+ ? "end-0 ltr:translate-x-full rtl:-translate-x-full translate-y-2 ltr:before:-translate-x-0.5 rtl:before:translate-x-0.5 before:start-0"
260
269
  : "top-1/2 -translate-y-1/2",
261
270
  !vertical &&
262
271
  (percentage > 50
263
- ? "-ms-3 ltr:-translate-x-full rtl:translate-x-full ltr:before:translate-x-[0.125rem] rtl:before:-translate-x-[0.125rem] before:end-0"
264
- : "ms-3 ltr:before:-translate-x-[0.125rem] rtl:before:translate-x-[0.125rem] before:start-0"),
272
+ ? "-ms-3 ltr:-translate-x-full rtl:translate-x-full ltr:before:translate-x-0.5 rtl:before:-translate-x-0.5 before:end-0"
273
+ : "ms-3 ltr:before:-translate-x-0.5 rtl:before:translate-x-0.5 before:start-0"),
265
274
  )}
266
275
  data-align={percentage > 50 ? "start" : "end"}
267
276
  style={valueStyles}
@@ -87,6 +87,15 @@ export interface SwitchProps extends RefProp<HTMLInputElement> {
87
87
  offLabelColor?: string | ColorType;
88
88
  }
89
89
 
90
+ /**
91
+ * Switch input component with custom labels and colors.
92
+ * This component renders a switch input with optional labels for the "on" and "off" states,
93
+ * and allows for custom colors for the switch background and labels.
94
+ * It supports disabled and invalid states, and handles change events.
95
+ *
96
+ * @param {SwitchProps} props - The properties for the Switch component.
97
+ * @returns {JSX.Element} The rendered Switch input component.
98
+ */
90
99
  export const Switch = ({
91
100
  ref,
92
101
  label,
@@ -102,6 +111,8 @@ export const Switch = ({
102
111
  onLabelColor,
103
112
  offLabelColor,
104
113
  defaultColor,
114
+ // @ts-expect-error ignore
115
+ required,
105
116
  onChange,
106
117
  ...rest
107
118
  }: SwitchProps) => {
@@ -127,7 +138,7 @@ export const Switch = ({
127
138
  disabled
128
139
  ? "opacity-50 cursor-not-allowed"
129
140
  : "hover:opacity-100 cursor-pointer",
130
- "inline-flex gap-1 py-1 items-center relative cursor-pointer opacity-85 rounded ring-offset-2 has-[:focus-visible]:ring-1",
141
+ "inline-flex gap-1 py-1 items-center relative cursor-pointer opacity-85 rounded ring-offset-2 has-focus-visible:ring-1",
131
142
  )}
132
143
  style={{ width }}
133
144
  >
@@ -136,7 +147,7 @@ export const Switch = ({
136
147
  actualValue && getBgClass(color ? `${color}-500` : "primary-500"),
137
148
  !actualValue &&
138
149
  (defaultColor ? getBgClass(`${defaultColor}-500`) : "bg-tint-200"),
139
- "relative grid grid-cols-2 items-center h-6 min-w-10 cursor-pointer rounded-full p-1 transition-colors duration-200 ease-in-out",
150
+ "relative grid grid-cols-2 items-center h-[1.5em] min-w-[2.5em] cursor-pointer rounded-full p-[0.25em] transition-colors duration-200 ease-in-out",
140
151
  )}
141
152
  style={{
142
153
  backgroundColor: actualValue
@@ -165,11 +176,11 @@ export const Switch = ({
165
176
  aria-hidden="true"
166
177
  className={classNames(
167
178
  !disabled && "shadow-sm",
168
- actualValue ? "translate-x-[100%]" : "translate-x-0",
169
- "pointer-events-none inline-block h-4 start-1 absolute rounded-full bg-white ring-0 transition duration-200 ease-in-out",
179
+ actualValue ? "translate-x-full" : "translate-x-0",
180
+ "pointer-events-none inline-block h-[1em] start-[0.25em] absolute rounded-full bg-white ring-0 transition duration-200 ease-in-out",
170
181
  )}
171
182
  style={{
172
- width: "calc(50% - 0.25rem)",
183
+ width: "calc(50% - 0.25em)",
173
184
  }}
174
185
  />
175
186
  {(!!onLabel || !!offLabel) && (
@@ -195,7 +206,7 @@ export const Switch = ({
195
206
  </div>
196
207
  <ErrorIcon invalid={invalid} error={error} />
197
208
  {label && (
198
- <span className="flex-initial leading-5 truncate">{label}</span>
209
+ <span className="flex-initial leading-tight truncate">{label}</span>
199
210
  )}
200
211
  </label>
201
212
  );
@@ -32,7 +32,7 @@ import {
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 TextareaProps
38
38
  extends InputProps,
@@ -49,8 +49,22 @@ export interface TextareaProps
49
49
  * increment rows on value change
50
50
  */
51
51
  variableRows?: boolean;
52
+ /**
53
+ * expandable textarea
54
+ */
55
+ expandable?: boolean;
52
56
  }
53
57
 
58
+ /**
59
+ * Textarea component that allows users to input multi-line text.
60
+ * It supports features like variable row count, expandable behavior,
61
+ * and handling of focus, blur, and change events.
62
+ * This component is designed to be flexible and user-friendly,
63
+ * providing a clear interface for text input with optional validation and accessibility features.
64
+ *
65
+ * @param {TextareaProps} props - The properties for the Textarea component.
66
+ * @returns {JSX.Element} The rendered Textarea component.
67
+ */
54
68
  export const Textarea = ({
55
69
  ref,
56
70
  value,
@@ -60,17 +74,21 @@ export const Textarea = ({
60
74
  disabled,
61
75
  required,
62
76
  placeholder,
77
+ defaultValue,
63
78
  autoFocus,
64
79
  rows = 5,
65
80
  maxRows = 12,
66
81
  variableRows,
82
+ expandable,
83
+ onBlur,
84
+ onFocus,
67
85
  onChange,
68
86
  onEnterPressed,
69
87
  ...rest
70
88
  }: TextareaProps) => {
71
89
  const [actualValue, setActualValue] = useState("");
72
90
  const [fixedRows, setFixedRows] = useState(rows);
73
- const deferred = useDeferredValue(value);
91
+ const deferred = useDeferredValue(value ?? defaultValue);
74
92
  const wrapperRef = useRef(null);
75
93
 
76
94
  useEffect(() => {
@@ -79,11 +97,11 @@ export const Textarea = ({
79
97
 
80
98
  const handleChange = useCallback(
81
99
  (e?: ChangeEvent<HTMLTextAreaElement>) => {
82
- const value = e?.target.value;
100
+ const value = e?.target.value ?? defaultValue;
83
101
  setActualValue(value ?? "");
84
- onChange?.(value ?? undefined);
102
+ onChange?.(value ?? null);
85
103
  },
86
- [onChange],
104
+ [onChange, defaultValue],
87
105
  );
88
106
 
89
107
  useEffect(() => {
@@ -95,20 +113,25 @@ export const Textarea = ({
95
113
  }
96
114
  }, [actualValue, variableRows, rows, maxRows]);
97
115
 
116
+ const handleEnterPressed = useHandleEnter(onEnterPressed, {
117
+ preventDefault: !!onEnterPressed,
118
+ });
119
+
98
120
  return (
99
121
  <InputWrapper
100
- showClear={!!actualValue}
122
+ showClear={!!actualValue && !disabled && !readOnly}
101
123
  onClear={handleChange}
102
124
  invalid={invalid}
103
125
  readOnly={readOnly}
104
126
  disabled={disabled}
105
127
  required={required}
106
128
  wrapperRef={wrapperRef}
129
+ textareaExpandable={expandable}
107
130
  textarea
108
131
  {...rest}
109
132
  >
110
133
  <textarea
111
- className="appearance-none self-stretch bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0 min-h-20 resize-none"
134
+ className="appearance-none self-stretch bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0 min-h-12 resize-none"
112
135
  aria-invalid={invalid}
113
136
  aria-disabled={disabled}
114
137
  aria-readonly={readOnly}
@@ -120,10 +143,12 @@ export const Textarea = ({
120
143
  name={name}
121
144
  ref={ref}
122
145
  autoComplete="off"
123
- {...{ autoFocus }}
146
+ autoFocus={autoFocus}
124
147
  value={actualValue}
148
+ onBlur={onBlur}
149
+ onFocus={onFocus}
125
150
  onChange={handleChange}
126
- onKeyDown={handleEnter(onEnterPressed)}
151
+ onKeyDown={handleEnterPressed as any}
127
152
  />
128
153
  </InputWrapper>
129
154
  );