@react-fabric/form 1.0.0-beta.55 → 1.0.0-beta.57

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 (67) hide show
  1. package/css/_field.css +1 -1
  2. package/dist/cjs/builder/FileField.js +2 -2
  3. package/dist/cjs/input/AvatarInput.js +18 -16
  4. package/dist/cjs/input/ColorInput.js +6 -3
  5. package/dist/cjs/input/DateInput.js +6 -4
  6. package/dist/cjs/input/Hidden.js +4 -3
  7. package/dist/cjs/input/Input.js +6 -4
  8. package/dist/cjs/input/Number.js +6 -4
  9. package/dist/cjs/input/Password.js +6 -4
  10. package/dist/cjs/input/Search.js +6 -4
  11. package/dist/cjs/input/Slider.js +2 -2
  12. package/dist/cjs/input/Textarea.js +7 -5
  13. package/dist/cjs/select/AutoComplete.js +1 -2
  14. package/dist/cjs/select/List.js +5 -2
  15. package/dist/cjs/select/Options.js +49 -42
  16. package/dist/cjs/select/Select.js +4 -2
  17. package/dist/cjs/select/useSelect.js +14 -7
  18. package/dist/esm/builder/FileField.js +3 -3
  19. package/dist/esm/input/AvatarInput.js +19 -17
  20. package/dist/esm/input/ColorInput.js +6 -3
  21. package/dist/esm/input/DateInput.js +6 -4
  22. package/dist/esm/input/Hidden.js +4 -3
  23. package/dist/esm/input/Input.js +6 -4
  24. package/dist/esm/input/Number.js +6 -4
  25. package/dist/esm/input/Password.js +6 -4
  26. package/dist/esm/input/Search.js +6 -4
  27. package/dist/esm/input/Slider.js +2 -2
  28. package/dist/esm/input/Textarea.js +7 -5
  29. package/dist/esm/select/AutoComplete.js +1 -2
  30. package/dist/esm/select/List.js +5 -2
  31. package/dist/esm/select/Options.js +49 -42
  32. package/dist/esm/select/Select.js +4 -2
  33. package/dist/esm/select/useSelect.js +14 -7
  34. package/dist/types/input/AvatarInput.d.ts +5 -1
  35. package/dist/types/input/DateInput.d.ts +1 -1
  36. package/dist/types/input/Hidden.d.ts +5 -1
  37. package/dist/types/input/Input.d.ts +1 -1
  38. package/dist/types/input/Number.d.ts +1 -1
  39. package/dist/types/input/Password.d.ts +1 -1
  40. package/dist/types/input/RangeSlider.d.ts +1 -1
  41. package/dist/types/input/Search.d.ts +1 -1
  42. package/dist/types/input/Slider.d.ts +1 -1
  43. package/dist/types/input/Textarea.d.ts +1 -1
  44. package/dist/types/select/List.d.ts +1 -1
  45. package/dist/types/select/Options.d.ts +2 -1
  46. package/dist/types/select/Select.d.ts +1 -1
  47. package/dist/types/select/useSelect.d.ts +4 -1
  48. package/dist/types/types/index.d.ts +8 -0
  49. package/package.json +4 -4
  50. package/src/builder/FileField.tsx +9 -3
  51. package/src/input/AvatarInput.tsx +23 -11
  52. package/src/input/ColorInput.tsx +6 -3
  53. package/src/input/DateInput.tsx +4 -3
  54. package/src/input/Hidden.tsx +9 -5
  55. package/src/input/Input.tsx +4 -3
  56. package/src/input/Number.tsx +4 -3
  57. package/src/input/Password.tsx +4 -3
  58. package/src/input/RangeSlider.tsx +1 -1
  59. package/src/input/Search.tsx +4 -3
  60. package/src/input/Slider.tsx +8 -5
  61. package/src/input/Textarea.tsx +5 -4
  62. package/src/select/AutoComplete.tsx +1 -1
  63. package/src/select/List.tsx +3 -0
  64. package/src/select/Options.tsx +47 -43
  65. package/src/select/Select.tsx +2 -0
  66. package/src/select/useSelect.tsx +15 -6
  67. package/src/types/index.ts +8 -0
