@react-fabric/form 1.0.0-beta.9 → 1.0.2

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 (215) hide show
  1. package/{src/input/ArrayInput.module.css → css/_arrayInput.css} +8 -8
  2. package/css/_colorPicker.css +95 -0
  3. package/{src/internal/Field.module.css → css/_field.css} +94 -33
  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 +107 -78
  7. package/dist/cjs/builder/FormField.js +60 -39
  8. package/dist/cjs/builder/SchemaEditor.js +72 -0
  9. package/dist/cjs/builder/components/AddSchemaFields.js +64 -0
  10. package/dist/cjs/builder/components/SchemaArrayForm.js +146 -0
  11. package/dist/cjs/builder/components/SchemaField.js +423 -0
  12. package/dist/cjs/form/Controller.js +66 -19
  13. package/dist/cjs/form/Form.js +97 -39
  14. package/dist/cjs/form/useForm.js +32 -0
  15. package/dist/cjs/hooks/useFileUploader.js +74 -45
  16. package/dist/cjs/hooks/useFormBuilder.js +85 -31
  17. package/dist/cjs/i18n/{ar.json.js → ar/form.json.js} +5 -4
  18. package/dist/cjs/i18n/{en.json.js → en/form.json.js} +5 -5
  19. package/dist/cjs/i18n/index.js +4 -4
  20. package/dist/cjs/index.js +41 -2
  21. package/dist/cjs/input/ArrayInput.js +259 -135
  22. package/dist/cjs/input/AvatarInput.js +99 -63
  23. package/dist/cjs/input/Checkbox.js +24 -25
  24. package/dist/cjs/input/ColorInput.js +138 -81
  25. package/dist/cjs/input/ColorPicker.js +78 -95
  26. package/dist/cjs/input/DateInput.js +192 -0
  27. package/dist/cjs/input/Field.js +21 -20
  28. package/dist/cjs/input/File.js +85 -80
  29. package/dist/cjs/input/Hidden.js +27 -13
  30. package/dist/cjs/input/Input.js +49 -48
  31. package/dist/cjs/input/Number.js +46 -44
  32. package/dist/cjs/input/Password.js +51 -49
  33. package/dist/cjs/input/Radio.js +26 -16
  34. package/dist/cjs/input/RadioGroup.js +26 -11
  35. package/dist/cjs/input/{Range.js → RangeSlider.js} +61 -66
  36. package/dist/cjs/input/Search.js +58 -64
  37. package/dist/cjs/input/Slider.js +57 -66
  38. package/dist/cjs/input/Switch.js +29 -27
  39. package/dist/cjs/input/Textarea.js +65 -53
  40. package/dist/cjs/internal/ErrorIcon.js +9 -9
  41. package/dist/cjs/internal/FieldWrapper.js +23 -13
  42. package/dist/cjs/internal/InputWrapper.js +85 -87
  43. package/dist/cjs/ranger/index.js +7 -7
  44. package/dist/cjs/ranger/utils.js +9 -4
  45. package/dist/cjs/select/AutoComplete.js +93 -76
  46. package/dist/cjs/select/DualList.js +235 -154
  47. package/dist/cjs/select/List.js +122 -89
  48. package/dist/cjs/select/Options.js +58 -54
  49. package/dist/cjs/select/Select.js +119 -89
  50. package/dist/cjs/select/helpers.js +5 -5
  51. package/dist/cjs/select/useSelect.js +83 -58
  52. package/dist/cjs/types/schema.js +43 -0
  53. package/dist/cjs/utils/eventHandlers.js +16 -6
  54. package/dist/cjs/utils/getColor.js +6 -6
  55. package/dist/cjs/utils/getProps.js +49 -0
  56. package/dist/esm/builder/FileField.js +109 -80
  57. package/dist/esm/builder/FormField.js +60 -39
  58. package/dist/esm/builder/SchemaEditor.js +72 -0
  59. package/dist/esm/builder/components/AddSchemaFields.js +64 -0
  60. package/dist/esm/builder/components/SchemaArrayForm.js +146 -0
  61. package/dist/esm/builder/components/SchemaField.js +423 -0
  62. package/dist/esm/form/Controller.js +66 -19
  63. package/dist/esm/form/Form.js +97 -39
  64. package/dist/esm/form/useForm.js +32 -0
  65. package/dist/esm/hooks/useFileUploader.js +75 -46
  66. package/dist/esm/hooks/useFormBuilder.js +84 -30
  67. package/dist/esm/i18n/{ar.json.js → ar/form.json.js} +5 -4
  68. package/dist/esm/i18n/{en.json.js → en/form.json.js} +5 -5
  69. package/dist/esm/i18n/index.js +2 -2
  70. package/dist/esm/index.js +36 -1
  71. package/dist/esm/input/ArrayInput.js +260 -136
  72. package/dist/esm/input/AvatarInput.js +99 -63
  73. package/dist/esm/input/Checkbox.js +26 -27
  74. package/dist/esm/input/ColorInput.js +141 -84
  75. package/dist/esm/input/ColorPicker.js +80 -97
  76. package/dist/esm/input/DateInput.js +192 -0
  77. package/dist/esm/input/Field.js +21 -20
  78. package/dist/esm/input/File.js +89 -84
  79. package/dist/esm/input/Hidden.js +28 -14
  80. package/dist/esm/input/Input.js +51 -50
  81. package/dist/esm/input/Number.js +48 -46
  82. package/dist/esm/input/Password.js +54 -52
  83. package/dist/esm/input/Radio.js +27 -17
  84. package/dist/esm/input/RadioGroup.js +27 -12
  85. package/dist/esm/input/{Range.js → RangeSlider.js} +62 -67
  86. package/dist/esm/input/Search.js +61 -67
  87. package/dist/esm/input/Slider.js +58 -67
  88. package/dist/esm/input/Switch.js +31 -29
  89. package/dist/esm/input/Textarea.js +67 -55
  90. package/dist/esm/internal/ErrorIcon.js +11 -11
  91. package/dist/esm/internal/FieldWrapper.js +23 -13
  92. package/dist/esm/internal/InputWrapper.js +88 -90
  93. package/dist/esm/ranger/index.js +7 -7
  94. package/dist/esm/ranger/utils.js +9 -4
  95. package/dist/esm/select/AutoComplete.js +95 -78
  96. package/dist/esm/select/DualList.js +233 -152
  97. package/dist/esm/select/List.js +125 -92
  98. package/dist/esm/select/Options.js +58 -54
  99. package/dist/esm/select/Select.js +122 -92
  100. package/dist/esm/select/helpers.js +5 -5
  101. package/dist/esm/select/useSelect.js +84 -59
  102. package/{src/input/ColorPicker.module.css → dist/esm/types/schema.js} +18 -27
  103. package/dist/esm/utils/eventHandlers.js +16 -6
  104. package/dist/esm/utils/getColor.js +6 -6
  105. package/dist/esm/utils/getProps.js +49 -0
  106. package/dist/types/builder/FileField.d.ts +7 -4
  107. package/dist/types/builder/FormField.d.ts +4 -2
  108. package/dist/types/builder/SchemaEditor.d.ts +6 -0
  109. package/dist/types/builder/components/AddSchemaFields.d.ts +2 -0
  110. package/dist/types/builder/components/SchemaArrayForm.d.ts +2 -0
  111. package/dist/types/builder/components/SchemaField.d.ts +12 -0
  112. package/dist/types/form/Controller.d.ts +45 -3
  113. package/dist/types/form/Form.d.ts +38 -7
  114. package/dist/types/form/useForm.d.ts +6 -0
  115. package/dist/types/hooks/useFileUploader.d.ts +24 -3
  116. package/dist/types/hooks/useFormBuilder.d.ts +41 -3
  117. package/dist/types/index.d.ts +32 -2
  118. package/dist/types/input/ArrayInput.d.ts +68 -13
  119. package/dist/types/input/AvatarInput.d.ts +23 -6
  120. package/dist/types/input/Checkbox.d.ts +12 -10
  121. package/dist/types/input/ColorInput.d.ts +15 -5
  122. package/dist/types/input/ColorPicker.d.ts +10 -2
  123. package/dist/types/input/DateInput.d.ts +21 -0
  124. package/dist/types/input/Field.d.ts +9 -36
  125. package/dist/types/input/File.d.ts +16 -4
  126. package/dist/types/input/Hidden.d.ts +7 -6
  127. package/dist/types/input/Input.d.ts +11 -4
  128. package/dist/types/input/Number.d.ts +9 -4
  129. package/dist/types/input/Password.d.ts +9 -4
  130. package/dist/types/input/Radio.d.ts +9 -4
  131. package/dist/types/input/RadioGroup.d.ts +14 -6
  132. package/dist/types/input/{Range.d.ts → RangeSlider.d.ts} +11 -6
  133. package/dist/types/input/Search.d.ts +14 -4
  134. package/dist/types/input/Slider.d.ts +9 -6
  135. package/dist/types/input/Switch.d.ts +13 -8
  136. package/dist/types/input/Textarea.d.ts +15 -4
  137. package/dist/types/internal/ErrorIcon.d.ts +4 -1
  138. package/dist/types/internal/FieldWrapper.d.ts +10 -1
  139. package/dist/types/internal/InputWrapper.d.ts +19 -1
  140. package/dist/types/ranger/index.d.ts +7 -3
  141. package/dist/types/ranger/utils.d.ts +2 -2
  142. package/dist/types/select/AutoComplete.d.ts +7 -2
  143. package/dist/types/select/DualList.d.ts +11 -37
  144. package/dist/types/select/List.d.ts +7 -2
  145. package/dist/types/select/Options.d.ts +8 -7
  146. package/dist/types/select/Select.d.ts +7 -2
  147. package/dist/types/select/helpers.d.ts +2 -2
  148. package/dist/types/select/useSelect.d.ts +10 -28
  149. package/dist/types/types/index.d.ts +73 -39
  150. package/dist/types/types/schema.d.ts +78 -32
  151. package/dist/types/utils/eventHandlers.d.ts +4 -4
  152. package/dist/types/utils/getColor.d.ts +4 -4
  153. package/dist/types/utils/getProps.d.ts +26 -0
  154. package/dist/types/utils/index.d.ts +1 -0
  155. package/package.json +25 -25
  156. package/src/builder/FileField.tsx +73 -56
  157. package/src/builder/FormField.tsx +66 -58
  158. package/src/builder/SchemaEditor.tsx +78 -0
  159. package/src/builder/components/AddSchemaFields.tsx +64 -0
  160. package/src/builder/components/SchemaArrayForm.tsx +146 -0
  161. package/src/builder/components/SchemaField.tsx +405 -0
  162. package/src/form/Controller.tsx +61 -24
  163. package/src/form/Form.tsx +74 -50
  164. package/src/form/useForm.ts +53 -0
  165. package/src/hooks/useFileUploader.ts +115 -84
  166. package/src/hooks/useFormBuilder.tsx +80 -41
  167. package/src/i18n/{ar.json → ar/form.json} +5 -4
  168. package/src/i18n/{en.json → en/form.json} +5 -5
  169. package/src/i18n/index.ts +2 -2
  170. package/src/index.ts +34 -20
  171. package/src/input/ArrayInput.tsx +255 -173
  172. package/src/input/AvatarInput.tsx +82 -39
  173. package/src/input/Checkbox.tsx +30 -54
  174. package/src/input/ColorInput.tsx +146 -105
  175. package/src/input/ColorPicker.tsx +76 -102
  176. package/src/input/DateInput.tsx +225 -0
  177. package/src/input/Field.tsx +23 -65
  178. package/src/input/File.tsx +76 -98
  179. package/src/input/Hidden.tsx +36 -24
  180. package/src/input/Input.tsx +55 -53
  181. package/src/input/Number.tsx +49 -54
  182. package/src/input/Password.tsx +61 -66
  183. package/src/input/Radio.tsx +28 -37
  184. package/src/input/RadioGroup.tsx +30 -25
  185. package/src/input/{Range.tsx → RangeSlider.tsx} +81 -109
  186. package/src/input/Search.tsx +64 -77
  187. package/src/input/Slider.tsx +80 -109
  188. package/src/input/Switch.tsx +39 -53
  189. package/src/input/Textarea.tsx +72 -59
  190. package/src/internal/ErrorIcon.tsx +22 -21
  191. package/src/internal/FieldWrapper.tsx +33 -23
  192. package/src/internal/InputWrapper.tsx +132 -143
  193. package/src/ranger/index.tsx +37 -117
  194. package/src/ranger/utils.ts +12 -11
  195. package/src/select/AutoComplete.tsx +113 -123
  196. package/src/select/DualList.tsx +218 -204
  197. package/src/select/List.tsx +130 -97
  198. package/src/select/Options.tsx +59 -57
  199. package/src/select/Select.tsx +148 -122
  200. package/src/select/helpers.ts +13 -8
  201. package/src/select/useSelect.tsx +110 -127
  202. package/src/types/index.ts +76 -45
  203. package/src/types/schema.ts +97 -40
  204. package/src/utils/eventHandlers.ts +24 -26
  205. package/src/utils/getColor.ts +9 -8
  206. package/src/utils/getProps.ts +47 -0
  207. package/src/utils/index.ts +1 -0
  208. package/dist/cjs/input/ArrayInput.module.css +0 -1
  209. package/dist/cjs/input/ColorPicker.module.css +0 -1
  210. package/dist/cjs/input/File.module.css +0 -1
  211. package/dist/cjs/internal/Field.module.css +0 -1
  212. package/dist/esm/input/ArrayInput.module.css +0 -1
  213. package/dist/esm/input/ColorPicker.module.css +0 -1
  214. package/dist/esm/input/File.module.css +0 -1
  215. package/dist/esm/internal/Field.module.css +0 -1
