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

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} +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 +46 -1
  13. package/dist/cjs/form/Form.js +43 -6
  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 +11 -2
  19. package/dist/cjs/input/ArrayInput.js +100 -33
  20. package/dist/cjs/input/AvatarInput.js +17 -8
  21. package/dist/cjs/input/Checkbox.js +15 -5
  22. package/dist/cjs/input/ColorInput.js +108 -56
  23. package/dist/cjs/input/ColorPicker.js +11 -35
  24. package/dist/cjs/input/DateInput.js +195 -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 +11 -1
  28. package/dist/cjs/input/Input.js +19 -7
  29. package/dist/cjs/input/Number.js +17 -7
  30. package/dist/cjs/input/Password.js +16 -8
  31. package/dist/cjs/input/Radio.js +11 -2
  32. package/dist/cjs/input/RadioGroup.js +14 -3
  33. package/dist/cjs/input/{Range.js → RangeSlider.js} +14 -6
  34. package/dist/cjs/input/Search.js +25 -16
  35. package/dist/cjs/input/Slider.js +10 -4
  36. package/dist/cjs/input/Switch.js +39 -7
  37. package/dist/cjs/input/Textarea.js +23 -4
  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 +19 -16
  41. package/dist/cjs/select/AutoComplete.js +19 -8
  42. package/dist/cjs/select/DualList.js +15 -1
  43. package/dist/cjs/select/List.js +45 -20
  44. package/dist/cjs/select/Options.js +2 -2
  45. package/dist/cjs/select/Select.js +14 -4
  46. package/dist/cjs/select/useSelect.js +10 -6
  47. package/dist/cjs/types/schema.js +43 -0
  48. package/dist/cjs/utils/eventHandlers.js +11 -5
  49. package/dist/cjs/utils/getColor.js +6 -0
  50. package/dist/esm/builder/FileField.js +39 -27
  51. package/dist/esm/builder/FormField.js +50 -34
  52. package/dist/esm/builder/SchemaEditor.js +72 -0
  53. package/dist/esm/builder/components/AddSchemaFields.js +62 -0
  54. package/dist/esm/builder/components/SchemaArrayForm.js +143 -0
  55. package/dist/esm/builder/components/SchemaField.js +414 -0
  56. package/dist/esm/form/Controller.js +46 -1
  57. package/dist/esm/form/Form.js +43 -6
  58. package/dist/esm/hooks/useFileUploader.js +16 -0
  59. package/dist/esm/hooks/useFormBuilder.js +55 -13
  60. package/dist/esm/i18n/ar.json.js +5 -4
  61. package/dist/esm/i18n/en.json.js +5 -5
  62. package/dist/esm/index.js +5 -1
  63. package/dist/esm/input/ArrayInput.js +100 -33
  64. package/dist/esm/input/AvatarInput.js +17 -8
  65. package/dist/esm/input/Checkbox.js +15 -5
  66. package/dist/esm/input/ColorInput.js +110 -58
  67. package/dist/esm/input/ColorPicker.js +11 -35
  68. package/dist/esm/input/DateInput.js +195 -0
  69. package/dist/esm/input/Field.js +11 -5
  70. package/dist/esm/input/File.js +16 -9
  71. package/dist/esm/input/Hidden.js +11 -1
  72. package/dist/esm/input/Input.js +21 -9
  73. package/dist/esm/input/Number.js +19 -9
  74. package/dist/esm/input/Password.js +18 -10
  75. package/dist/esm/input/Radio.js +11 -2
  76. package/dist/esm/input/RadioGroup.js +15 -4
  77. package/dist/esm/input/{Range.js → RangeSlider.js} +14 -6
  78. package/dist/esm/input/Search.js +27 -18
  79. package/dist/esm/input/Slider.js +10 -4
  80. package/dist/esm/input/Switch.js +41 -9
  81. package/dist/esm/input/Textarea.js +24 -5
  82. package/dist/esm/internal/ErrorIcon.js +1 -1
  83. package/dist/esm/internal/FieldWrapper.js +11 -4
  84. package/dist/esm/internal/InputWrapper.js +19 -16
  85. package/dist/esm/select/AutoComplete.js +19 -8
  86. package/dist/esm/select/DualList.js +15 -1
  87. package/dist/esm/select/List.js +46 -21
  88. package/dist/esm/select/Options.js +2 -2
  89. package/dist/esm/select/Select.js +14 -4
  90. package/dist/esm/select/useSelect.js +11 -7
  91. package/{src/input/ColorPicker.module.css → dist/esm/types/schema.js} +18 -27
  92. package/dist/esm/utils/eventHandlers.js +11 -5
  93. package/dist/esm/utils/getColor.js +6 -1
  94. package/dist/types/builder/FileField.d.ts +4 -2
  95. package/dist/types/builder/FormField.d.ts +3 -1
  96. package/dist/types/builder/SchemaEditor.d.ts +9 -0
  97. package/dist/types/builder/components/AddSchemaFields.d.ts +2 -0
  98. package/dist/types/builder/components/SchemaArrayForm.d.ts +2 -0
  99. package/dist/types/builder/components/SchemaField.d.ts +10 -0
  100. package/dist/types/form/Controller.d.ts +47 -2
  101. package/dist/types/form/Form.d.ts +37 -2
  102. package/dist/types/hooks/useFileUploader.d.ts +16 -0
  103. package/dist/types/hooks/useFormBuilder.d.ts +32 -1
  104. package/dist/types/index.d.ts +5 -2
  105. package/dist/types/input/ArrayInput.d.ts +78 -6
  106. package/dist/types/input/AvatarInput.d.ts +14 -3
  107. package/dist/types/input/Checkbox.d.ts +13 -4
  108. package/dist/types/input/ColorInput.d.ts +15 -2
  109. package/dist/types/input/ColorPicker.d.ts +11 -1
  110. package/dist/types/input/DateInput.d.ts +28 -0
  111. package/dist/types/input/Field.d.ts +5 -1
  112. package/dist/types/input/File.d.ts +9 -0
  113. package/dist/types/input/Hidden.d.ts +8 -0
  114. package/dist/types/input/Input.d.ts +9 -1
  115. package/dist/types/input/Number.d.ts +9 -1
  116. package/dist/types/input/Password.d.ts +7 -1
  117. package/dist/types/input/Radio.d.ts +9 -1
  118. package/dist/types/input/RadioGroup.d.ts +26 -3
  119. package/dist/types/input/{Range.d.ts → RangeSlider.d.ts} +10 -2
  120. package/dist/types/input/Search.d.ts +14 -1
  121. package/dist/types/input/Slider.d.ts +7 -1
  122. package/dist/types/input/Switch.d.ts +16 -4
  123. package/dist/types/input/Textarea.d.ts +15 -1
  124. package/dist/types/internal/FieldWrapper.d.ts +1 -1
  125. package/dist/types/internal/InputWrapper.d.ts +1 -1
  126. package/dist/types/ranger/index.d.ts +2 -2
  127. package/dist/types/select/AutoComplete.d.ts +8 -1
  128. package/dist/types/select/DualList.d.ts +17 -2
  129. package/dist/types/select/List.d.ts +9 -1
  130. package/dist/types/select/Select.d.ts +9 -1
  131. package/dist/types/select/useSelect.d.ts +7 -6
  132. package/dist/types/types/index.d.ts +28 -7
  133. package/dist/types/types/schema.d.ts +78 -33
  134. package/dist/types/utils/eventHandlers.d.ts +1 -1
  135. package/package.json +15 -12
  136. package/src/builder/FileField.tsx +53 -27
  137. package/src/builder/FormField.tsx +52 -32
  138. package/src/builder/SchemaEditor.tsx +79 -0
  139. package/src/builder/components/AddSchemaFields.tsx +74 -0
  140. package/src/builder/components/SchemaArrayForm.tsx +158 -0
  141. package/src/builder/components/SchemaField.tsx +436 -0
  142. package/src/form/Controller.tsx +48 -3
  143. package/src/form/Form.tsx +45 -5
  144. package/src/hooks/useFileUploader.ts +16 -0
  145. package/src/hooks/useFormBuilder.tsx +58 -15
  146. package/src/i18n/ar.json +5 -4
  147. package/src/i18n/en.json +5 -5
  148. package/src/index.ts +11 -2
  149. package/src/input/ArrayInput.tsx +169 -54
  150. package/src/input/AvatarInput.tsx +20 -8
  151. package/src/input/Checkbox.tsx +15 -5
  152. package/src/input/ColorInput.tsx +126 -69
  153. package/src/input/ColorPicker.tsx +12 -36
  154. package/src/input/DateInput.tsx +259 -0
  155. package/src/input/Field.tsx +15 -11
  156. package/src/input/File.tsx +16 -14
  157. package/src/input/Hidden.tsx +12 -1
  158. package/src/input/Input.tsx +23 -10
  159. package/src/input/Number.tsx +18 -10
  160. package/src/input/Password.tsx +19 -12
  161. package/src/input/Radio.tsx +11 -2
  162. package/src/input/RadioGroup.tsx +35 -4
  163. package/src/input/{Range.tsx → RangeSlider.tsx} +13 -5
  164. package/src/input/Search.tsx +29 -19
  165. package/src/input/Slider.tsx +10 -4
  166. package/src/input/Switch.tsx +52 -7
  167. package/src/input/Textarea.tsx +29 -5
  168. package/src/internal/ErrorIcon.tsx +1 -1
  169. package/src/internal/FieldWrapper.tsx +8 -4
  170. package/src/internal/InputWrapper.tsx +36 -20
  171. package/src/select/AutoComplete.tsx +12 -1
  172. package/src/select/DualList.tsx +19 -0
  173. package/src/select/List.tsx +49 -8
  174. package/src/select/Options.tsx +1 -1
  175. package/src/select/Select.tsx +21 -3
  176. package/src/select/useSelect.tsx +13 -5
  177. package/src/types/index.ts +34 -8
  178. package/src/types/schema.ts +96 -41
  179. package/src/utils/eventHandlers.ts +9 -3
  180. package/dist/cjs/input/ArrayInput.module.css +0 -1
  181. package/dist/cjs/input/ColorPicker.module.css +0 -1
  182. package/dist/cjs/input/File.module.css +0 -1
  183. package/dist/cjs/internal/Field.module.css +0 -1
  184. package/dist/esm/input/ArrayInput.module.css +0 -1
  185. package/dist/esm/input/ColorPicker.module.css +0 -1
  186. package/dist/esm/input/File.module.css +0 -1
  187. package/dist/esm/internal/Field.module.css +0 -1
