@react-fabric/form 1.0.0-beta.9 → 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/useSelect.tsx
CHANGED
|
@@ -21,23 +21,16 @@
|
|
|
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-call */
|
|
26
|
+
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
27
|
+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
28
|
+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
29
|
+
|
|
24
30
|
import { useDebounce } from "@react-fabric/core";
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
groupBy,
|
|
29
|
-
isArray,
|
|
30
|
-
isString,
|
|
31
|
-
matchString,
|
|
32
|
-
} from "@react-fabric/utilities";
|
|
33
|
-
import {
|
|
34
|
-
useCallback,
|
|
35
|
-
useDeferredValue,
|
|
36
|
-
useEffect,
|
|
37
|
-
useMemo,
|
|
38
|
-
useReducer,
|
|
39
|
-
useRef,
|
|
40
|
-
} from "react";
|
|
31
|
+
import { compareValues, dedupe, EMPTY_ARRAY, groupBy, isArray, isString, matchString } from "@react-fabric/utilities";
|
|
32
|
+
import { useCallback, useDeferredValue, useEffect, useMemo, useReducer, useRef } from "react";
|
|
33
|
+
import type { SelectProps } from "../types";
|
|
41
34
|
import { getSelectedValue, makeOption } from "./helpers";
|
|
42
35
|
|
|
43
36
|
type StateActions =
|
|
@@ -64,6 +57,7 @@ interface State {
|
|
|
64
57
|
open: boolean;
|
|
65
58
|
loading: boolean;
|
|
66
59
|
activeIndex: number | null;
|
|
60
|
+
activeItem: AnyObject;
|
|
67
61
|
selectedIndex: number | null;
|
|
68
62
|
value: AnyObject;
|
|
69
63
|
// filter
|
|
@@ -72,37 +66,30 @@ interface State {
|
|
|
72
66
|
items: AnyObject[];
|
|
73
67
|
}
|
|
74
68
|
|
|
75
|
-
export
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
autoComplete?: boolean;
|
|
98
|
-
matcher?: (item: AnyObject, query: string) => boolean;
|
|
99
|
-
createOption?: (query: string) => AnyObject;
|
|
100
|
-
onQuery?: (query: string) => AnyObject;
|
|
101
|
-
onSelect?: (item: AnyObject) => void;
|
|
102
|
-
onChange?: (value: AnyObject) => void;
|
|
103
|
-
}) => {
|
|
69
|
+
export function useSelect(
|
|
70
|
+
props: Omit<SelectProps<AnyObject>, "multiple"> & { multiple?: boolean; autoComplete?: boolean; alwaysOpen?: boolean },
|
|
71
|
+
) {
|
|
72
|
+
const {
|
|
73
|
+
options = EMPTY_ARRAY,
|
|
74
|
+
value,
|
|
75
|
+
autoComplete,
|
|
76
|
+
alwaysOpen = false,
|
|
77
|
+
defaultOpen = false,
|
|
78
|
+
multiple = false,
|
|
79
|
+
allowCreate = false,
|
|
80
|
+
groupProperty = "",
|
|
81
|
+
valueProperty = "value",
|
|
82
|
+
labelProperty = "label",
|
|
83
|
+
sortProperty,
|
|
84
|
+
createOption = makeOption,
|
|
85
|
+
matcher,
|
|
86
|
+
onQuery,
|
|
87
|
+
onChange,
|
|
88
|
+
onSelect,
|
|
89
|
+
} = props;
|
|
90
|
+
|
|
104
91
|
// value state
|
|
105
|
-
const emptyValue = useMemo(() => (multiple ? [] : ""), [multiple]);
|
|
92
|
+
const emptyValue = useMemo(() => (multiple ? ([] as string[]) : ""), [multiple]);
|
|
106
93
|
const deferred = useDeferredValue(value ?? emptyValue);
|
|
107
94
|
|
|
108
95
|
// list items
|
|
@@ -111,32 +98,28 @@ export const useSelect = ({
|
|
|
111
98
|
const listContentRef = useRef<AnyObject[]>([]);
|
|
112
99
|
const optionsRef = useRef<AnyObject[]>([]);
|
|
113
100
|
|
|
114
|
-
const
|
|
101
|
+
const matchOption = useCallback(
|
|
115
102
|
(item: AnyObject, query: string) => {
|
|
116
|
-
return (
|
|
117
|
-
matcher?.(item, query) ??
|
|
118
|
-
matchString(item[labelProperty] ?? item, query)
|
|
119
|
-
);
|
|
103
|
+
return matcher?.(item, query) ?? matchString(item[labelProperty] ?? item, query);
|
|
120
104
|
},
|
|
121
|
-
[matcher],
|
|
105
|
+
[labelProperty, matcher],
|
|
122
106
|
);
|
|
123
107
|
|
|
124
108
|
const fireChange = useCallback(
|
|
125
109
|
(value: AnyObject) => {
|
|
126
110
|
setTimeout(() => {
|
|
127
111
|
onSelect?.(value ?? emptyValue);
|
|
112
|
+
// @ts-expect-error ignore
|
|
128
113
|
onChange?.(getSelectedValue(value, emptyValue, valueProperty));
|
|
129
114
|
}, 50);
|
|
130
115
|
},
|
|
131
|
-
[emptyValue,
|
|
116
|
+
[onSelect, emptyValue, onChange, valueProperty],
|
|
132
117
|
);
|
|
133
118
|
|
|
134
119
|
const [state, dispatch] = useReducer(
|
|
135
120
|
(state: State, action: StateActions) => {
|
|
136
121
|
if (action.type === "open") {
|
|
137
|
-
const index = state.items.indexOf(
|
|
138
|
-
state.multiple ? state.value[0] : state.value,
|
|
139
|
-
);
|
|
122
|
+
const index = state.items.indexOf(state.multiple ? state.value[0] : state.value);
|
|
140
123
|
return {
|
|
141
124
|
...state,
|
|
142
125
|
open: true,
|
|
@@ -144,20 +127,21 @@ export const useSelect = ({
|
|
|
144
127
|
};
|
|
145
128
|
}
|
|
146
129
|
if (action.type === "close") {
|
|
147
|
-
return { ...state, activeIndex: null, open: false };
|
|
130
|
+
return { ...state, activeIndex: null, activeItem: null, open: false };
|
|
148
131
|
}
|
|
149
132
|
if (action.type === "active" && state.open) {
|
|
150
|
-
if (autoComplete && action.index !== null)
|
|
151
|
-
|
|
152
|
-
|
|
133
|
+
if (autoComplete && action.index !== null) state.query = state.items[action.index];
|
|
134
|
+
return {
|
|
135
|
+
...state,
|
|
136
|
+
activeIndex: action.index,
|
|
137
|
+
activeItem: action.index !== null ? state.items[action.index] : null,
|
|
138
|
+
};
|
|
153
139
|
}
|
|
154
140
|
if (action.type === "setValue") {
|
|
155
141
|
return {
|
|
156
142
|
...state,
|
|
157
143
|
value: action.value,
|
|
158
|
-
selectedIndex: state.items.indexOf(
|
|
159
|
-
state.multiple ? action.value[0] : action.value,
|
|
160
|
-
),
|
|
144
|
+
selectedIndex: state.items.indexOf(state.multiple ? action.value[0] : action.value),
|
|
161
145
|
};
|
|
162
146
|
}
|
|
163
147
|
if (action.type === "changeValue") {
|
|
@@ -165,17 +149,17 @@ export const useSelect = ({
|
|
|
165
149
|
...state,
|
|
166
150
|
value: state.emptyValue,
|
|
167
151
|
activeIndex: null,
|
|
152
|
+
activeItem: null,
|
|
168
153
|
selectedIndex: autoComplete
|
|
169
154
|
? state.multiple
|
|
170
|
-
? action.value?.length
|
|
155
|
+
? (action.value?.length as number)
|
|
171
156
|
: 0
|
|
172
157
|
: state.items.indexOf(action.value),
|
|
173
158
|
};
|
|
174
159
|
if (action.value !== undefined) {
|
|
175
160
|
if (multiple) {
|
|
176
161
|
const values = [];
|
|
177
|
-
if (isString(action.value))
|
|
178
|
-
values.push(...action.value.split(/,\s?/));
|
|
162
|
+
if (isString(action.value)) values.push(...action.value.split(/,\s?/));
|
|
179
163
|
else values.push(action.value);
|
|
180
164
|
newState.value = [...state.value];
|
|
181
165
|
values.forEach((value: AnyObject) => {
|
|
@@ -187,7 +171,7 @@ export const useSelect = ({
|
|
|
187
171
|
newState.value = action.value;
|
|
188
172
|
}
|
|
189
173
|
}
|
|
190
|
-
if (!state.multiple) newState.open =
|
|
174
|
+
if (!state.multiple) newState.open = alwaysOpen;
|
|
191
175
|
fireChange(newState.value);
|
|
192
176
|
return newState;
|
|
193
177
|
}
|
|
@@ -223,6 +207,7 @@ export const useSelect = ({
|
|
|
223
207
|
if (autoComplete && action.query !== undefined) {
|
|
224
208
|
optionList = dedupe([action.query, ...optionList]);
|
|
225
209
|
}
|
|
210
|
+
if (sortProperty) optionList = optionList.sort(compareValues("asc", sortProperty !== true ? sortProperty : undefined));
|
|
226
211
|
const grouped = groupBy(optionList, groupProperty, "");
|
|
227
212
|
if (action.open) newState.open = true;
|
|
228
213
|
newState.items = [];
|
|
@@ -230,7 +215,7 @@ export const useSelect = ({
|
|
|
230
215
|
if (allowCreate && action.query)
|
|
231
216
|
newState.items.push({
|
|
232
217
|
___create___: true,
|
|
233
|
-
value:
|
|
218
|
+
value: action.query,
|
|
234
219
|
});
|
|
235
220
|
Object.entries(grouped).forEach(([key, list]) => {
|
|
236
221
|
if (key) {
|
|
@@ -239,42 +224,40 @@ export const useSelect = ({
|
|
|
239
224
|
newState.items.push(...list);
|
|
240
225
|
});
|
|
241
226
|
if (action.setActive) {
|
|
242
|
-
const index = newState.items.findIndex(
|
|
243
|
-
(opt: AnyObject) => !opt.___group___,
|
|
244
|
-
);
|
|
227
|
+
const index = newState.items.findIndex((opt: AnyObject) => !opt.___group___);
|
|
245
228
|
newState.activeIndex = index >= 0 ? index : null;
|
|
229
|
+
newState.activeItem = index >= 0 ? newState.items[index] : null;
|
|
246
230
|
} else {
|
|
247
231
|
newState.activeIndex = null;
|
|
232
|
+
newState.activeItem = null;
|
|
248
233
|
}
|
|
249
234
|
listContentRef.current = newState.items;
|
|
250
235
|
return newState;
|
|
251
236
|
}
|
|
252
237
|
return state;
|
|
253
238
|
},
|
|
254
|
-
{
|
|
239
|
+
{},
|
|
240
|
+
() => ({
|
|
255
241
|
multiple,
|
|
256
242
|
emptyValue,
|
|
257
|
-
open:
|
|
243
|
+
open: alwaysOpen || defaultOpen,
|
|
258
244
|
loading: false,
|
|
259
245
|
activeIndex: null,
|
|
246
|
+
activeItem: null,
|
|
260
247
|
selectedIndex: null,
|
|
261
248
|
value: emptyValue,
|
|
262
249
|
// filter
|
|
263
250
|
query: undefined,
|
|
264
251
|
// dropdown items
|
|
265
252
|
items: [],
|
|
266
|
-
},
|
|
253
|
+
}),
|
|
267
254
|
);
|
|
268
255
|
|
|
269
256
|
useEffect(() => {
|
|
270
257
|
if (!multiple) {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
: undefined;
|
|
275
|
-
const hasValue = options.find(
|
|
276
|
-
(opt: AnyObject) => (opt[valueProperty] ?? opt) === deferred,
|
|
277
|
-
);
|
|
258
|
+
// @ts-expect-error ignore
|
|
259
|
+
const newOption = (allowCreate || autoComplete) && deferred ? createOption(deferred) : undefined;
|
|
260
|
+
const hasValue = options.find((opt: AnyObject) => (opt[valueProperty] ?? opt) === deferred);
|
|
278
261
|
optionsRef.current = options;
|
|
279
262
|
// if current value missing in list inject it
|
|
280
263
|
if (!hasValue && newOption) {
|
|
@@ -283,26 +266,19 @@ export const useSelect = ({
|
|
|
283
266
|
dispatch({ type: "options", options: optionsRef.current });
|
|
284
267
|
dispatch({
|
|
285
268
|
type: "setValue",
|
|
286
|
-
value: hasValue ?? newOption ??
|
|
269
|
+
value: hasValue ?? newOption ?? deferred,
|
|
287
270
|
});
|
|
288
271
|
}
|
|
289
272
|
if (multiple) {
|
|
290
273
|
optionsRef.current = [];
|
|
291
|
-
const value = (isArray(deferred) ? deferred : []).map(
|
|
292
|
-
(item
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
);
|
|
300
|
-
if (!hasValue && newOption) {
|
|
301
|
-
optionsRef.current.push(newOption);
|
|
302
|
-
}
|
|
303
|
-
return hasValue ?? newOption ?? "";
|
|
304
|
-
},
|
|
305
|
-
);
|
|
274
|
+
const value = (isArray(deferred) ? deferred : []).map((item: AnyObject) => {
|
|
275
|
+
const newOption = (allowCreate || autoComplete) && item ? createOption(item) : undefined;
|
|
276
|
+
const hasValue = options.find((opt: AnyObject) => (opt[valueProperty] ?? opt) === item);
|
|
277
|
+
if (!hasValue && newOption) {
|
|
278
|
+
optionsRef.current.push(newOption);
|
|
279
|
+
}
|
|
280
|
+
return hasValue ?? newOption ?? item;
|
|
281
|
+
});
|
|
306
282
|
optionsRef.current = [...optionsRef.current, ...(options ?? [])];
|
|
307
283
|
dispatch({ type: "options", options: optionsRef.current });
|
|
308
284
|
dispatch({
|
|
@@ -310,17 +286,18 @@ export const useSelect = ({
|
|
|
310
286
|
value,
|
|
311
287
|
});
|
|
312
288
|
}
|
|
313
|
-
}, [deferred, options, multiple, allowCreate, autoComplete, createOption]);
|
|
289
|
+
}, [deferred, options, multiple, allowCreate, autoComplete, createOption, valueProperty]);
|
|
314
290
|
|
|
315
291
|
const fireQuery = useDebounce(
|
|
316
292
|
(query: string) => {
|
|
317
293
|
void Promise.resolve(onQuery?.(query)).then((options) => {
|
|
294
|
+
// @ts-expect-error ignore
|
|
318
295
|
const newOptions = [...options];
|
|
319
296
|
// if autocomplete inject other filtered options from history
|
|
320
297
|
if (autoComplete) {
|
|
321
298
|
newOptions.push(
|
|
322
299
|
...optionsRef.current.filter((opt: AnyObject) => {
|
|
323
|
-
return
|
|
300
|
+
return matchOption?.(opt, query);
|
|
324
301
|
}),
|
|
325
302
|
);
|
|
326
303
|
}
|
|
@@ -334,6 +311,7 @@ export const useSelect = ({
|
|
|
334
311
|
});
|
|
335
312
|
},
|
|
336
313
|
[onQuery],
|
|
314
|
+
300,
|
|
337
315
|
);
|
|
338
316
|
|
|
339
317
|
// handlers
|
|
@@ -352,38 +330,38 @@ export const useSelect = ({
|
|
|
352
330
|
open: true,
|
|
353
331
|
setActive: true,
|
|
354
332
|
options: optionsRef.current.filter((opt: AnyObject) => {
|
|
355
|
-
return
|
|
333
|
+
return matchOption?.(opt, query);
|
|
356
334
|
}),
|
|
357
335
|
});
|
|
358
336
|
}
|
|
359
337
|
},
|
|
360
|
-
[
|
|
338
|
+
[fireQuery, matchOption, onQuery],
|
|
361
339
|
);
|
|
362
340
|
|
|
363
|
-
const handleChange = useCallback(
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
dispatch({
|
|
369
|
-
type: "changeValue",
|
|
370
|
-
value: value?.___create___ ? value.value : value,
|
|
371
|
-
});
|
|
372
|
-
if (value?.___create___) {
|
|
373
|
-
let val = [value.value];
|
|
374
|
-
if (multiple) {
|
|
375
|
-
val = value.value.split(/,\s?/);
|
|
341
|
+
const handleChange = useCallback(
|
|
342
|
+
(value?: AnyObject) => {
|
|
343
|
+
let values = value;
|
|
344
|
+
if (autoComplete && multiple && value) {
|
|
345
|
+
values = value.split(/,\s?/);
|
|
376
346
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
347
|
+
dispatch({
|
|
348
|
+
type: "changeValue",
|
|
349
|
+
value: value?.___create___ ? value.value : value,
|
|
350
|
+
});
|
|
351
|
+
if (value?.___create___) {
|
|
352
|
+
let val = [value.value];
|
|
353
|
+
if (multiple) {
|
|
354
|
+
val = value.value.split(/,\s?/);
|
|
355
|
+
}
|
|
356
|
+
optionsRef.current = [...optionsRef.current, ...val.map(createOption)];
|
|
357
|
+
}
|
|
358
|
+
if (autoComplete) {
|
|
359
|
+
optionsRef.current = dedupe([...(isArray(values) ? values : [values]), ...optionsRef.current]);
|
|
360
|
+
}
|
|
361
|
+
dispatch({ type: "options", options: optionsRef.current });
|
|
362
|
+
},
|
|
363
|
+
[autoComplete, createOption, multiple],
|
|
364
|
+
);
|
|
387
365
|
|
|
388
366
|
const handleRemove = useCallback((index?: number) => {
|
|
389
367
|
dispatch({ type: "removeItem", index });
|
|
@@ -399,7 +377,11 @@ export const useSelect = ({
|
|
|
399
377
|
}, []);
|
|
400
378
|
|
|
401
379
|
const setActiveIndex = useCallback((index: number | null) => {
|
|
402
|
-
dispatch({ type: "active", index });
|
|
380
|
+
if (index && index > -1) dispatch({ type: "active", index });
|
|
381
|
+
}, []);
|
|
382
|
+
|
|
383
|
+
const clearActiveIndex = useCallback(() => {
|
|
384
|
+
dispatch({ type: "active", index: null });
|
|
403
385
|
}, []);
|
|
404
386
|
|
|
405
387
|
return {
|
|
@@ -410,11 +392,12 @@ export const useSelect = ({
|
|
|
410
392
|
handleChange,
|
|
411
393
|
handleRemove,
|
|
412
394
|
handleQuery,
|
|
413
|
-
|
|
395
|
+
clearActiveIndex,
|
|
396
|
+
macthOption: matchOption,
|
|
414
397
|
setActiveIndex,
|
|
415
398
|
setOpen,
|
|
416
399
|
setItemRef: (index: number, node: HTMLElement) => {
|
|
417
400
|
listRef.current[index] = node;
|
|
418
401
|
},
|
|
419
402
|
};
|
|
420
|
-
}
|
|
403
|
+
}
|
package/src/types/index.ts
CHANGED
|
@@ -21,16 +21,19 @@
|
|
|
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 {
|
|
25
|
-
type RefProp,
|
|
26
|
-
type TestProps,
|
|
27
|
-
} from "@react-fabric/core/dist/types/types";
|
|
24
|
+
import type { ChildNode, NestedKeys, RefProp, TestProps } from "@react-fabric/core/dist/types/types";
|
|
28
25
|
|
|
29
26
|
export interface InputProps<V = string> extends TestProps {
|
|
27
|
+
name?: string;
|
|
28
|
+
autoFocus?: boolean;
|
|
30
29
|
/**
|
|
31
30
|
* value
|
|
32
31
|
*/
|
|
33
32
|
value?: V;
|
|
33
|
+
/**
|
|
34
|
+
* default value
|
|
35
|
+
*/
|
|
36
|
+
defaultValue?: V;
|
|
34
37
|
/**
|
|
35
38
|
* change handler
|
|
36
39
|
*/
|
|
@@ -46,57 +49,47 @@ export interface InputProps<V = string> extends TestProps {
|
|
|
46
49
|
/**
|
|
47
50
|
* on enter press callback
|
|
48
51
|
*/
|
|
49
|
-
onEnterPressed?: React.KeyboardEventHandler
|
|
52
|
+
onEnterPressed?: React.KeyboardEventHandler<HTMLInputElement>;
|
|
50
53
|
|
|
54
|
+
placeholder?: string;
|
|
55
|
+
|
|
56
|
+
disabled?: boolean;
|
|
57
|
+
readOnly?: boolean;
|
|
51
58
|
/**
|
|
52
|
-
*
|
|
59
|
+
* show clear button
|
|
53
60
|
*/
|
|
61
|
+
allowClear?: boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface WrapperProps {
|
|
54
65
|
name?: string;
|
|
55
66
|
/**
|
|
56
67
|
* input label
|
|
57
68
|
*/
|
|
58
69
|
label?: string;
|
|
59
70
|
/**
|
|
60
|
-
*
|
|
71
|
+
* required
|
|
61
72
|
*/
|
|
62
|
-
|
|
73
|
+
required?: boolean;
|
|
74
|
+
disabled?: boolean;
|
|
63
75
|
/**
|
|
64
76
|
* append to label end
|
|
65
77
|
*/
|
|
66
|
-
appendLabel?:
|
|
78
|
+
appendLabel?: React.ReactNode;
|
|
67
79
|
/**
|
|
68
80
|
*
|
|
69
81
|
*/
|
|
70
|
-
decorateStart?:
|
|
82
|
+
decorateStart?: React.ReactNode;
|
|
71
83
|
/**
|
|
72
84
|
*
|
|
73
85
|
*/
|
|
74
|
-
decorateEnd?:
|
|
86
|
+
decorateEnd?: React.ReactNode;
|
|
87
|
+
decorateEndShowWhenEmpty?: boolean;
|
|
88
|
+
decorateEndHideWhenEmpty?: boolean;
|
|
75
89
|
/**
|
|
76
90
|
* info label at bottom
|
|
77
91
|
*/
|
|
78
|
-
info?:
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* required
|
|
82
|
-
*/
|
|
83
|
-
required?: boolean;
|
|
84
|
-
/**
|
|
85
|
-
* disabled input
|
|
86
|
-
*/
|
|
87
|
-
disabled?: boolean;
|
|
88
|
-
/**
|
|
89
|
-
* read-only input
|
|
90
|
-
*/
|
|
91
|
-
readOnly?: boolean;
|
|
92
|
-
/**
|
|
93
|
-
* auto focus when rendered
|
|
94
|
-
*/
|
|
95
|
-
autoFocus?: boolean;
|
|
96
|
-
/**
|
|
97
|
-
* show clear button
|
|
98
|
-
*/
|
|
99
|
-
allowClear?: boolean;
|
|
92
|
+
info?: React.ReactNode;
|
|
100
93
|
/**
|
|
101
94
|
* invalid value input
|
|
102
95
|
*/
|
|
@@ -105,7 +98,6 @@ export interface InputProps<V = string> extends TestProps {
|
|
|
105
98
|
* error message
|
|
106
99
|
*/
|
|
107
100
|
error?: string;
|
|
108
|
-
|
|
109
101
|
/**
|
|
110
102
|
* field width
|
|
111
103
|
*/
|
|
@@ -117,28 +109,46 @@ export interface InputProps<V = string> extends TestProps {
|
|
|
117
109
|
/**
|
|
118
110
|
* label width for inline
|
|
119
111
|
*/
|
|
120
|
-
labelWidth?: string;
|
|
112
|
+
labelWidth?: string | number;
|
|
113
|
+
/**
|
|
114
|
+
* remove the outline borders
|
|
115
|
+
*/
|
|
116
|
+
borderless?: boolean;
|
|
121
117
|
}
|
|
122
118
|
|
|
123
|
-
export interface BaseSelectProps<T>
|
|
124
|
-
|
|
125
|
-
|
|
119
|
+
export interface BaseSelectProps<T extends AnyObject = string> {
|
|
120
|
+
/**
|
|
121
|
+
* open dropdown by default
|
|
122
|
+
*/
|
|
123
|
+
defaultOpen?: boolean;
|
|
124
|
+
/**
|
|
125
|
+
* hide dropdown handle
|
|
126
|
+
*/
|
|
127
|
+
hideHandle?: boolean;
|
|
126
128
|
/**
|
|
127
129
|
* list options
|
|
128
130
|
*/
|
|
129
|
-
options
|
|
131
|
+
options: T[];
|
|
130
132
|
/**
|
|
131
133
|
* group by property name
|
|
132
134
|
*/
|
|
133
|
-
groupProperty?:
|
|
135
|
+
groupProperty?: NestedKeys<T>;
|
|
134
136
|
/**
|
|
135
137
|
* label property name
|
|
136
138
|
*/
|
|
137
|
-
labelProperty?:
|
|
139
|
+
labelProperty?: NestedKeys<T>;
|
|
140
|
+
/**
|
|
141
|
+
* value property name
|
|
142
|
+
*/
|
|
143
|
+
valueProperty?: NestedKeys<T>;
|
|
138
144
|
/**
|
|
139
145
|
* value property name
|
|
140
146
|
*/
|
|
141
|
-
|
|
147
|
+
infoProperty?: NestedKeys<T>;
|
|
148
|
+
/**
|
|
149
|
+
* value property name
|
|
150
|
+
*/
|
|
151
|
+
sortProperty?: NestedKeys<T> | true;
|
|
142
152
|
/**
|
|
143
153
|
* searchable list
|
|
144
154
|
*/
|
|
@@ -147,6 +157,10 @@ export interface BaseSelectProps<T>
|
|
|
147
157
|
* allow create item
|
|
148
158
|
*/
|
|
149
159
|
allowCreate?: boolean;
|
|
160
|
+
/**
|
|
161
|
+
* empty list display
|
|
162
|
+
*/
|
|
163
|
+
emptyDisplay?: ChildNode;
|
|
150
164
|
/**
|
|
151
165
|
* create new item object
|
|
152
166
|
*/
|
|
@@ -158,20 +172,22 @@ export interface BaseSelectProps<T>
|
|
|
158
172
|
/**
|
|
159
173
|
* item renderer
|
|
160
174
|
*/
|
|
161
|
-
renderer?: (option: T) =>
|
|
175
|
+
renderer?: (option: T) => ChildNode;
|
|
162
176
|
/**
|
|
163
177
|
* item matcher
|
|
164
178
|
*/
|
|
165
179
|
matcher?: (option: T, query: string) => boolean;
|
|
166
180
|
}
|
|
167
181
|
|
|
168
|
-
|
|
182
|
+
type BaseSelectType<T> = BaseSelectProps<T> & Omit<InputProps, "value" | "onChange"> & WrapperProps & RefProp<HTMLInputElement>;
|
|
183
|
+
|
|
184
|
+
export type SelectProps<T> = BaseSelectType<T> &
|
|
169
185
|
(
|
|
170
186
|
| {
|
|
171
187
|
/**
|
|
172
188
|
* is multiple selection
|
|
173
189
|
*/
|
|
174
|
-
multiple?:
|
|
190
|
+
multiple?: false;
|
|
175
191
|
/**
|
|
176
192
|
* selected value
|
|
177
193
|
*/
|
|
@@ -204,3 +220,18 @@ export type SelectProps<T> = BaseSelectProps<T> &
|
|
|
204
220
|
onChange?: (value?: string[]) => void;
|
|
205
221
|
}
|
|
206
222
|
);
|
|
223
|
+
|
|
224
|
+
export type DualSelectProps<T> = BaseSelectType<T> & {
|
|
225
|
+
/**
|
|
226
|
+
* selected value
|
|
227
|
+
*/
|
|
228
|
+
value?: string[];
|
|
229
|
+
/**
|
|
230
|
+
* select handler
|
|
231
|
+
*/
|
|
232
|
+
onSelect?: (option: T[]) => void;
|
|
233
|
+
/**
|
|
234
|
+
* change handler
|
|
235
|
+
*/
|
|
236
|
+
onChange?: (value?: string[]) => void;
|
|
237
|
+
};
|