@@ -35,42 +35,51 @@ import {
35
35
  useListNavigation,
36
36
  useTypeahead,
37
37
  } from "@floating-ui/react";
38
- import { Chip, CoreIcons, Icon } from "@react-fabric/core";
39
- import { isArray, isEmpty } from "@react-fabric/utilities";
40
- import classNames from "classnames";
38
+ import { Chip, Icon } from "@react-fabric/core";
39
+ import { cn, getByPath, isArray, isEmpty } from "@react-fabric/utilities";
41
40
  import { useCallback, useId, useLayoutEffect, useMemo } from "react";
42
41
  import { InputWrapper } from "../internal/InputWrapper";
43
- import { type SelectProps } from "../types";
42
+ import type { SelectProps } from "../types";
43
+ import { getWrapperProps } from "../utils";
44
44
  import { Options } from "./Options";
45
45
  import { useSelect } from "./useSelect";
46
46
 
47
- export const Select = <T extends AnyObject = string>({
48
- ref,
49
- name,
50
- value,
51
- invalid,
52
- readOnly,
53
- disabled,
54
- required,
55
- placeholder,
56
- autoFocus,
57
- error,
58
- options,
59
- groupProperty,
60
- labelProperty = "label" as AnyObject,
61
- valueProperty = "id" as AnyObject,
62
- multiple,
63
- searchable,
64
- allowCreate,
65
- createOption,
66
- matcher,
67
- renderer,
68
- onQuery,
69
- onChange,
70
- onSelect,
71
- onEnterPressed,
72
- ...rest
73
- }: SelectProps<T>) => {
47
+ /**
48
+ * Select component for selecting items from a dropdown list.
49
+ * It supports single and multiple selections, searching, creating new options,
50
+ * and custom rendering of options.
51
+ */
52
+ export function Select<T extends AnyObject = string>(props: SelectProps<T>) {
53
+ const {
54
+ ref,
55
+ name,
56
+ value,
57
+ invalid,
58
+ readOnly,
59
+ disabled,
60
+ placeholder,
61
+ autoFocus,
62
+ options,
63
+ defaultOpen,
64
+ hideHandle,
65
+ groupProperty,
66
+ labelProperty = "label",
67
+ valueProperty = "value",
68
+ sortProperty,
69
+ infoProperty,
70
+ multiple,
71
+ searchable,
72
+ allowClear,
73
+ allowCreate,
74
+ emptyDisplay,
75
+ createOption,
76
+ matcher,
77
+ renderer,
78
+ onQuery,
79
+ onChange,
80
+ onSelect,
81
+ onEnterPressed,
82
+ } = props;
74
83
  const {
75
84
  state,
76
85
  listRef,
@@ -86,9 +95,11 @@ export const Select = <T extends AnyObject = string>({
86
95
  value,
87
96
  options,
88
97
  multiple,
98
+ defaultOpen,
89
99
  groupProperty,
90
100
  labelProperty,
91
101
  valueProperty,
102
+ sortProperty,
92
103
  allowCreate,
93
104
  createOption,
94
105
  matcher,
@@ -97,12 +108,12 @@ export const Select = <T extends AnyObject = string>({
97
108
  onQuery,
98
109
  });
99
110
 
100
- const dropdownKey = useId();
111
+ const dropdownKeyId = useId();
101
112
 
102
- const { context, floatingStyles, refs } = useFloating({
113
+ const { context, floatingStyles, refs, placement } = useFloating({
103
114
  open: state.open,
104
- onOpenChange: (open, evt, reason) => {
105
- !open && (listRef.current = []);
115
+ onOpenChange: (open) => {
116
+ if (!open) listRef.current = [];
106
117
  setOpen(open);
107
118
  },
108
119
  strategy: "fixed",
@@ -135,35 +146,33 @@ export const Select = <T extends AnyObject = string>({
135
146
  return list.find((item) => macthOption(item, typedString));
136
147
  },
137
148
  onMatch(index) {
138
- state.open && setActiveIndex(index);
149
+ if (state.open) setActiveIndex(index);
139
150
 
140
- !searchable &&
141
- !multiple &&
142
- !state.open &&
143
- handleChange(listContentRef.current[index]);
151
+ if (!searchable && !multiple && !state.open) handleChange(listContentRef.current[index]);
144
152
  },
145
153
  });
146
154
 
147
- const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions(
148
- [dismiss, searchable ? undefined : click, navigation, typeahead],
149
- );
155
+ const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions([
156
+ dismiss,
157
+ searchable ? undefined : click,
158
+ navigation,
159
+ typeahead,
160
+ ]);
150
161
 
151
162
  const referenceProps = useMemo(() => {
152
- const props: AnyObject = getReferenceProps();
163
+ const props = getReferenceProps();
153
164
 
154
165
  return {
155
166
  ...props,
156
167
  onBlur(evt: React.FocusEvent) {
157
- if (
158
- evt.relatedTarget?.closest(`[data-select-dropdown="${dropdownKey}"]`)
159
- ) {
168
+ if (evt.relatedTarget?.closest(`[data-select-dropdown="${dropdownKeyId}"]`)) {
160
169
  refs.domReference.current?.querySelector("input")?.focus();
161
170
  return;
162
171
  }
163
172
  handleQuery("");
164
- setOpen(false);
173
+ // setOpen(false);
165
174
  },
166
- onKeyDown(evt: React.KeyboardEvent) {
175
+ onKeyDown(evt: React.KeyboardEvent<HTMLInputElement>) {
167
176
  if (evt.key === "Enter") {
168
177
  if (state.activeIndex !== null) {
169
178
  handleChange(listContentRef.current[state.activeIndex]);
@@ -172,111 +181,146 @@ export const Select = <T extends AnyObject = string>({
172
181
  return false;
173
182
  } else if (state.query) {
174
183
  handleQuery("");
184
+ evt.preventDefault();
185
+ evt.stopPropagation();
186
+ return false;
175
187
  } else {
176
188
  onEnterPressed?.(evt);
177
189
  }
178
190
  } else if (evt.key === "Tab" && state.activeIndex !== null) {
179
191
  handleChange(listContentRef.current[state.activeIndex]);
180
192
  } else if (evt.key === "Backspace") {
181
- !state.query && handleRemove();
193
+ if (!state.query) handleRemove();
182
194
  } else if (!searchable || ["ArrowUp", "ArrowDown"].includes(evt.key)) {
183
- if (
184
- ["ArrowUp", "ArrowDown"].includes(evt.key) &&
185
- state.activeIndex === null
186
- ) {
195
+ if (["ArrowUp", "ArrowDown"].includes(evt.key) && state.activeIndex === null) {
187
196
  setActiveIndex(state.selectedIndex);
188
197
  }
189
198
  setTimeout(() => {
199
+ // @ts-expect-error ignore
190
200
  props.onKeyDown?.(evt);
191
201
  }, 50);
192
202
  }
193
203
  },
194
- };
204
+ } as KeyValue;
195
205
  }, [
196
206
  getReferenceProps,
197
- onEnterPressed,
207
+ dropdownKeyId,
198
208
  handleQuery,
199
- refs,
200
- state.query,
209
+ refs.domReference,
201
210
  state.activeIndex,
211
+ state.query,
212
+ state.selectedIndex,
213
+ searchable,
214
+ handleChange,
215
+ listContentRef,
216
+ onEnterPressed,
217
+ handleRemove,
218
+ setActiveIndex,
202
219
  ]);
203
220
 
204
221
  const floatingProps = useMemo(() => {
205
222
  return getFloatingProps({
206
- "data-select-dropdown": dropdownKey,
207
- } as AnyObject);
208
- }, [getFloatingProps]);
223
+ // @ts-expect-error ignore
224
+ "data-select-dropdown": dropdownKeyId,
225
+ });
226
+ }, [dropdownKeyId, getFloatingProps]);
209
227
 
210
228
  const makeItemProps = useCallback(
211
229
  (item: AnyObject) => {
212
- const selected = isArray(state.value)
213
- ? state.value?.includes?.(item)
214
- : state.value === item;
230
+ const selected = isArray(state.value) ? state.value?.includes?.(item) : state.value === item;
215
231
  return getItemProps({
232
+ // @ts-expect-error ignore
216
233
  "data-selected": selected ? true : undefined,
217
234
  onClick() {
218
235
  handleChange(item);
219
236
  },
220
- } as AnyObject);
237
+ });
221
238
  },
222
239
  [getItemProps, handleChange, state.value],
223
240
  );
224
241
 
225
242
  const displayValue = useMemo(() => {
226
243
  if (isArray(state.value)) {
227
- return state.value.map((item: AnyObject, index) => (
244
+ return state.value.map((item: T, index) => (
245
+ // eslint-disable-next-line @eslint-react/no-array-index-key
228
246
  <Chip key={index} onRemove={() => handleRemove(index)}>
247
+ {/* @ts-expect-error ignore */}
229
248
  {renderer?.(item) ?? item[labelProperty] ?? item}
230
249
  </Chip>
231
250
  ));
232
251
  } else if (!isEmpty(state.value) && !state.query) {
233
252
  return (
234
- <span className="self-center pointer-events-none select-none">
235
- {renderer?.(state.value) ?? state.value[labelProperty] ?? state.value}
253
+ <span className="self-center pointer-events-none select-none truncate leading-tight">
254
+ {/* eslint-disable-next-line @typescript-eslint/no-unsafe-member-access */}
255
+ {renderer?.(state.value as T) ?? state.value[labelProperty] ?? state.value}
236
256
  </span>
237
257
  );
238
258
  }
239
- }, [state.value, state.query, placeholder, renderer]);
259
+ }, [state.value, state.query, renderer, labelProperty, handleRemove]);
240
260
 
241
261
  const openDropdown = useCallback(() => {
242
262
  setOpen(true);
243
263
  refs.domReference.current?.querySelector("input")?.focus();
244
- }, []);
264
+ }, [refs.domReference, setOpen]);
245
265
 
246
266
  useLayoutEffect(() => {
247
- state.open &&
248
- setTimeout(() => {
249
- state.selectedIndex &&
267
+ if (state.open) {
268
+ const tmr = setTimeout(() => {
269
+ if (state.selectedIndex)
250
270
  listRef.current[state.selectedIndex]?.scrollIntoView({
251
- block: "center",
271
+ block: "nearest",
252
272
  });
253
273
  }, 100);
254
- }, [state.open]);
274
+ return () => {
275
+ clearTimeout(tmr);
276
+ };
277
+ }
278
+ }, [listRef, state.open, state.selectedIndex]);
255
279
 
256
280
  return (
257
281
  <InputWrapper
258
- showClear={!isEmpty(state.value)}
282
+ showClear={allowClear && !isEmpty(state.value)}
259
283
  onClear={handleChange}
260
- invalid={invalid}
261
- readOnly={readOnly}
262
- disabled={disabled}
263
- required={required}
264
- error={error}
265
- wrapperRef={refs.setReference}
266
- className={state.open ? "!z-[99]" : ""}
267
- {...rest}
284
+ isOpen={state.open}
285
+ ref={(el) => refs.setReference(el)}
286
+ className={cn(
287
+ state.open ? "z-99!" : "",
288
+ "rounded-capped",
289
+ state.open && (placement === "top" ? "rounded-t-none" : "rounded-b-none"),
290
+ )}
291
+ {...getWrapperProps(props)}
292
+ actionButton={
293
+ <>
294
+ {!state.loading && !hideHandle && (
295
+ <button type="button" className="size-fit leading-0 cursor-pointer" onClick={() => !disabled && openDropdown()}>
296
+ <Icon
297
+ className={cn(
298
+ "flex-content py-2 z-0 order-9 text-muted cursor-pointer self-stretch flex items-center h-auto",
299
+ state.open && "-scale-y-100 pointer-events-none",
300
+ disabled && "pointer-events-none",
301
+ )}
302
+ icon="icon-[mdi--chevron-down]"
303
+ />
304
+ </button>
305
+ )}
306
+ {state.loading && (
307
+ <Icon
308
+ className={cn("flex-content p-2 z-0 order-9 text-muted pointer-events-none")}
309
+ icon="icon-[svg-spinners--eclipse]"
310
+ />
311
+ )}
312
+ </>
313
+ }
268
314
  >
269
315
  <div
270
316
  role="none"
271
317
  data-select-display="true"
272
- className={classNames(
318
+ className={cn(
273
319
  "group flex-1 py-1 px-2 truncate text-start flex gap-1 relative min-h-5 justify-start",
274
320
  multiple ? "flex-wrap" : "flex-nowrap overflow-hidden",
275
321
  )}
276
322
  {...referenceProps}
277
- onMouseUp={(e) =>
278
- e.currentTarget.querySelector<HTMLElement>("input")?.focus()
279
- }
323
+ onMouseUp={(e) => e.currentTarget.querySelector<HTMLElement>("input")?.focus()}
280
324
  >
281
325
  {displayValue}
282
326
  <input
@@ -288,46 +332,26 @@ export const Select = <T extends AnyObject = string>({
288
332
  name={name}
289
333
  size={1}
290
334
  ref={ref}
291
- autoFocus={autoFocus}
292
- className={classNames(
293
- "appearance-none bg-transparent outline-none border-none ring-0 flex-1",
335
+ autoComplete="off"
336
+ {...{ autoFocus }}
337
+ className={cn(
338
+ "appearance-none bg-transparent outline-none border-none ring-0 flex-1 peer",
294
339
  disabled && "cursor-not-allowed pointer-events-none",
295
- multiple && "min-w-24",
340
+ multiple ? "min-w-24" : "min-w-2",
296
341
  )}
297
342
  placeholder={isEmpty(state.value) ? placeholder : ""}
298
343
  onChange={(evt) => handleQuery(evt.target.value)}
299
344
  />
300
345
  </div>
301
- {!state.loading && (
302
- <Icon
303
- className={classNames(
304
- "flex-content p-2 z-0 order-9 text-muted cursor-pointer self-stretch flex items-center h-auto",
305
- state.open && "-scale-y-100 pointer-events-none",
306
- disabled && "pointer-events-none",
307
- )}
308
- onClick={() => !disabled && openDropdown()}
309
- icon={CoreIcons.chevronDown}
310
- />
311
- )}
312
- {state.loading && (
313
- <Icon
314
- animate="spin"
315
- className={classNames(
316
- "flex-content p-2 z-0 order-9 text-muted pointer-events-none",
317
- )}
318
- icon={CoreIcons.spinner}
319
- />
320
- )}
321
346
  {state.open && !state.loading && (
322
- <FloatingPortal>
323
- <FloatingFocusManager
324
- context={context}
325
- initialFocus={-1}
326
- visuallyHiddenDismiss
327
- >
347
+ <FloatingPortal root={refs.domReference.current?.closest<HTMLElement>(".theme-base") ?? undefined}>
348
+ <FloatingFocusManager context={context} initialFocus={-1} visuallyHiddenDismiss closeOnFocusOut>
328
349
  <Options
329
- ref={refs.setFloating}
330
- className="outline shadow-lg max-h-[40vh] z-[var(--z-popover)]"
350
+ ref={(el) => refs.setFloating(el)}
351
+ className={cn(
352
+ "outline rounded-capped shadow-lg max-h-[40vh] z-(--z-popover)",
353
+ state.open && (placement === "top" ? "rounded-b-none" : "rounded-t-none"),
354
+ )}
331
355
  style={{
332
356
  width: refs.reference.current?.getBoundingClientRect().width,
333
357
  ...floatingStyles,
@@ -339,12 +363,14 @@ export const Select = <T extends AnyObject = string>({
339
363
  labelProperty={labelProperty}
340
364
  itemRef={setItemRef}
341
365
  itemProps={makeItemProps}
366
+ empty={emptyDisplay}
367
+ info={getByPath(state.activeItem as KeyValue, infoProperty ?? "info")}
342
368
  >
343
- {(item, label) => renderer?.(item) ?? label}
369
+ {(item, label) => renderer?.(item as T) ?? label}
344
370
  </Options>
345
371
  </FloatingFocusManager>
346
372
  </FloatingPortal>
347
373
  )}
348
374
  </InputWrapper>
349
375
  );
350
- };
376
+ }
@@ -21,16 +21,21 @@
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
+ /* eslint-disable @typescript-eslint/no-unsafe-return */
25
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
26
+
24
27
  import { isArray } from "@react-fabric/utilities";
25
28
 
26
- export const makeOption = (query: string) => query;
29
+ export function makeOption(query: string) {
30
+ return query;
31
+ }
27
32
 
28
- export const getSelectedValue = (
33
+ export function getSelectedValue(
29
34
  value: AnyObject,
30
- emptyValue: AnyObject,
31
- valueProperty: AnyObject = "",
32
- ) => {
35
+ emptyValue: string | string[],
36
+ valueProperty: string = "",
37
+ ): string | string[] {
33
38
  return isArray(value)
34
- ? value.map((val: AnyObject) => val[valueProperty] ?? val)
35
- : value?.[valueProperty] ?? value ?? emptyValue;
36
- };
39
+ ? value.map((val) => val[valueProperty] ?? val).filter(Boolean)
40
+ : (value?.[valueProperty] ?? value ?? emptyValue);
41
+ }