@@ -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,
@@ -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
- type CssProp,
26
26
  type ChildrenProp,
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
 
@@ -42,6 +43,23 @@ export interface RadioGroupProps extends ChildrenProp<typeof Radio>, CssProp {
42
43
  * field info
43
44
  */
44
45
  info?: string;
46
+
47
+ /**
48
+ * field width
49
+ */
50
+ width?: number | string;
51
+ /**
52
+ * inline label and input
53
+ */
54
+ inline?: boolean;
55
+ /**
56
+ * label width for inline
57
+ */
58
+ labelWidth?: string;
59
+ /**
60
+ * label width for inline
61
+ */
62
+ optionWidth?: string;
45
63
  /**
46
64
  * field name
47
65
  */
@@ -56,18 +74,31 @@ export interface RadioGroupProps extends ChildrenProp<typeof Radio>, CssProp {
56
74
  onChange?: (value: AnyObject) => void;
57
75
  }
58
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
+ */
59
85
  export const RadioGroup = ({
60
86
  children,
61
87
  name,
62
88
  className,
89
+ optionWidth,
63
90
  onChange,
64
91
  ...rest
65
92
  }: RadioGroupProps) => {
66
93
  return (
67
94
  <FieldWrapper {...rest}>
68
95
  <div className={className}>
69
- {Children.map(children, (child) =>
70
- cloneElement(child as AnyObject, { name, onChange }),
96
+ {cloneChildren(children, (child: AnyObject) =>
97
+ cloneElement(child, {
98
+ name,
99
+ onChange,
100
+ width: optionWidth,
101
+ }),
71
102
  )}
72
103
  </div>
73
104
  </FieldWrapper>
@@ -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 }}
@@ -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,
@@ -63,10 +75,11 @@ export const Search = ({
63
75
  searching,
64
76
  placeholder,
65
77
  autoFocus,
78
+ hideHandle,
66
79
  allowClear = true,
67
- searchOnChange = true,
68
- decorateEnd,
69
- decorateStart,
80
+ searchOnChange = false,
81
+ onBlur,
82
+ onFocus,
70
83
  onSearch,
71
84
  onChange,
72
85
  onEnterPressed,
@@ -82,10 +95,7 @@ export const Search = ({
82
95
  250,
83
96
  );
84
97
 
85
- const handleEnterPressed = useMemo(
86
- () => handleEnter(handleSearch),
87
- [handleSearch],
88
- );
98
+ const handleEnterPressed = useHandleEnter(handleSearch);
89
99
 
90
100
  useEffect(() => {
91
101
  setActualValue(deferred ?? "");
@@ -96,10 +106,10 @@ export const Search = ({
96
106
  const value = e?.target.value;
97
107
  refValue.current = value ?? "";
98
108
  setActualValue(value ?? "");
99
- onChange?.(value ?? undefined);
109
+ onChange?.(value ?? null);
100
110
 
101
- // value was cleared so force fire onSearch
102
- if (searchOnChange) {
111
+ // when e is undefined value was cleared so force fire onSearch
112
+ if (!e || searchOnChange) {
103
113
  handleSearch();
104
114
  }
105
115
  },
@@ -108,21 +118,19 @@ export const Search = ({
108
118
 
109
119
  return (
110
120
  <InputWrapper
111
- showClear={!!actualValue}
121
+ showClear={!!actualValue && !disabled && !readOnly}
112
122
  onClear={handleChange}
113
123
  allowClear={allowClear}
114
124
  invalid={invalid}
115
125
  readOnly={readOnly}
116
126
  disabled={disabled}
117
127
  required={required}
118
- decorateStart={decorateStart}
119
- decorateEnd={
128
+ actionButton={
120
129
  <Fragment>
121
- {decorateEnd}
122
130
  {searching && (
123
131
  <Icon className="p-2" icon={CoreIcons.spinner} animate="spin" />
124
132
  )}
125
- {!searching && (
133
+ {!searching && !hideHandle && (
126
134
  <Button
127
135
  tabIndex={-1}
128
136
  variant="link"
@@ -137,7 +145,7 @@ export const Search = ({
137
145
  {...rest}
138
146
  >
139
147
  <input
140
- className="appearance-none bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0"
148
+ className="appearance-none bg-transparent py-1 px-2 flex-1 border-none outline-none ring-0 peer"
141
149
  aria-invalid={invalid}
142
150
  aria-disabled={disabled}
143
151
  aria-readonly={readOnly}
@@ -150,8 +158,10 @@ export const Search = ({
150
158
  type="search"
151
159
  ref={ref}
152
160
  autoComplete="off"
153
- {...{ autoFocus }}
161
+ autoFocus={autoFocus}
154
162
  value={actualValue}
163
+ onBlur={onBlur}
164
+ onFocus={onFocus}
155
165
  onChange={handleChange}
156
166
  onKeyDown={handleEnterPressed}
157
167
  />
@@ -96,7 +96,13 @@ export interface SliderProps
96
96
  }
97
97
 
98
98
  /**
99
- * Number slider input
99
+ * Slider component for rendering a slider input with labels and value display.
100
+ * It supports vertical orientation, custom min/max labels, and step ranges.
101
+ * The component handles value changes, displays tooltips, and integrates with ranger for slider functionality.
102
+ * It also supports deferred value updates for smoother interactions.
103
+ *
104
+ * @param {SliderProps} props - The properties for the Slider component.
105
+ * @returns {JSX.Element} The rendered Slider component.
100
106
  */
101
107
  export const Slider = ({
102
108
  error,
@@ -146,7 +152,7 @@ export const Slider = ({
146
152
  (value: number) => {
147
153
  const val = useInfinity && value === max ? Infinity : value;
148
154
  setActualValue(val ?? 0);
149
- onChange?.(val ?? undefined);
155
+ onChange?.(val ?? null);
150
156
  },
151
157
  [onChange, max, useInfinity],
152
158
  );
@@ -194,7 +200,7 @@ export const Slider = ({
194
200
  >
195
201
  <div
196
202
  className={classNames(
197
- "flex-auto order-3 flex items-center select-none relative z-0",
203
+ "flex-1 order-3 flex items-center select-none relative z-0",
198
204
  vertical && "flex-col w-8",
199
205
  invalid ? "text-danger-500" : "text-primary-600",
200
206
  )}
@@ -205,7 +211,7 @@ export const Slider = ({
205
211
  <div
206
212
  ref={rangerInstance.trackElRef}
207
213
  className={classNames(
208
- "flex-auto relative flex items-center",
214
+ "flex-1 relative flex items-center",
209
215
  vertical ? "my-2 w-8" : "mx-2 h-8",
210
216
  )}
211
217
  style={{ height: vertical ? height : undefined }}
@@ -27,6 +27,7 @@ import {
27
27
  } from "@react-fabric/core/dist/types/types";
28
28
  import classNames from "classnames";
29
29
  import {
30
+ Fragment,
30
31
  useCallback,
31
32
  useDeferredValue,
32
33
  useEffect,
@@ -34,7 +35,7 @@ import {
34
35
  type ChangeEvent,
35
36
  } from "react";
36
37
  import { ErrorIcon } from "../internal/ErrorIcon";
37
- import { getBgClass, getColor } from "../utils";
38
+ import { getBgClass, getColor, getColorClass } from "../utils";
38
39
 
39
40
  export interface SwitchProps extends RefProp<HTMLInputElement> {
40
41
  /**
@@ -71,15 +72,30 @@ export interface SwitchProps extends RefProp<HTMLInputElement> {
71
72
  /**
72
73
  * cutom Switch icon
73
74
  */
74
- color?: ColorType;
75
+ color?: string | ColorType;
75
76
  /**
76
77
  * cutom Switch icon
77
78
  */
78
- defaultColor?: ColorType;
79
+ defaultColor?: string | ColorType;
79
80
 
80
81
  name?: string;
82
+
83
+ onLabel?: string;
84
+ onLabelColor?: string | ColorType;
85
+
86
+ offLabel?: string;
87
+ offLabelColor?: string | ColorType;
81
88
  }
82
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
+ */
83
99
  export const Switch = ({
84
100
  ref,
85
101
  label,
@@ -90,7 +106,13 @@ export const Switch = ({
90
106
  width,
91
107
  color,
92
108
  name,
109
+ onLabel,
110
+ offLabel,
111
+ onLabelColor,
112
+ offLabelColor,
93
113
  defaultColor,
114
+ // @ts-expect-error ignore
115
+ required,
94
116
  onChange,
95
117
  ...rest
96
118
  }: SwitchProps) => {
@@ -125,7 +147,7 @@ export const Switch = ({
125
147
  actualValue && getBgClass(color ? `${color}-500` : "primary-500"),
126
148
  !actualValue &&
127
149
  (defaultColor ? getBgClass(`${defaultColor}-500`) : "bg-tint-200"),
128
- "relative flex h-6 w-10 cursor-pointer rounded-full p-1 transition-colors duration-200 ease-in-out",
150
+ "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",
129
151
  )}
130
152
  style={{
131
153
  backgroundColor: actualValue
@@ -154,14 +176,37 @@ export const Switch = ({
154
176
  aria-hidden="true"
155
177
  className={classNames(
156
178
  !disabled && "shadow-sm",
157
- actualValue ? "translate-x-4" : "translate-x-0",
158
- "pointer-events-none inline-block size-4 rounded-full bg-white ring-0 transition duration-200 ease-in-out",
179
+ actualValue ? "translate-x-[100%]" : "translate-x-0",
180
+ "pointer-events-none inline-block h-4 start-1 absolute rounded-full bg-white ring-0 transition duration-200 ease-in-out",
159
181
  )}
182
+ style={{
183
+ width: "calc(50% - 0.25rem)",
184
+ }}
160
185
  />
186
+ {(!!onLabel || !!offLabel) && (
187
+ <Fragment>
188
+ <span
189
+ className={`whitespace-nowrap text-xs px-1 ${getColorClass(onLabelColor ?? "white")}`}
190
+ style={{
191
+ color: getColor(onLabelColor),
192
+ }}
193
+ >
194
+ {onLabel ?? " "}
195
+ </span>
196
+ <span
197
+ className={`whitespace-nowrap text-xs px-1 ${getColorClass(offLabelColor ?? "base")}`}
198
+ style={{
199
+ color: getColor(offLabelColor),
200
+ }}
201
+ >
202
+ {offLabel ?? " "}
203
+ </span>
204
+ </Fragment>
205
+ )}
161
206
  </div>
162
207
  <ErrorIcon invalid={invalid} error={error} />
163
208
  {label && (
164
- <span className="flex-initial leading-5 truncate">{label}</span>
209
+ <span className="flex-initial leading-tight truncate">{label}</span>
165
210
  )}
166
211
  </label>
167
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,
@@ -64,6 +78,9 @@ export const Textarea = ({
64
78
  rows = 5,
65
79
  maxRows = 12,
66
80
  variableRows,
81
+ expandable,
82
+ onBlur,
83
+ onFocus,
67
84
  onChange,
68
85
  onEnterPressed,
69
86
  ...rest
@@ -81,7 +98,7 @@ export const Textarea = ({
81
98
  (e?: ChangeEvent<HTMLTextAreaElement>) => {
82
99
  const value = e?.target.value;
83
100
  setActualValue(value ?? "");
84
- onChange?.(value ?? undefined);
101
+ onChange?.(value ?? null);
85
102
  },
86
103
  [onChange],
87
104
  );
@@ -95,15 +112,20 @@ export const Textarea = ({
95
112
  }
96
113
  }, [actualValue, variableRows, rows, maxRows]);
97
114
 
115
+ const handleEnterPressed = useHandleEnter(onEnterPressed, {
116
+ preventDefault: !!onEnterPressed,
117
+ });
118
+
98
119
  return (
99
120
  <InputWrapper
100
- showClear={!!actualValue}
121
+ showClear={!!actualValue && !disabled && !readOnly}
101
122
  onClear={handleChange}
102
123
  invalid={invalid}
103
124
  readOnly={readOnly}
104
125
  disabled={disabled}
105
126
  required={required}
106
127
  wrapperRef={wrapperRef}
128
+ textareaExpandable={expandable}
107
129
  textarea
108
130
  {...rest}
109
131
  >
@@ -120,10 +142,12 @@ export const Textarea = ({
120
142
  name={name}
121
143
  ref={ref}
122
144
  autoComplete="off"
123
- {...{ autoFocus }}
145
+ autoFocus={autoFocus}
124
146
  value={actualValue}
147
+ onBlur={onBlur}
148
+ onFocus={onFocus}
125
149
  onChange={handleChange}
126
- onKeyDown={handleEnter(onEnterPressed)}
150
+ onKeyDown={handleEnterPressed}
127
151
  />
128
152
  </InputWrapper>
129
153
  );
@@ -48,7 +48,7 @@ export const ErrorIcon = ({ error = "", invalid }: KeyValue) => {
48
48
  ) as string)
49
49
  }
50
50
  placement="bottom"
51
- color="danger"
51
+ color="danger-200"
52
52
  >
53
53
  <Icon
54
54
  className="flex-content first:ms-2 self-center"
@@ -22,7 +22,7 @@
22
22
  */
23
23
 
24
24
  import classNames from "classnames";
25
- import classes from "./Field.module.css";
25
+ import { ErrorIcon } from "./ErrorIcon";
26
26
 
27
27
  export const FieldWrapper = ({
28
28
  label,
@@ -36,21 +36,23 @@ export const FieldWrapper = ({
36
36
  children,
37
37
  listing,
38
38
  className,
39
+ invalid,
40
+ error,
39
41
  "data-inner": isInner,
40
42
  }: KeyValue) => {
41
43
  return (
42
44
  <div
43
45
  className={classNames(
44
- classes.fieldWrapper,
46
+ "fabric-fieldWrapper",
45
47
  className,
46
48
  inline ? "flex flex-nowrap gap-1" : "block",
47
49
  )}
48
50
  data-ref="fieldWrapper"
49
51
  style={{ width, flexBasis: width }}
50
52
  >
51
- {!isInner && (
53
+ {!isInner && label && (
52
54
  <div
53
- className="flex flex-nowrap flex-content gap-1 py-0.5 px-2"
55
+ className="flex flex-nowrap flex-content items-end gap-1 py-0.5 px-2"
54
56
  style={{ width: inline ? labelWidth : undefined }}
55
57
  >
56
58
  <label
@@ -73,6 +75,8 @@ export const FieldWrapper = ({
73
75
  ✽
74
76
  </span>
75
77
  )}
78
+ <ErrorIcon invalid={invalid} error={error} />
79
+ <div className="flex-1" />
76
80
  {appendLabel && <span className="flex-content">{appendLabel}</span>}
77
81
  </div>
78
82
  )}