@react-fabric/form 1.0.0-beta.8 → 1.0.1
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.
- package/{src/input/ArrayInput.module.css → css/_arrayInput.css} +8 -8
- package/css/_colorPicker.css +95 -0
- package/{src/internal/Field.module.css → css/_field.css} +94 -33
- package/{src/input/File.module.css → css/_file.css} +21 -6
- package/css/styles.css +27 -0
- package/dist/cjs/builder/FileField.js +107 -78
- package/dist/cjs/builder/FormField.js +60 -39
- package/dist/cjs/builder/SchemaEditor.js +72 -0
- package/dist/cjs/builder/components/AddSchemaFields.js +64 -0
- package/dist/cjs/builder/components/SchemaArrayForm.js +146 -0
- package/dist/cjs/builder/components/SchemaField.js +423 -0
- package/dist/cjs/form/Controller.js +66 -19
- package/dist/cjs/form/Form.js +97 -39
- package/dist/cjs/form/useForm.js +32 -0
- package/dist/cjs/hooks/useFileUploader.js +74 -45
- package/dist/cjs/hooks/useFormBuilder.js +85 -31
- package/dist/cjs/i18n/{ar.json.js → ar/form.json.js} +5 -4
- package/dist/cjs/i18n/{en.json.js → en/form.json.js} +5 -5
- package/dist/cjs/i18n/index.js +4 -4
- package/dist/cjs/index.js +41 -2
- package/dist/cjs/input/ArrayInput.js +259 -135
- package/dist/cjs/input/AvatarInput.js +99 -63
- package/dist/cjs/input/Checkbox.js +24 -25
- package/dist/cjs/input/ColorInput.js +138 -81
- package/dist/cjs/input/ColorPicker.js +78 -95
- package/dist/cjs/input/DateInput.js +192 -0
- package/dist/cjs/input/Field.js +21 -20
- package/dist/cjs/input/File.js +85 -80
- package/dist/cjs/input/Hidden.js +27 -13
- package/dist/cjs/input/Input.js +49 -48
- package/dist/cjs/input/Number.js +46 -44
- package/dist/cjs/input/Password.js +51 -49
- package/dist/cjs/input/Radio.js +26 -16
- package/dist/cjs/input/RadioGroup.js +26 -11
- package/dist/cjs/input/{Range.js → RangeSlider.js} +61 -66
- package/dist/cjs/input/Search.js +58 -64
- package/dist/cjs/input/Slider.js +57 -66
- package/dist/cjs/input/Switch.js +29 -27
- package/dist/cjs/input/Textarea.js +65 -53
- package/dist/cjs/internal/ErrorIcon.js +9 -9
- package/dist/cjs/internal/FieldWrapper.js +23 -13
- package/dist/cjs/internal/InputWrapper.js +85 -87
- package/dist/cjs/ranger/index.js +7 -7
- package/dist/cjs/ranger/utils.js +9 -4
- package/dist/cjs/select/AutoComplete.js +93 -76
- package/dist/cjs/select/DualList.js +235 -154
- package/dist/cjs/select/List.js +122 -89
- package/dist/cjs/select/Options.js +58 -54
- package/dist/cjs/select/Select.js +119 -89
- package/dist/cjs/select/helpers.js +5 -5
- package/dist/cjs/select/useSelect.js +83 -58
- package/dist/cjs/types/schema.js +43 -0
- package/dist/cjs/utils/eventHandlers.js +16 -6
- package/dist/cjs/utils/getColor.js +6 -6
- package/dist/cjs/utils/getProps.js +49 -0
- package/dist/esm/builder/FileField.js +109 -80
- package/dist/esm/builder/FormField.js +60 -39
- package/dist/esm/builder/SchemaEditor.js +72 -0
- package/dist/esm/builder/components/AddSchemaFields.js +64 -0
- package/dist/esm/builder/components/SchemaArrayForm.js +146 -0
- package/dist/esm/builder/components/SchemaField.js +423 -0
- package/dist/esm/form/Controller.js +66 -19
- package/dist/esm/form/Form.js +97 -39
- package/dist/esm/form/useForm.js +32 -0
- package/dist/esm/hooks/useFileUploader.js +75 -46
- package/dist/esm/hooks/useFormBuilder.js +84 -30
- package/dist/esm/i18n/{ar.json.js → ar/form.json.js} +5 -4
- package/dist/esm/i18n/{en.json.js → en/form.json.js} +5 -5
- package/dist/esm/i18n/index.js +2 -2
- package/dist/esm/index.js +36 -1
- package/dist/esm/input/ArrayInput.js +260 -136
- package/dist/esm/input/AvatarInput.js +99 -63
- package/dist/esm/input/Checkbox.js +26 -27
- package/dist/esm/input/ColorInput.js +141 -84
- package/dist/esm/input/ColorPicker.js +80 -97
- package/dist/esm/input/DateInput.js +192 -0
- package/dist/esm/input/Field.js +21 -20
- package/dist/esm/input/File.js +89 -84
- package/dist/esm/input/Hidden.js +28 -14
- package/dist/esm/input/Input.js +51 -50
- package/dist/esm/input/Number.js +48 -46
- package/dist/esm/input/Password.js +54 -52
- package/dist/esm/input/Radio.js +27 -17
- package/dist/esm/input/RadioGroup.js +27 -12
- package/dist/esm/input/{Range.js → RangeSlider.js} +62 -67
- package/dist/esm/input/Search.js +61 -67
- package/dist/esm/input/Slider.js +58 -67
- package/dist/esm/input/Switch.js +31 -29
- package/dist/esm/input/Textarea.js +67 -55
- package/dist/esm/internal/ErrorIcon.js +11 -11
- package/dist/esm/internal/FieldWrapper.js +23 -13
- package/dist/esm/internal/InputWrapper.js +88 -90
- package/dist/esm/ranger/index.js +7 -7
- package/dist/esm/ranger/utils.js +9 -4
- package/dist/esm/select/AutoComplete.js +95 -78
- package/dist/esm/select/DualList.js +233 -152
- package/dist/esm/select/List.js +125 -92
- package/dist/esm/select/Options.js +58 -54
- package/dist/esm/select/Select.js +122 -92
- package/dist/esm/select/helpers.js +5 -5
- package/dist/esm/select/useSelect.js +84 -59
- package/{src/input/ColorPicker.module.css → dist/esm/types/schema.js} +18 -27
- package/dist/esm/utils/eventHandlers.js +16 -6
- package/dist/esm/utils/getColor.js +6 -6
- package/dist/esm/utils/getProps.js +49 -0
- package/dist/types/builder/FileField.d.ts +7 -4
- package/dist/types/builder/FormField.d.ts +4 -2
- package/dist/types/builder/SchemaEditor.d.ts +6 -0
- package/dist/types/builder/components/AddSchemaFields.d.ts +2 -0
- package/dist/types/builder/components/SchemaArrayForm.d.ts +2 -0
- package/dist/types/builder/components/SchemaField.d.ts +12 -0
- package/dist/types/form/Controller.d.ts +45 -3
- package/dist/types/form/Form.d.ts +38 -7
- package/dist/types/form/useForm.d.ts +6 -0
- package/dist/types/hooks/useFileUploader.d.ts +24 -3
- package/dist/types/hooks/useFormBuilder.d.ts +41 -3
- package/dist/types/index.d.ts +32 -2
- package/dist/types/input/ArrayInput.d.ts +68 -13
- package/dist/types/input/AvatarInput.d.ts +23 -6
- package/dist/types/input/Checkbox.d.ts +12 -10
- package/dist/types/input/ColorInput.d.ts +15 -5
- package/dist/types/input/ColorPicker.d.ts +10 -2
- package/dist/types/input/DateInput.d.ts +21 -0
- package/dist/types/input/Field.d.ts +9 -36
- package/dist/types/input/File.d.ts +16 -4
- package/dist/types/input/Hidden.d.ts +7 -6
- package/dist/types/input/Input.d.ts +11 -4
- package/dist/types/input/Number.d.ts +9 -4
- package/dist/types/input/Password.d.ts +9 -4
- package/dist/types/input/Radio.d.ts +9 -4
- package/dist/types/input/RadioGroup.d.ts +14 -6
- package/dist/types/input/{Range.d.ts → RangeSlider.d.ts} +11 -6
- package/dist/types/input/Search.d.ts +14 -4
- package/dist/types/input/Slider.d.ts +9 -6
- package/dist/types/input/Switch.d.ts +13 -8
- package/dist/types/input/Textarea.d.ts +15 -4
- package/dist/types/internal/ErrorIcon.d.ts +4 -1
- package/dist/types/internal/FieldWrapper.d.ts +10 -1
- package/dist/types/internal/InputWrapper.d.ts +19 -1
- package/dist/types/ranger/index.d.ts +7 -3
- package/dist/types/ranger/utils.d.ts +2 -2
- package/dist/types/select/AutoComplete.d.ts +7 -2
- package/dist/types/select/DualList.d.ts +11 -37
- package/dist/types/select/List.d.ts +7 -2
- package/dist/types/select/Options.d.ts +8 -7
- package/dist/types/select/Select.d.ts +7 -2
- package/dist/types/select/helpers.d.ts +2 -2
- package/dist/types/select/useSelect.d.ts +10 -28
- package/dist/types/types/index.d.ts +73 -39
- package/dist/types/types/schema.d.ts +78 -32
- package/dist/types/utils/eventHandlers.d.ts +4 -4
- package/dist/types/utils/getColor.d.ts +4 -4
- package/dist/types/utils/getProps.d.ts +26 -0
- package/dist/types/utils/index.d.ts +1 -0
- package/package.json +25 -25
- package/src/builder/FileField.tsx +73 -56
- package/src/builder/FormField.tsx +66 -58
- package/src/builder/SchemaEditor.tsx +78 -0
- package/src/builder/components/AddSchemaFields.tsx +64 -0
- package/src/builder/components/SchemaArrayForm.tsx +146 -0
- package/src/builder/components/SchemaField.tsx +405 -0
- package/src/form/Controller.tsx +61 -24
- package/src/form/Form.tsx +74 -50
- package/src/form/useForm.ts +53 -0
- package/src/hooks/useFileUploader.ts +115 -84
- package/src/hooks/useFormBuilder.tsx +80 -41
- package/src/i18n/{ar.json → ar/form.json} +5 -4
- package/src/i18n/{en.json → en/form.json} +5 -5
- package/src/i18n/index.ts +2 -2
- package/src/index.ts +34 -20
- package/src/input/ArrayInput.tsx +255 -173
- package/src/input/AvatarInput.tsx +82 -39
- package/src/input/Checkbox.tsx +30 -54
- package/src/input/ColorInput.tsx +146 -105
- package/src/input/ColorPicker.tsx +76 -102
- package/src/input/DateInput.tsx +225 -0
- package/src/input/Field.tsx +23 -65
- package/src/input/File.tsx +76 -98
- package/src/input/Hidden.tsx +36 -24
- package/src/input/Input.tsx +55 -53
- package/src/input/Number.tsx +49 -54
- package/src/input/Password.tsx +61 -66
- package/src/input/Radio.tsx +28 -37
- package/src/input/RadioGroup.tsx +30 -25
- package/src/input/{Range.tsx → RangeSlider.tsx} +81 -109
- package/src/input/Search.tsx +64 -77
- package/src/input/Slider.tsx +80 -109
- package/src/input/Switch.tsx +39 -53
- package/src/input/Textarea.tsx +72 -59
- package/src/internal/ErrorIcon.tsx +22 -21
- package/src/internal/FieldWrapper.tsx +33 -23
- package/src/internal/InputWrapper.tsx +132 -143
- package/src/ranger/index.tsx +37 -117
- package/src/ranger/utils.ts +12 -11
- package/src/select/AutoComplete.tsx +113 -123
- package/src/select/DualList.tsx +218 -204
- package/src/select/List.tsx +130 -97
- package/src/select/Options.tsx +59 -57
- package/src/select/Select.tsx +148 -122
- package/src/select/helpers.ts +13 -8
- package/src/select/useSelect.tsx +110 -127
- package/src/types/index.ts +76 -45
- package/src/types/schema.ts +97 -40
- package/src/utils/eventHandlers.ts +24 -26
- package/src/utils/getColor.ts +9 -8
- package/src/utils/getProps.ts +47 -0
- package/src/utils/index.ts +1 -0
- package/dist/cjs/input/ArrayInput.module.css +0 -1
- package/dist/cjs/input/ColorPicker.module.css +0 -1
- package/dist/cjs/input/File.module.css +0 -1
- package/dist/cjs/internal/Field.module.css +0 -1
- package/dist/esm/input/ArrayInput.module.css +0 -1
- package/dist/esm/input/ColorPicker.module.css +0 -1
- package/dist/esm/input/File.module.css +0 -1
- package/dist/esm/internal/Field.module.css +0 -1
package/src/select/Select.tsx
CHANGED
|
@@ -35,42 +35,51 @@ import {
|
|
|
35
35
|
useListNavigation,
|
|
36
36
|
useTypeahead,
|
|
37
37
|
} from "@floating-ui/react";
|
|
38
|
-
import { Chip,
|
|
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 {
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
|
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
|
|
105
|
-
!open
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
}
|
|
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:
|
|
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
|
-
{
|
|
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,
|
|
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: "
|
|
271
|
+
block: "nearest",
|
|
252
272
|
});
|
|
253
273
|
}, 100);
|
|
254
|
-
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
{...
|
|
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={
|
|
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
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
|
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=
|
|
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
|
+
}
|
package/src/select/helpers.ts
CHANGED
|
@@ -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
|
|
29
|
+
export function makeOption(query: string) {
|
|
30
|
+
return query;
|
|
31
|
+
}
|
|
27
32
|
|
|
28
|
-
export
|
|
33
|
+
export function getSelectedValue(
|
|
29
34
|
value: AnyObject,
|
|
30
|
-
emptyValue:
|
|
31
|
-
valueProperty:
|
|
32
|
-
)
|
|
35
|
+
emptyValue: string | string[],
|
|
36
|
+
valueProperty: string = "",
|
|
37
|
+
): string | string[] {
|
|
33
38
|
return isArray(value)
|
|
34
|
-
? value.map((val
|
|
35
|
-
: value?.[valueProperty] ?? value ?? emptyValue;
|
|
36
|
-
}
|
|
39
|
+
? value.map((val) => val[valueProperty] ?? val).filter(Boolean)
|
|
40
|
+
: (value?.[valueProperty] ?? value ?? emptyValue);
|
|
41
|
+
}
|