@@ -33,6 +33,10 @@ interface Props {
33
33
  * hidden value will fire change for form update
34
34
  */
35
35
  hiddenValue?: AnyObject;
36
+ /**
37
+ * default avatar image
38
+ */
39
+ defaultValue?: string;
36
40
  /**
37
41
  * form onChange handler
38
42
  */
@@ -49,6 +53,7 @@ interface Props {
49
53
  */
50
54
  export const HiddenInput = ({
51
55
  hiddenValue,
56
+ defaultValue,
52
57
  onChange,
53
58
  // @ts-expect-error ignore
54
59
  // eslint-disable-next-line react/prop-types
@@ -62,11 +67,10 @@ export const HiddenInput = ({
62
67
  name,
63
68
  }: Props) => {
64
69
  useEffect(() => {
65
- hiddenValue &&
66
- setTimeout(() => {
67
- onChange?.(hiddenValue);
68
- }, 50);
69
- }, [hiddenValue]);
70
+ setTimeout(() => {
71
+ onChange?.(hiddenValue ?? defaultValue);
72
+ }, 50);
73
+ }, [hiddenValue, defaultValue]);
70
74
  return (
71
75
  <Fragment>
72
76
  <input type="hidden" onChange={onChange} value={value} name={name} />
@@ -58,6 +58,7 @@ export const Input = ({
58
58
  disabled,
59
59
  required,
60
60
  placeholder,
61
+ defaultValue,
61
62
  autoFocus,
62
63
  error,
63
64
  monospace,
@@ -69,7 +70,7 @@ export const Input = ({
69
70
  ...rest
70
71
  }: TextInputProps) => {
71
72
  const [actualValue, setActualValue] = useState("");
72
- const deferred = useDeferredValue(value);
73
+ const deferred = useDeferredValue(value ?? defaultValue);
73
74
 
74
75
  const handleEnterPressed = useHandleEnter(onEnterPressed);
75
76
 
@@ -79,11 +80,11 @@ export const Input = ({
79
80
 
80
81
  const handleChange = useCallback(
81
82
  (e?: ChangeEvent<HTMLInputElement>) => {
82
- const value = e?.target.value;
83
+ const value = e?.target.value ?? defaultValue;
83
84
  setActualValue(value ?? "");
84
85
  onChange?.(value ?? null);
85
86
  },
86
- [onchange],
87
+ [onChange, defaultValue],
87
88
  );
88
89
 
89
90
  return (
@@ -66,6 +66,7 @@ export const Number = ({
66
66
  disabled,
67
67
  required,
68
68
  placeholder,
69
+ defaultValue,
69
70
  autoFocus,
70
71
  name,
71
72
  min,
@@ -78,7 +79,7 @@ export const Number = ({
78
79
  ...rest
79
80
  }: NumberProps) => {
80
81
  const [actualValue, setActualValue] = useState<"" | number>("");
81
- const deferred = useDeferredValue(value);
82
+ const deferred = useDeferredValue(value ?? defaultValue);
82
83
 
83
84
  const handleEnterPressed = useHandleEnter(onEnterPressed);
84
85
 
@@ -88,11 +89,11 @@ export const Number = ({
88
89
 
89
90
  const handleChange = useCallback(
90
91
  (e?: ChangeEvent<HTMLInputElement>) => {
91
- const number = e?.target.valueAsNumber;
92
+ const number = e?.target.valueAsNumber ?? defaultValue;
92
93
  setActualValue(number ?? "");
93
94
  onChange?.(number ?? null);
94
95
  },
95
- [onChange],
96
+ [onChange, defaultValue],
96
97
  );
97
98
 
98
99
  return (
@@ -63,6 +63,7 @@ export const Password = ({
63
63
  disabled,
64
64
  required,
65
65
  placeholder,
66
+ defaultValue,
66
67
  autoFocus,
67
68
  showToggle,
68
69
  onBlur,
@@ -75,7 +76,7 @@ export const Password = ({
75
76
  const [show, setShow] = useState(false);
76
77
 
77
78
  const [actualValue, setActualValue] = useState("");
78
- const deferred = useDeferredValue(value);
79
+ const deferred = useDeferredValue(value ?? defaultValue);
79
80
 
80
81
  const handleEnterPressed = useHandleEnter(onEnterPressed);
81
82
 
@@ -85,11 +86,11 @@ export const Password = ({
85
86
 
86
87
  const handleChange = useCallback(
87
88
  (e?: ChangeEvent<HTMLInputElement>) => {
88
- const value = e?.target.value;
89
+ const value = e?.target.value ?? defaultValue;
89
90
  setActualValue(value ?? "");
90
91
  onChange?.(value ?? null);
91
92
  },
92
- [onChange],
93
+ [onChange, defaultValue],
93
94
  );
94
95
 
95
96
  return (
@@ -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
@@ -74,6 +74,7 @@ export const Search = ({
74
74
  required,
75
75
  searching,
76
76
  placeholder,
77
+ defaultValue,
77
78
  autoFocus,
78
79
  hideHandle,
79
80
  allowClear = true,
@@ -86,7 +87,7 @@ export const Search = ({
86
87
  ...rest
87
88
  }: SearchProps) => {
88
89
  const [actualValue, setActualValue] = useState("");
89
- const deferred = useDeferredValue(value);
90
+ const deferred = useDeferredValue(value ?? defaultValue);
90
91
 
91
92
  const refValue = useRef("");
92
93
  const handleSearch = useDebounce(
@@ -103,7 +104,7 @@ export const Search = ({
103
104
 
104
105
  const handleChange = useCallback(
105
106
  (e?: ChangeEvent<HTMLInputElement>) => {
106
- const value = e?.target.value;
107
+ const value = e?.target.value ?? defaultValue;
107
108
  refValue.current = value ?? "";
108
109
  setActualValue(value ?? "");
109
110
  onChange?.(value ?? null);
@@ -113,7 +114,7 @@ export const Search = ({
113
114
  handleSearch();
114
115
  }
115
116
  },
116
- [onChange, searchOnChange, handleSearch],
117
+ [onChange, searchOnChange, handleSearch, defaultValue],
117
118
  );
118
119
 
119
120
  return (
@@ -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
  */
@@ -211,8 +214,8 @@ export const Slider = ({
211
214
  <div
212
215
  ref={rangerInstance.trackElRef}
213
216
  className={classNames(
214
- "flex-1 relative flex items-center",
215
- 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",
216
219
  )}
217
220
  style={{ height: vertical ? height : undefined }}
218
221
  >
@@ -259,10 +262,10 @@ export const Slider = ({
259
262
  {displayValue && (
260
263
  <div
261
264
  className={classNames(
262
- "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",
263
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",
264
267
  vertical
265
- ? "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.125rem] rtl:before:translate-x-[0.125rem] before:start-0"
266
269
  : "top-1/2 -translate-y-1/2",
267
270
  !vertical &&
268
271
  (percentage > 50
@@ -74,6 +74,7 @@ export const Textarea = ({
74
74
  disabled,
75
75
  required,
76
76
  placeholder,
77
+ defaultValue,
77
78
  autoFocus,
78
79
  rows = 5,
79
80
  maxRows = 12,
@@ -87,7 +88,7 @@ export const Textarea = ({
87
88
  }: TextareaProps) => {
88
89
  const [actualValue, setActualValue] = useState("");
89
90
  const [fixedRows, setFixedRows] = useState(rows);
90
- const deferred = useDeferredValue(value);
91
+ const deferred = useDeferredValue(value ?? defaultValue);
91
92
  const wrapperRef = useRef(null);
92
93
 
93
94
  useEffect(() => {
@@ -96,11 +97,11 @@ export const Textarea = ({
96
97
 
97
98
  const handleChange = useCallback(
98
99
  (e?: ChangeEvent<HTMLTextAreaElement>) => {
99
- const value = e?.target.value;
100
+ const value = e?.target.value ?? defaultValue;
100
101
  setActualValue(value ?? "");
101
102
  onChange?.(value ?? null);
102
103
  },
103
- [onChange],
104
+ [onChange, defaultValue],
104
105
  );
105
106
 
106
107
  useEffect(() => {
@@ -130,7 +131,7 @@ export const Textarea = ({
130
131
  {...rest}
131
132
  >
132
133
  <textarea
133
- 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"
134
135
  aria-invalid={invalid}
135
136
  aria-disabled={disabled}
136
137
  aria-readonly={readOnly}
@@ -190,7 +190,7 @@ export const AutoComplete = ({
190
190
  return false;
191
191
  } else {
192
192
  onEnterPressed?.(evt);
193
- submitRef.current?.click();
193
+ setOpen(false);
194
194
  }
195
195
  } else if (evt.key === "Backspace") {
196
196
  !state.query && multiple && handleRemove();
@@ -61,6 +61,7 @@ export const List = <T extends AnyObject = string>({
61
61
  groupProperty,
62
62
  labelProperty,
63
63
  valueProperty,
64
+ infoProperty,
64
65
  sortProperty,
65
66
  multiple,
66
67
  allowCreate,
@@ -106,6 +107,7 @@ export const List = <T extends AnyObject = string>({
106
107
  value,
107
108
  options,
108
109
  multiple,
110
+ alwaysOpen: true,
109
111
  groupProperty,
110
112
  labelProperty,
111
113
  valueProperty,
@@ -320,6 +322,7 @@ export const List = <T extends AnyObject = string>({
320
322
  itemRef={setItemRef}
321
323
  itemProps={makeItemProps}
322
324
  empty={emptyDisplay}
325
+ info={state.activeItem?.[infoProperty ?? "info"]}
323
326
  >
324
327
  {(item, label) => renderer?.(item) ?? label}
325
328
  </Options>
@@ -34,6 +34,7 @@ export const Options = ({
34
34
  className,
35
35
  itemRef,
36
36
  empty,
37
+ info,
37
38
  valueProperty = "id",
38
39
  labelProperty = "label",
39
40
  itemProps,
@@ -41,6 +42,7 @@ export const Options = ({
41
42
  ...rest
42
43
  }: {
43
44
  ref: AnyObject;
45
+ info?: AnyObject;
44
46
  itemRef: AnyObject;
45
47
  style?: AnyObject;
46
48
  items: AnyObject[];
@@ -55,29 +57,48 @@ export const Options = ({
55
57
  return (
56
58
  <div
57
59
  ref={ref}
58
- className={classNames(
59
- "bg-base select-none overflow-x-hidden overflow-y-auto",
60
- className,
61
- )}
60
+ className={classNames("bg-base select-none flex flex-col", className)}
62
61
  style={style}
63
62
  {...rest}
64
63
  >
65
- {items.length === 0 &&
66
- (empty ?? (
67
- <EmptyContent message={<Trans i18nKey="form:select.emptyList" />} />
68
- ))}
69
- {items.map((item, index) => {
70
- if (item.___group___)
71
- return (
72
- <p
73
- key={index}
74
- className="px-2 py-1 text-muted text-xs sticky -top-px z-2 bg-base border-t"
75
- >
76
- {item.label}
77
- </p>
78
- );
64
+ <div className={classNames("overflow-x-hidden overflow-y-auto flex-1")}>
65
+ {items.length === 0 &&
66
+ (empty ?? (
67
+ <EmptyContent message={<Trans i18nKey="form:select.emptyList" />} />
68
+ ))}
69
+ {items.map((item, index) => {
70
+ if (item.___group___)
71
+ return (
72
+ <p
73
+ key={index}
74
+ className="px-2 py-1 text-muted text-xs sticky -top-px z-2 bg-base border-t"
75
+ >
76
+ {item.label}
77
+ </p>
78
+ );
79
+
80
+ if (item.___create___)
81
+ return (
82
+ <div
83
+ role="none"
84
+ key={index}
85
+ tabIndex={active === index ? 0 : -1}
86
+ ref={(el: AnyObject) => itemRef(index, el)}
87
+ data-active={active === index ? true : undefined}
88
+ className={classNames(
89
+ "block text-start truncate px-2 py-1 leading-normal cursor-pointer select-none",
90
+ "data-[active]:bg-primary-200 data-[selected]:bg-primary-600 data-[selected]:text-white data-[active]:data-[selected]:underline",
91
+ )}
92
+ {...itemProps(item)}
93
+ >
94
+ <Trans
95
+ i18nKey="form:select.createOption"
96
+ values={{ query: item.value ?? item }}
97
+ components={[<strong key="t0">query</strong>]}
98
+ />
99
+ </div>
100
+ );
79
101
 
80
- if (item.___create___)
81
102
  return (
82
103
  <div
83
104
  role="none"
@@ -86,36 +107,19 @@ export const Options = ({
86
107
  ref={(el: AnyObject) => itemRef(index, el)}
87
108
  data-active={active === index ? true : undefined}
88
109
  className={classNames(
89
- "block text-start truncate px-2 py-1 leading-normal cursor-pointer select-none",
110
+ "block text-start truncate px-2 py-1 leading-normal cursor-pointer",
90
111
  "data-[active]:bg-primary-200 data-[selected]:bg-primary-600 data-[selected]:text-white data-[active]:data-[selected]:underline",
91
112
  )}
92
113
  {...itemProps(item)}
93
114
  >
94
- <Trans
95
- i18nKey="form:select.createOption"
96
- values={{ query: item.value ?? item }}
97
- components={[<strong key="t0">query</strong>]}
98
- />
115
+ {children(item, item[labelProperty] ?? item)}
99
116
  </div>
100
117
  );
101
-
102
- return (
103
- <div
104
- role="none"
105
- key={index}
106
- tabIndex={active === index ? 0 : -1}
107
- ref={(el: AnyObject) => itemRef(index, el)}
108
- data-active={active === index ? true : undefined}
109
- className={classNames(
110
- "block text-start truncate px-2 py-1 leading-normal cursor-pointer",
111
- "data-[active]:bg-primary-200 data-[selected]:bg-primary-600 data-[selected]:text-white data-[active]:data-[selected]:underline",
112
- )}
113
- {...itemProps(item)}
114
- >
115
- {children(item, item[labelProperty] ?? item)}
116
- </div>
117
- );
118
- })}
118
+ })}
119
+ </div>
120
+ {info && (
121
+ <div className="px-2 py-1 text-sm bg-dimmed text-muted">{info}</div>
122
+ )}
119
123
  </div>
120
124
  );
121
125
  };
@@ -70,6 +70,7 @@ export const Select = <T extends AnyObject = string>({
70
70
  labelProperty = "label" as AnyObject,
71
71
  valueProperty = "id" as AnyObject,
72
72
  sortProperty,
73
+ infoProperty,
73
74
  multiple,
74
75
  searchable,
75
76
  allowCreate,
@@ -357,6 +358,7 @@ export const Select = <T extends AnyObject = string>({
357
358
  itemRef={setItemRef}
358
359
  itemProps={makeItemProps}
359
360
  empty={emptyDisplay}
361
+ info={state.activeItem?.[infoProperty ?? "info"]}
360
362
  >
361
363
  {(item, label) => renderer?.(item) ?? label}
362
364
  </Options>
@@ -65,6 +65,7 @@ interface State {
65
65
  open: boolean;
66
66
  loading: boolean;
67
67
  activeIndex: number | null;
68
+ activeItem: AnyObject;
68
69
  selectedIndex: number | null;
69
70
  value: AnyObject;
70
71
  // filter
@@ -76,8 +77,9 @@ interface State {
76
77
  export const useSelect = ({
77
78
  options = EMPTY_ARRAY,
78
79
  value,
79
- defaultOpen = false,
80
80
  autoComplete,
81
+ alwaysOpen = false,
82
+ defaultOpen = false,
81
83
  multiple = false,
82
84
  allowCreate = false,
83
85
  groupProperty = "",
@@ -93,6 +95,7 @@ export const useSelect = ({
93
95
  value?: AnyObject;
94
96
  options?: AnyObject[];
95
97
  multiple?: boolean;
98
+ alwaysOpen?: boolean;
96
99
  defaultOpen?: boolean;
97
100
  allowCreate?: boolean;
98
101
  groupProperty?: AnyObject;
@@ -149,12 +152,16 @@ export const useSelect = ({
149
152
  };
150
153
  }
151
154
  if (action.type === "close") {
152
- return { ...state, activeIndex: null, open: false };
155
+ return { ...state, activeIndex: null, activeItem: null, open: false };
153
156
  }
154
157
  if (action.type === "active" && state.open) {
155
158
  if (autoComplete && action.index !== null)
156
159
  state.query = state.items[action.index];
157
- return { ...state, activeIndex: action.index };
160
+ return {
161
+ ...state,
162
+ activeIndex: action.index,
163
+ activeItem: state.items[action.index ?? -1],
164
+ };
158
165
  }
159
166
  if (action.type === "setValue") {
160
167
  return {
@@ -170,6 +177,7 @@ export const useSelect = ({
170
177
  ...state,
171
178
  value: state.emptyValue,
172
179
  activeIndex: null,
180
+ activeItem: null,
173
181
  selectedIndex: autoComplete
174
182
  ? state.multiple
175
183
  ? action.value?.length
@@ -192,7 +200,7 @@ export const useSelect = ({
192
200
  newState.value = action.value;
193
201
  }
194
202
  }
195
- if (!state.multiple) newState.open = false;
203
+ if (!state.multiple) newState.open = alwaysOpen;
196
204
  fireChange(newState.value);
197
205
  return newState;
198
206
  }
@@ -261,9 +269,10 @@ export const useSelect = ({
261
269
  {
262
270
  multiple,
263
271
  emptyValue,
264
- open: defaultOpen,
272
+ open: alwaysOpen || defaultOpen,
265
273
  loading: false,
266
274
  activeIndex: null,
275
+ activeItem: null,
267
276
  selectedIndex: null,
268
277
  value: emptyValue,
269
278
  // filter
@@ -407,7 +416,7 @@ export const useSelect = ({
407
416
  }, []);
408
417
 
409
418
  const setActiveIndex = useCallback((index: number | null) => {
410
- dispatch({ type: "active", index });
419
+ index && index > -1 && dispatch({ type: "active", index });
411
420
  }, []);
412
421
 
413
422
  return {
@@ -32,6 +32,10 @@ export interface InputProps<V = string> extends TestProps {
32
32
  * value
33
33
  */
34
34
  value?: V | null;
35
+ /**
36
+ * default value
37
+ */
38
+ defaultValue?: V | null;
35
39
  /**
36
40
  * change handler
37
41
  */
@@ -150,6 +154,10 @@ export interface BaseSelectProps<T extends AnyObject = string> {
150
154
  * value property name
151
155
  */
152
156
  valueProperty?: keyof T;
157
+ /**
158
+ * value property name
159
+ */
160
+ infoProperty?: keyof T;
153
161
  /**
154
162
  * value property name
155
163
  */