@react-fabric/form 1.0.0-beta.3 → 1.0.0-beta.30

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 (66) hide show
  1. package/dist/cjs/form/Form.js +12 -5
  2. package/dist/cjs/input/ArrayInput.js +35 -23
  3. package/dist/cjs/input/ColorInput.js +68 -26
  4. package/dist/cjs/input/ColorPicker.js +0 -33
  5. package/dist/cjs/input/ColorPicker.module.css +1 -1
  6. package/dist/cjs/input/Field.js +3 -2
  7. package/dist/cjs/input/File.module.css +1 -1
  8. package/dist/cjs/input/Hidden.js +3 -1
  9. package/dist/cjs/input/RadioGroup.js +6 -3
  10. package/dist/cjs/input/Switch.js +25 -4
  11. package/dist/cjs/input/Textarea.js +6 -2
  12. package/dist/cjs/internal/FieldWrapper.js +3 -1
  13. package/dist/cjs/internal/InputWrapper.js +7 -10
  14. package/dist/cjs/select/List.js +41 -20
  15. package/dist/cjs/select/Options.js +2 -2
  16. package/dist/cjs/select/Select.js +10 -7
  17. package/dist/cjs/select/useSelect.js +12 -7
  18. package/dist/cjs/utils/getColor.js +6 -0
  19. package/dist/esm/form/Form.js +13 -6
  20. package/dist/esm/input/ArrayInput.js +37 -25
  21. package/dist/esm/input/ColorInput.js +70 -28
  22. package/dist/esm/input/ColorPicker.js +0 -33
  23. package/dist/esm/input/ColorPicker.module.css +1 -1
  24. package/dist/esm/input/Field.js +4 -3
  25. package/dist/esm/input/File.module.css +1 -1
  26. package/dist/esm/input/Hidden.js +3 -1
  27. package/dist/esm/input/RadioGroup.js +7 -4
  28. package/dist/esm/input/Switch.js +27 -6
  29. package/dist/esm/input/Textarea.js +6 -2
  30. package/dist/esm/internal/FieldWrapper.js +3 -1
  31. package/dist/esm/internal/InputWrapper.js +7 -10
  32. package/dist/esm/select/List.js +42 -21
  33. package/dist/esm/select/Options.js +2 -2
  34. package/dist/esm/select/Select.js +10 -7
  35. package/dist/esm/select/useSelect.js +13 -8
  36. package/dist/esm/utils/getColor.js +6 -1
  37. package/dist/types/input/ArrayInput.d.ts +20 -3
  38. package/dist/types/input/RadioGroup.d.ts +18 -2
  39. package/dist/types/input/Switch.d.ts +7 -3
  40. package/dist/types/input/Textarea.d.ts +5 -1
  41. package/dist/types/internal/InputWrapper.d.ts +1 -1
  42. package/dist/types/select/List.d.ts +1 -1
  43. package/dist/types/types/index.d.ts +2 -2
  44. package/package.json +7 -7
  45. package/src/form/Form.tsx +13 -5
  46. package/src/input/ArrayInput.tsx +78 -40
  47. package/src/input/ColorInput.tsx +96 -31
  48. package/src/input/ColorPicker.module.css +19 -5
  49. package/src/input/ColorPicker.tsx +0 -33
  50. package/src/input/Field.tsx +6 -8
  51. package/src/input/File.module.css +18 -4
  52. package/src/input/Hidden.tsx +3 -1
  53. package/src/input/Input.tsx +1 -1
  54. package/src/input/Number.tsx +1 -1
  55. package/src/input/Password.tsx +1 -1
  56. package/src/input/RadioGroup.tsx +27 -4
  57. package/src/input/Search.tsx +1 -1
  58. package/src/input/Switch.tsx +40 -6
  59. package/src/input/Textarea.tsx +10 -2
  60. package/src/internal/FieldWrapper.tsx +2 -1
  61. package/src/internal/InputWrapper.tsx +8 -11
  62. package/src/select/List.tsx +34 -7
  63. package/src/select/Options.tsx +1 -1
  64. package/src/select/Select.tsx +4 -1
  65. package/src/select/useSelect.tsx +27 -18
  66. package/src/types/index.ts +5 -2
@@ -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
  */
@@ -60,14 +78,19 @@ export const RadioGroup = ({
60
78
  children,
61
79
  name,
62
80
  className,
81
+ optionWidth,
63
82
  onChange,
64
83
  ...rest
65
84
  }: RadioGroupProps) => {
66
85
  return (
67
86
  <FieldWrapper {...rest}>
68
87
  <div className={className}>
69
- {Children.map(children, (child) =>
70
- cloneElement(child as AnyObject, { name, onChange }),
88
+ {cloneChildren(children, (child: AnyObject) =>
89
+ cloneElement(child, {
90
+ name,
91
+ onChange,
92
+ width: optionWidth,
93
+ }),
71
94
  )}
72
95
  </div>
73
96
  </FieldWrapper>
@@ -150,7 +150,7 @@ export const Search = ({
150
150
  type="search"
151
151
  ref={ref}
152
152
  autoComplete="off"
153
- {...{ autoFocus }}
153
+ autoFocus={autoFocus}
154
154
  value={actualValue}
155
155
  onChange={handleChange}
156
156
  onKeyDown={handleEnterPressed}
@@ -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,13 +72,19 @@ 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
 
83
90
  export const Switch = ({
@@ -90,6 +97,10 @@ export const Switch = ({
90
97
  width,
91
98
  color,
92
99
  name,
100
+ onLabel,
101
+ offLabel,
102
+ onLabelColor,
103
+ offLabelColor,
93
104
  defaultColor,
94
105
  onChange,
95
106
  ...rest
@@ -125,7 +136,7 @@ export const Switch = ({
125
136
  actualValue && getBgClass(color ? `${color}-500` : "primary-500"),
126
137
  !actualValue &&
127
138
  (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",
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",
129
140
  )}
130
141
  style={{
131
142
  backgroundColor: actualValue
@@ -154,10 +165,33 @@ export const Switch = ({
154
165
  aria-hidden="true"
155
166
  className={classNames(
156
167
  !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",
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",
159
170
  )}
171
+ style={{
172
+ width: "calc(50% - 0.25rem)",
173
+ }}
160
174
  />
175
+ {(!!onLabel || !!offLabel) && (
176
+ <Fragment>
177
+ <span
178
+ className={`whitespace-nowrap text-xs px-1 ${getColorClass(onLabelColor ?? "white")}`}
179
+ style={{
180
+ color: getColor(onLabelColor),
181
+ }}
182
+ >
183
+ {onLabel ?? " "}
184
+ </span>
185
+ <span
186
+ className={`whitespace-nowrap text-xs px-1 ${getColorClass(offLabelColor ?? "base")}`}
187
+ style={{
188
+ color: getColor(offLabelColor),
189
+ }}
190
+ >
191
+ {offLabel ?? " "}
192
+ </span>
193
+ </Fragment>
194
+ )}
161
195
  </div>
162
196
  <ErrorIcon invalid={invalid} error={error} />
163
197
  {label && (
@@ -49,6 +49,10 @@ 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
 
54
58
  export const Textarea = ({
@@ -64,6 +68,7 @@ export const Textarea = ({
64
68
  rows = 5,
65
69
  maxRows = 12,
66
70
  variableRows,
71
+ expandable,
67
72
  onChange,
68
73
  onEnterPressed,
69
74
  ...rest
@@ -104,6 +109,7 @@ export const Textarea = ({
104
109
  disabled={disabled}
105
110
  required={required}
106
111
  wrapperRef={wrapperRef}
112
+ textareaExpandable={expandable}
107
113
  textarea
108
114
  {...rest}
109
115
  >
@@ -120,10 +126,12 @@ export const Textarea = ({
120
126
  name={name}
121
127
  ref={ref}
122
128
  autoComplete="off"
123
- {...{ autoFocus }}
129
+ autoFocus={autoFocus}
124
130
  value={actualValue}
125
131
  onChange={handleChange}
126
- onKeyDown={handleEnter(onEnterPressed)}
132
+ onKeyDown={handleEnter(onEnterPressed, {
133
+ preventDefault: !!onEnterPressed,
134
+ })}
127
135
  />
128
136
  </InputWrapper>
129
137
  );
@@ -50,7 +50,7 @@ export const FieldWrapper = ({
50
50
  >
51
51
  {!isInner && (
52
52
  <div
53
- className="flex flex-nowrap flex-content gap-1 py-0.5 px-2"
53
+ className="flex flex-nowrap flex-content items-end gap-1 py-0.5 px-2"
54
54
  style={{ width: inline ? labelWidth : undefined }}
55
55
  >
56
56
  <label
@@ -73,6 +73,7 @@ export const FieldWrapper = ({
73
73
  ✽
74
74
  </span>
75
75
  )}
76
+ <div className="flex-1" />
76
77
  {appendLabel && <span className="flex-content">{appendLabel}</span>}
77
78
  </div>
78
79
  )}
@@ -21,7 +21,6 @@
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 { FloatingPortal } from "@floating-ui/react";
25
24
  import { CoreIcons, Icon } from "@react-fabric/core";
26
25
  import { isString } from "@react-fabric/utilities";
27
26
  import classNames from "classnames";
@@ -50,6 +49,7 @@ export const InputWrapper = ({
50
49
  noOutline,
51
50
  noBorder,
52
51
  textarea = false,
52
+ textareaExpandable = false,
53
53
  "data-inner": isInner,
54
54
  "data-open": isOpen,
55
55
  // expand autocomplete
@@ -117,7 +117,11 @@ export const InputWrapper = ({
117
117
  !noOutline &&
118
118
  "after:absolute after:bottom-0 after:h-px after:bg-primary-500 after:mx-auto",
119
119
  )}
120
- onKeyDown={(e) => e.key === "Escape" && setExpanded(false)}
120
+ onKeyDown={(e) =>
121
+ e.key === "Escape" &&
122
+ isExpanded &&
123
+ (setExpanded(false), e.stopPropagation())
124
+ }
121
125
  >
122
126
  {decorateStart && (
123
127
  <div
@@ -156,7 +160,7 @@ export const InputWrapper = ({
156
160
  {decorateEnd}
157
161
  </div>
158
162
  )}
159
- {textarea && (
163
+ {textareaExpandable && (
160
164
  <Icon
161
165
  onClick={() => setExpanded(!isExpanded)}
162
166
  icon={isExpanded ? CoreIcons.arrowCollapse : CoreIcons.arrowExpand}
@@ -173,14 +177,7 @@ export const InputWrapper = ({
173
177
  className="flex-1 relative min-h-8"
174
178
  style={{ width, flexBasis: width }}
175
179
  >
176
- {!isExpanded && el}
177
- {isExpanded && (
178
- <FloatingPortal
179
- root={elRef.current?.closest<HTMLElement>(".theme-base")}
180
- >
181
- {el}
182
- </FloatingPortal>
183
- )}
180
+ {el}
184
181
  </div>
185
182
  </Wrapper>
186
183
  );
@@ -32,7 +32,7 @@ import {
32
32
  import { Chip } from "@react-fabric/core";
33
33
  import { isArray, isEmpty } from "@react-fabric/utilities";
34
34
  import classNames from "classnames";
35
- import { Fragment, useCallback, useId, useMemo } from "react";
35
+ import { Fragment, useCallback, useId, useLayoutEffect, useMemo } from "react";
36
36
  import { FieldWrapper } from "../internal/FieldWrapper";
37
37
  import { InputWrapper } from "../internal/InputWrapper";
38
38
  import { type SelectProps } from "../types";
@@ -55,6 +55,7 @@ export const List = <T extends AnyObject = string>({
55
55
  valueProperty,
56
56
  multiple,
57
57
  allowCreate,
58
+ searchable,
58
59
  createOption,
59
60
  matcher,
60
61
  renderer,
@@ -166,9 +167,12 @@ export const List = <T extends AnyObject = string>({
166
167
  }
167
168
  } else if (evt.key === "Backspace") {
168
169
  !state.query && handleRemove();
169
- } else if (["ArrowUp", "ArrowDown"].includes(evt.key)) {
170
- if (state.activeIndex === null) {
171
- setActiveIndex(state.selectedIndex);
170
+ } else if (!searchable || ["ArrowUp", "ArrowDown"].includes(evt.key)) {
171
+ if (
172
+ ["ArrowUp", "ArrowDown"].includes(evt.key) &&
173
+ state.activeIndex === null
174
+ ) {
175
+ setActiveIndex(state.selectedIndex ?? 0);
172
176
  }
173
177
  setTimeout(() => {
174
178
  props.onKeyDown?.(evt);
@@ -225,6 +229,15 @@ export const List = <T extends AnyObject = string>({
225
229
  }
226
230
  }, [state.value, state.query, placeholder, renderer]);
227
231
 
232
+ useLayoutEffect(() => {
233
+ setTimeout(() => {
234
+ state.selectedIndex &&
235
+ listRef.current[state.selectedIndex]?.scrollIntoView({
236
+ block: "center",
237
+ });
238
+ }, 100);
239
+ }, [state.selectedIndex]);
240
+
228
241
  return (
229
242
  <Wrapper {...wrapperProps}>
230
243
  <InputWrapper
@@ -240,22 +253,34 @@ export const List = <T extends AnyObject = string>({
240
253
  wrapperRef={refs.setReference}
241
254
  >
242
255
  <div
256
+ role="none"
243
257
  data-select-display="true"
244
- className="group flex-1 py-1 px-2 truncate text-start flex gap-1 flex-wrap"
258
+ className={classNames(
259
+ "group flex-1 py-1 px-2 truncate text-start flex gap-1 relative min-h-5 justify-start",
260
+ multiple ? "flex-wrap" : "flex-nowrap overflow-hidden",
261
+ )}
262
+ {...referenceProps}
263
+ onMouseUp={(e) =>
264
+ e.currentTarget.querySelector<HTMLElement>("input")?.focus()
265
+ }
245
266
  >
246
267
  {!hideSelected && displayValue}
247
268
  <input
269
+ readOnly={!searchable || readOnly}
248
270
  disabled={disabled}
249
271
  value={state.query ?? ""}
250
272
  aria-invalid={invalid}
251
273
  data-testid={name}
252
274
  name={name}
275
+ size={1}
276
+ ref={ref}
277
+ autoFocus={autoFocus}
253
278
  className={classNames(
254
279
  "appearance-none bg-transparent outline-none border-none ring-0 flex-1 min-w-24",
255
280
  disabled && "cursor-not-allowed pointer-events-none",
281
+ multiple && "min-w-24",
256
282
  )}
257
283
  placeholder={isEmpty(state.value) ? placeholder : ""}
258
- {...referenceProps}
259
284
  onChange={(evt) => handleQuery(evt.target.value)}
260
285
  />
261
286
  </div>
@@ -277,10 +302,12 @@ export const List = <T extends AnyObject = string>({
277
302
  {...floatingProps}
278
303
  items={state.items}
279
304
  active={state.activeIndex}
305
+ valueProperty={valueProperty}
306
+ labelProperty={labelProperty}
280
307
  itemRef={setItemRef}
281
308
  itemProps={makeItemProps}
282
309
  >
283
- {(item) => renderer?.(item) ?? item[labelProperty] ?? item}
310
+ {(item, label) => renderer?.(item) ?? label}
284
311
  </Options>
285
312
  </FloatingFocusManager>
286
313
  </Wrapper>
@@ -93,7 +93,7 @@ export const Options = ({
93
93
  >
94
94
  <Trans
95
95
  i18nKey="form:select.createOption"
96
- values={{ query: item.value }}
96
+ values={{ query: item.value ?? item }}
97
97
  components={[<strong key="t0">query</strong>]}
98
98
  />
99
99
  </div>
@@ -172,6 +172,9 @@ export const Select = <T extends AnyObject = string>({
172
172
  return false;
173
173
  } else if (state.query) {
174
174
  handleQuery("");
175
+ evt.preventDefault();
176
+ evt.stopPropagation();
177
+ return false;
175
178
  } else {
176
179
  onEnterPressed?.(evt);
177
180
  }
@@ -273,6 +276,7 @@ export const Select = <T extends AnyObject = string>({
273
276
  "group flex-1 py-1 px-2 truncate text-start flex gap-1 relative min-h-5 justify-start",
274
277
  multiple ? "flex-wrap" : "flex-nowrap overflow-hidden",
275
278
  )}
279
+ {...referenceProps}
276
280
  onMouseUp={(e) =>
277
281
  e.currentTarget.querySelector<HTMLElement>("input")?.focus()
278
282
  }
@@ -294,7 +298,6 @@ export const Select = <T extends AnyObject = string>({
294
298
  multiple && "min-w-24",
295
299
  )}
296
300
  placeholder={isEmpty(state.value) ? placeholder : ""}
297
- {...referenceProps}
298
301
  onChange={(evt) => handleQuery(evt.target.value)}
299
302
  />
300
303
  </div>
@@ -23,10 +23,12 @@
23
23
 
24
24
  import { useDebounce } from "@react-fabric/core";
25
25
  import {
26
+ compareValues,
26
27
  dedupe,
27
28
  EMPTY_ARRAY,
28
29
  groupBy,
29
30
  isArray,
31
+ isString,
30
32
  matchString,
31
33
  } from "@react-fabric/utilities";
32
34
  import {
@@ -173,9 +175,9 @@ export const useSelect = ({
173
175
  if (action.value !== undefined) {
174
176
  if (multiple) {
175
177
  const values = [];
176
- if (action.value && autoComplete)
178
+ if (isString(action.value))
177
179
  values.push(...action.value.split(/,\s?/));
178
- else if (action.value) values.push(action.value);
180
+ else values.push(action.value);
179
181
  newState.value = [...state.value];
180
182
  values.forEach((value: AnyObject) => {
181
183
  newState.value = newState.value.includes?.(value)
@@ -222,6 +224,7 @@ export const useSelect = ({
222
224
  if (autoComplete && action.query !== undefined) {
223
225
  optionList = dedupe([action.query, ...optionList]);
224
226
  }
227
+ optionList = optionList.sort(compareValues("asc", labelProperty));
225
228
  const grouped = groupBy(optionList, groupProperty, "");
226
229
  if (action.open) newState.open = true;
227
230
  newState.items = [];
@@ -229,7 +232,7 @@ export const useSelect = ({
229
232
  if (allowCreate && action.query)
230
233
  newState.items.push({
231
234
  ___create___: true,
232
- value: createOption(action.query),
235
+ value: action.query,
233
236
  });
234
237
  Object.entries(grouped).forEach(([key, list]) => {
235
238
  if (key) {
@@ -282,24 +285,26 @@ export const useSelect = ({
282
285
  dispatch({ type: "options", options: optionsRef.current });
283
286
  dispatch({
284
287
  type: "setValue",
285
- value: hasValue ?? newOption ?? "",
288
+ value: hasValue ?? newOption ?? deferred,
286
289
  });
287
290
  }
288
291
  if (multiple) {
289
292
  optionsRef.current = [];
290
- const value = (deferred ?? []).map((item: AnyObject) => {
291
- const newOption =
292
- (allowCreate || autoComplete) && item
293
- ? createOption(item)
294
- : undefined;
295
- const hasValue = options.find(
296
- (opt: AnyObject) => (opt[valueProperty] ?? opt) === deferred,
297
- );
298
- if (!hasValue && newOption) {
299
- optionsRef.current.push(newOption);
300
- }
301
- return hasValue ?? newOption ?? "";
302
- });
293
+ const value = (isArray(deferred) ? deferred : []).map(
294
+ (item: AnyObject) => {
295
+ const newOption =
296
+ (allowCreate || autoComplete) && item
297
+ ? createOption(item)
298
+ : undefined;
299
+ const hasValue = options.find(
300
+ (opt: AnyObject) => (opt[valueProperty] ?? opt) === item,
301
+ );
302
+ if (!hasValue && newOption) {
303
+ optionsRef.current.push(newOption);
304
+ }
305
+ return hasValue ?? newOption ?? item;
306
+ },
307
+ );
303
308
  optionsRef.current = [...optionsRef.current, ...(options ?? [])];
304
309
  dispatch({ type: "options", options: optionsRef.current });
305
310
  dispatch({
@@ -367,7 +372,11 @@ export const useSelect = ({
367
372
  value: value?.___create___ ? value.value : value,
368
373
  });
369
374
  if (value?.___create___) {
370
- optionsRef.current = [...optionsRef.current, createOption(value.value)];
375
+ let val = [value.value];
376
+ if (multiple) {
377
+ val = value.value.split(/,\s?/);
378
+ }
379
+ optionsRef.current = [...optionsRef.current, ...val.map(createOption)];
371
380
  }
372
381
  if (autoComplete) {
373
382
  optionsRef.current = dedupe([
@@ -21,9 +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 { type RefProp } from "@react-fabric/core/dist/types/types";
24
+ import {
25
+ type RefProp,
26
+ type TestProps,
27
+ } from "@react-fabric/core/dist/types/types";
25
28
 
26
- export interface InputProps<V = string> {
29
+ export interface InputProps<V = string> extends TestProps {
27
30
  /**
28
31
  * value
29
32
  */