@snack-uikit/fields 0.53.2-preview-0a70077b.0 → 0.54.0
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/CHANGELOG.md +11 -0
- package/README.md +28 -130
- package/dist/cjs/components/FieldSecure/FieldSecure.d.ts +1 -1
- package/dist/cjs/components/FieldSelect/FieldSelectMultiple.d.ts +3 -1
- package/dist/cjs/components/FieldSelect/FieldSelectMultiple.js +28 -7
- package/dist/cjs/components/FieldSelect/FieldSelectSingle.d.ts +3 -1
- package/dist/cjs/components/FieldSelect/FieldSelectSingle.js +25 -12
- package/dist/cjs/components/FieldSelect/{hooks.d.ts → hooks/common.d.ts} +1 -1
- package/dist/cjs/components/FieldSelect/{hooks.js → hooks/common.js} +4 -5
- package/dist/cjs/components/FieldSelect/hooks/customOption.d.ts +26 -0
- package/dist/cjs/components/FieldSelect/hooks/customOption.js +69 -0
- package/dist/cjs/components/FieldSelect/hooks/index.d.ts +2 -0
- package/dist/cjs/components/FieldSelect/hooks/index.js +26 -0
- package/dist/cjs/components/FieldSelect/index.d.ts +1 -1
- package/dist/cjs/components/FieldSelect/types.d.ts +20 -3
- package/dist/cjs/components/FieldSelect/utils/customOption.d.ts +4 -0
- package/dist/cjs/components/FieldSelect/utils/customOption.js +21 -0
- package/dist/cjs/components/FieldSelect/utils/index.d.ts +2 -0
- package/dist/cjs/components/FieldSelect/utils/index.js +3 -1
- package/dist/cjs/components/FieldText/FieldText.d.ts +1 -1
- package/dist/cjs/components/FieldTextArea/FieldTextArea.d.ts +1 -1
- package/dist/cjs/components/index.d.ts +0 -1
- package/dist/cjs/components/index.js +0 -1
- package/dist/cjs/helperComponents/ButtonFieldList/ButtonFieldList.d.ts +1 -1
- package/dist/esm/components/FieldSecure/FieldSecure.d.ts +1 -1
- package/dist/esm/components/FieldSelect/FieldSelectMultiple.d.ts +3 -1
- package/dist/esm/components/FieldSelect/FieldSelectMultiple.js +27 -9
- package/dist/esm/components/FieldSelect/FieldSelectSingle.d.ts +3 -1
- package/dist/esm/components/FieldSelect/FieldSelectSingle.js +24 -14
- package/dist/esm/components/FieldSelect/{hooks.d.ts → hooks/common.d.ts} +1 -1
- package/dist/esm/components/FieldSelect/{hooks.js → hooks/common.js} +3 -4
- package/dist/esm/components/FieldSelect/hooks/customOption.d.ts +26 -0
- package/dist/esm/components/FieldSelect/hooks/customOption.js +38 -0
- package/dist/esm/components/FieldSelect/hooks/index.d.ts +2 -0
- package/dist/esm/components/FieldSelect/hooks/index.js +2 -0
- package/dist/esm/components/FieldSelect/index.d.ts +1 -1
- package/dist/esm/components/FieldSelect/types.d.ts +20 -3
- package/dist/esm/components/FieldSelect/utils/customOption.d.ts +4 -0
- package/dist/esm/components/FieldSelect/utils/customOption.js +13 -0
- package/dist/esm/components/FieldSelect/utils/index.d.ts +2 -0
- package/dist/esm/components/FieldSelect/utils/index.js +2 -0
- package/dist/esm/components/FieldText/FieldText.d.ts +1 -1
- package/dist/esm/components/FieldTextArea/FieldTextArea.d.ts +1 -1
- package/dist/esm/components/index.d.ts +0 -1
- package/dist/esm/components/index.js +0 -1
- package/dist/esm/helperComponents/ButtonFieldList/ButtonFieldList.d.ts +1 -1
- package/package.json +2 -2
- package/src/components/FieldSelect/FieldSelectMultiple.tsx +55 -10
- package/src/components/FieldSelect/FieldSelectSingle.tsx +33 -15
- package/src/components/FieldSelect/{hooks.ts → hooks/common.ts} +4 -5
- package/src/components/FieldSelect/hooks/customOption.ts +90 -0
- package/src/components/FieldSelect/hooks/index.ts +2 -0
- package/src/components/FieldSelect/index.ts +2 -0
- package/src/components/FieldSelect/types.ts +24 -3
- package/src/components/FieldSelect/utils/customOption.ts +23 -0
- package/src/components/FieldSelect/utils/index.ts +2 -0
- package/src/components/index.ts +0 -1
- package/dist/cjs/components/FieldAdd/FieldAdd.d.ts +0 -52
- package/dist/cjs/components/FieldAdd/FieldAdd.js +0 -216
- package/dist/cjs/components/FieldAdd/index.d.ts +0 -2
- package/dist/cjs/components/FieldAdd/index.js +0 -13
- package/dist/cjs/components/FieldAdd/styles.module.css +0 -51
- package/dist/esm/components/FieldAdd/FieldAdd.d.ts +0 -52
- package/dist/esm/components/FieldAdd/FieldAdd.js +0 -31
- package/dist/esm/components/FieldAdd/index.d.ts +0 -2
- package/dist/esm/components/FieldAdd/index.js +0 -1
- package/dist/esm/components/FieldAdd/styles.module.css +0 -51
- package/src/components/FieldAdd/FieldAdd.tsx +0 -215
- package/src/components/FieldAdd/index.ts +0 -3
- package/src/components/FieldAdd/styles.module.scss +0 -60
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const getCustomOptionTriggerByCode = (code) => {
|
|
2
|
+
switch (code) {
|
|
3
|
+
case 'Enter':
|
|
4
|
+
return 'enter';
|
|
5
|
+
case 'Space':
|
|
6
|
+
return 'space';
|
|
7
|
+
case 'Comma':
|
|
8
|
+
return 'comma';
|
|
9
|
+
default:
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
export const shouldHandleCustomOptionTrigger = (trigger, availableTriggers) => (trigger ? availableTriggers.includes(trigger) : false);
|
|
@@ -32,5 +32,5 @@ type FieldTextOwnProps = {
|
|
|
32
32
|
export type FieldTextProps = WithSupportProps<FieldTextOwnProps & InputProps & WrapperProps>;
|
|
33
33
|
export declare const FieldText: import("react").ForwardRefExoticComponent<{
|
|
34
34
|
'data-test-id'?: string;
|
|
35
|
-
} & import("react").AriaAttributes & FieldTextOwnProps & Pick<Partial<InputPrivateProps>, "onChange" | "value"> & Pick<Required<InputPrivateProps>, "inputMode"> & Pick<InputPrivateProps, "id" | "name" | "onPaste" | "onFocus" | "onBlur" | "onKeyDown" | "disabled" | "
|
|
35
|
+
} & import("react").AriaAttributes & FieldTextOwnProps & Pick<Partial<InputPrivateProps>, "onChange" | "value"> & Pick<Required<InputPrivateProps>, "inputMode"> & Pick<InputPrivateProps, "id" | "name" | "onPaste" | "onFocus" | "onBlur" | "onKeyDown" | "disabled" | "readonly" | "placeholder" | "autoFocus" | "autoComplete" | "maxLength" | "spellCheck" | "pattern"> & WrapperProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
36
36
|
export {};
|
|
@@ -30,5 +30,5 @@ type FieldTextAreaOwnProps = {
|
|
|
30
30
|
export type FieldTextAreaProps = WithSupportProps<FieldTextAreaOwnProps & InputProps & WrapperProps>;
|
|
31
31
|
export declare const FieldTextArea: import("react").ForwardRefExoticComponent<{
|
|
32
32
|
'data-test-id'?: string;
|
|
33
|
-
} & import("react").AriaAttributes & FieldTextAreaOwnProps & Pick<Partial<TextAreaProps>, "value"> & Pick<TextAreaProps, "id" | "name" | "onFocus" | "onBlur" | "onKeyDown" | "disabled" | "
|
|
33
|
+
} & import("react").AriaAttributes & FieldTextAreaOwnProps & Pick<Partial<TextAreaProps>, "value"> & Pick<TextAreaProps, "id" | "name" | "onFocus" | "onBlur" | "onKeyDown" | "disabled" | "readonly" | "placeholder" | "autoFocus" | "inputMode" | "maxLength" | "spellCheck"> & WrapperProps & import("react").RefAttributes<HTMLTextAreaElement>>;
|
|
34
34
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { ButtonFieldProps } from '../ButtonField';
|
|
2
|
-
export declare const ButtonFieldList: import("react").ForwardRefExoticComponent<Omit<ButtonFieldProps, "hasArrow" | "arrowOpen"> & Pick<import("@snack-uikit/list").DroplistProps, "search" | "
|
|
2
|
+
export declare const ButtonFieldList: import("react").ForwardRefExoticComponent<Omit<ButtonFieldProps, "hasArrow" | "arrowOpen"> & Pick<import("@snack-uikit/list").DroplistProps, "search" | "open" | "onOpenChange" | "items" | "scroll"> & {
|
|
3
3
|
selection?: Omit<import("@snack-uikit/list").SelectionSingleState, "mode">;
|
|
4
4
|
} & import("react").RefAttributes<HTMLButtonElement>>;
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
6
|
"title": "Fields",
|
|
7
|
-
"version": "0.
|
|
7
|
+
"version": "0.54.0",
|
|
8
8
|
"sideEffects": [
|
|
9
9
|
"*.css",
|
|
10
10
|
"*.woff",
|
|
@@ -66,5 +66,5 @@
|
|
|
66
66
|
"peerDependencies": {
|
|
67
67
|
"@snack-uikit/locale": "*"
|
|
68
68
|
},
|
|
69
|
-
"gitHead": "
|
|
69
|
+
"gitHead": "91f67a065fb1a5b9b57c9c03374c752e3acf3a0f"
|
|
70
70
|
}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import cn from 'classnames';
|
|
2
2
|
import mergeRefs from 'merge-refs';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
FocusEvent,
|
|
5
|
+
forwardRef,
|
|
6
|
+
KeyboardEvent,
|
|
7
|
+
KeyboardEventHandler,
|
|
8
|
+
useCallback,
|
|
9
|
+
useEffect,
|
|
10
|
+
useRef,
|
|
11
|
+
useState,
|
|
12
|
+
} from 'react';
|
|
4
13
|
|
|
5
14
|
import { InputPrivate } from '@snack-uikit/input-private';
|
|
6
15
|
import { BaseItemProps, Droplist, ItemProps, SelectionSingleValueType } from '@snack-uikit/list';
|
|
@@ -12,10 +21,24 @@ import { usePostfix, usePrefix, useValueControl } from '../../hooks';
|
|
|
12
21
|
import { getValidationState } from '../../utils/getValidationState';
|
|
13
22
|
import { FieldDecorator } from '../FieldDecorator';
|
|
14
23
|
import { extractFieldDecoratorProps } from '../FieldDecorator/utils';
|
|
15
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
useButtons,
|
|
26
|
+
useFieldSelectMultipleCustomOption,
|
|
27
|
+
useHandleDeleteItem,
|
|
28
|
+
useHandleOnKeyDown,
|
|
29
|
+
useSearch,
|
|
30
|
+
useSearchInput,
|
|
31
|
+
} from './hooks';
|
|
16
32
|
import styles from './styles.module.scss';
|
|
17
33
|
import { FieldSelectMultipleProps, ItemWithId, SelectedOptionFormatter } from './types';
|
|
18
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
checkisSearchUnavailable,
|
|
36
|
+
extractListProps,
|
|
37
|
+
getArrowIcon,
|
|
38
|
+
getCustomOptionTriggerByCode,
|
|
39
|
+
shouldHandleCustomOptionTrigger,
|
|
40
|
+
updateMultipleItems,
|
|
41
|
+
} from './utils';
|
|
19
42
|
|
|
20
43
|
const BASE_MIN_WIDTH = 4;
|
|
21
44
|
|
|
@@ -47,6 +70,7 @@ export const FieldSelectMultiple = forwardRef<HTMLInputElement, FieldSelectMulti
|
|
|
47
70
|
postfix,
|
|
48
71
|
removeByBackspace = false,
|
|
49
72
|
addOptionByEnter = false,
|
|
73
|
+
addCustomOptionTriggers,
|
|
50
74
|
untouchableScrollbars = false,
|
|
51
75
|
open: openProp,
|
|
52
76
|
enableFuzzySearch = true,
|
|
@@ -80,6 +104,15 @@ export const FieldSelectMultiple = forwardRef<HTMLInputElement, FieldSelectMulti
|
|
|
80
104
|
resetSearchOnOptionSelection,
|
|
81
105
|
});
|
|
82
106
|
|
|
107
|
+
const { resolvedAddCustomOptionTriggers, tryCommitCustomOptionFromInput } = useFieldSelectMultipleCustomOption({
|
|
108
|
+
addCustomOptionTriggers,
|
|
109
|
+
addOptionByEnter,
|
|
110
|
+
inputValue,
|
|
111
|
+
value,
|
|
112
|
+
setValue,
|
|
113
|
+
updateInputValue,
|
|
114
|
+
});
|
|
115
|
+
|
|
83
116
|
const prefixSettings = usePrefix({ prefix, disabled });
|
|
84
117
|
const postfixSettings = usePostfix({ postfix, disabled });
|
|
85
118
|
|
|
@@ -114,6 +147,15 @@ export const FieldSelectMultiple = forwardRef<HTMLInputElement, FieldSelectMulti
|
|
|
114
147
|
});
|
|
115
148
|
|
|
116
149
|
const handleItemDelete = useHandleDeleteItem(setValue);
|
|
150
|
+
const handleTagDelete = useCallback(
|
|
151
|
+
(option: BaseItemProps) => () => {
|
|
152
|
+
const deleteItemHandler = handleItemDelete(option);
|
|
153
|
+
deleteItemHandler();
|
|
154
|
+
localRef.current?.focus();
|
|
155
|
+
},
|
|
156
|
+
[handleItemDelete],
|
|
157
|
+
);
|
|
158
|
+
|
|
117
159
|
const handleOnKeyDown = (onKeyDown?: KeyboardEventHandler<HTMLElement>) => (e: KeyboardEvent<HTMLInputElement>) => {
|
|
118
160
|
if (removeByBackspace && e.code === 'Backspace' && inputValue === '') {
|
|
119
161
|
if (selectedItems?.length && !selectedItems.slice(-1)[0].disabled) {
|
|
@@ -124,12 +166,14 @@ export const FieldSelectMultiple = forwardRef<HTMLInputElement, FieldSelectMulti
|
|
|
124
166
|
if (e.code === 'Enter') {
|
|
125
167
|
e.stopPropagation();
|
|
126
168
|
e.preventDefault();
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
169
|
+
tryCommitCustomOptionFromInput('enter');
|
|
170
|
+
} else {
|
|
171
|
+
const customOptionTrigger = getCustomOptionTriggerByCode(e.code);
|
|
172
|
+
|
|
173
|
+
if (shouldHandleCustomOptionTrigger(customOptionTrigger, resolvedAddCustomOptionTriggers)) {
|
|
174
|
+
e.stopPropagation();
|
|
175
|
+
e.preventDefault();
|
|
176
|
+
tryCommitCustomOptionFromInput(customOptionTrigger);
|
|
133
177
|
}
|
|
134
178
|
}
|
|
135
179
|
|
|
@@ -160,6 +204,7 @@ export const FieldSelectMultiple = forwardRef<HTMLInputElement, FieldSelectMulti
|
|
|
160
204
|
|
|
161
205
|
const handleBlur = (e: FocusEvent<HTMLInputElement>) => {
|
|
162
206
|
if (!open && !buttonsRefs.filter(Boolean).includes(e.relatedTarget)) {
|
|
207
|
+
tryCommitCustomOptionFromInput('blur');
|
|
163
208
|
updateInputValue();
|
|
164
209
|
|
|
165
210
|
rest?.onBlur?.(e);
|
|
@@ -251,7 +296,7 @@ export const FieldSelectMultiple = forwardRef<HTMLInputElement, FieldSelectMulti
|
|
|
251
296
|
label={selectedOptionFormatter(option)}
|
|
252
297
|
key={option.id}
|
|
253
298
|
appearance={option.appearance ?? 'neutral'}
|
|
254
|
-
onDelete={!option.disabled && !disabled && !readonly ?
|
|
299
|
+
onDelete={!option.disabled && !disabled && !readonly ? handleTagDelete(option) : undefined}
|
|
255
300
|
className={styles.tag}
|
|
256
301
|
data-disabled={disabled || undefined}
|
|
257
302
|
/>
|
|
@@ -20,10 +20,16 @@ import { usePostfix, usePrefix, useValueControl } from '../../hooks';
|
|
|
20
20
|
import { getValidationState } from '../../utils/getValidationState';
|
|
21
21
|
import { FieldDecorator } from '../FieldDecorator';
|
|
22
22
|
import { extractFieldDecoratorProps } from '../FieldDecorator/utils';
|
|
23
|
-
import { useButtons, useHandleOnKeyDown, useSearch, useSearchInput } from './hooks';
|
|
23
|
+
import { useButtons, useFieldSelectSingleCustomOption, useHandleOnKeyDown, useSearch, useSearchInput } from './hooks';
|
|
24
24
|
import styles from './styles.module.scss';
|
|
25
25
|
import { FieldSelectSingleProps, ItemWithId, SelectedOptionFormatter } from './types';
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
checkisSearchUnavailable,
|
|
28
|
+
extractListProps,
|
|
29
|
+
getArrowIcon,
|
|
30
|
+
shouldHandleCustomOptionTrigger,
|
|
31
|
+
updateItems,
|
|
32
|
+
} from './utils';
|
|
27
33
|
|
|
28
34
|
const defaultSelectedOptionFormatter: SelectedOptionFormatter = item =>
|
|
29
35
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
@@ -54,6 +60,7 @@ export const FieldSelectSingle = forwardRef<HTMLInputElement, FieldSelectSingleP
|
|
|
54
60
|
prefix,
|
|
55
61
|
postfix,
|
|
56
62
|
addOptionByEnter = false,
|
|
63
|
+
addCustomOptionTriggers: addCustomOptionTriggersProp,
|
|
57
64
|
untouchableScrollbars = false,
|
|
58
65
|
open: openProp,
|
|
59
66
|
onOpenChange,
|
|
@@ -131,14 +138,6 @@ export const FieldSelectSingle = forwardRef<HTMLInputElement, FieldSelectSingleP
|
|
|
131
138
|
valueToCopy: selectedOptionFormatter(selectedItem),
|
|
132
139
|
});
|
|
133
140
|
|
|
134
|
-
const handleBlur = (e: FocusEvent<HTMLInputElement>) => {
|
|
135
|
-
if (!open && !buttonsRefs.filter(Boolean).includes(e.relatedTarget)) {
|
|
136
|
-
updateInputValue(selectedItem);
|
|
137
|
-
|
|
138
|
-
rest?.onBlur?.(e);
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
|
|
142
141
|
const commonHandleOnKeyDown = useHandleOnKeyDown({
|
|
143
142
|
inputKeyDownNavigationHandler,
|
|
144
143
|
onInputKeyDownProp,
|
|
@@ -157,6 +156,28 @@ export const FieldSelectSingle = forwardRef<HTMLInputElement, FieldSelectSingleP
|
|
|
157
156
|
[setOpen, setValue],
|
|
158
157
|
);
|
|
159
158
|
|
|
159
|
+
const { resolvedAddCustomOptionTriggers, tryCommitCustomOptionFromInput } = useFieldSelectSingleCustomOption({
|
|
160
|
+
addCustomOptionTriggers: addCustomOptionTriggersProp,
|
|
161
|
+
addOptionByEnter,
|
|
162
|
+
inputValue,
|
|
163
|
+
handleSelectionChange,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const handleBlur = (e: FocusEvent<HTMLInputElement>) => {
|
|
167
|
+
if (!open && !buttonsRefs.filter(Boolean).includes(e.relatedTarget)) {
|
|
168
|
+
const commitOnBlur =
|
|
169
|
+
shouldHandleCustomOptionTrigger('blur', resolvedAddCustomOptionTriggers) && inputValue !== '';
|
|
170
|
+
|
|
171
|
+
if (commitOnBlur) {
|
|
172
|
+
tryCommitCustomOptionFromInput('blur');
|
|
173
|
+
} else {
|
|
174
|
+
updateInputValue(selectedItem);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
rest?.onBlur?.(e);
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
160
181
|
const handleOnKeyDown = (onKeyDown?: KeyboardEventHandler<HTMLElement>) => (e: KeyboardEvent<HTMLInputElement>) => {
|
|
161
182
|
if (!open && prevInputValue.current !== inputValue) {
|
|
162
183
|
setOpen(true);
|
|
@@ -165,10 +186,7 @@ export const FieldSelectSingle = forwardRef<HTMLInputElement, FieldSelectSingleP
|
|
|
165
186
|
if (e.code === 'Enter') {
|
|
166
187
|
e.stopPropagation();
|
|
167
188
|
e.preventDefault();
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if (addOptionByEnter && e.code === 'Enter' && inputValue !== '') {
|
|
171
|
-
handleSelectionChange(inputValue);
|
|
189
|
+
tryCommitCustomOptionFromInput('enter');
|
|
172
190
|
}
|
|
173
191
|
|
|
174
192
|
commonHandleOnKeyDown(onKeyDown)(e);
|
|
@@ -178,7 +196,7 @@ export const FieldSelectSingle = forwardRef<HTMLInputElement, FieldSelectSingleP
|
|
|
178
196
|
if (isBrowser() && !readonly && !disabled && !buttonsRefs.includes(document.activeElement)) {
|
|
179
197
|
setOpen(open);
|
|
180
198
|
|
|
181
|
-
if (!open) {
|
|
199
|
+
if (!open && !shouldHandleCustomOptionTrigger('blur', resolvedAddCustomOptionTriggers)) {
|
|
182
200
|
updateInputValue(selectedItem);
|
|
183
201
|
}
|
|
184
202
|
}
|
|
@@ -11,11 +11,10 @@ import {
|
|
|
11
11
|
SelectionSingleValueType,
|
|
12
12
|
} from '@snack-uikit/list';
|
|
13
13
|
|
|
14
|
-
import { useCopyButton, useValueControl } from '
|
|
15
|
-
import { extractChildIds } from '
|
|
16
|
-
import { ItemWithId, SearchState, SelectedOptionFormatter } from '
|
|
17
|
-
import { getValueByPath, isBaseOptionProps } from '
|
|
18
|
-
import { filterItemsByFlattenIds } from './utils/filterItemsByFlattenIds';
|
|
14
|
+
import { useCopyButton, useValueControl } from '../../../hooks';
|
|
15
|
+
import { extractChildIds } from '../legacy';
|
|
16
|
+
import { ItemWithId, SearchState, SelectedOptionFormatter } from '../types';
|
|
17
|
+
import { filterItemsByFlattenIds, getValueByPath, isBaseOptionProps } from '../utils';
|
|
19
18
|
|
|
20
19
|
type UseHandleOnKeyDownProps = {
|
|
21
20
|
inputKeyDownNavigationHandler: KeyboardEventHandler<HTMLInputElement>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { useCallback, useMemo } from 'react';
|
|
2
|
+
import { Handler } from 'uncontrollable';
|
|
3
|
+
|
|
4
|
+
import { SelectionSingleValueType } from '@snack-uikit/list';
|
|
5
|
+
|
|
6
|
+
import { FieldSelectMultipleAddCustomOptionTrigger } from '../types';
|
|
7
|
+
|
|
8
|
+
type UseResolvedAddCustomOptionTriggersParams = {
|
|
9
|
+
addCustomOptionTriggers?: FieldSelectMultipleAddCustomOptionTrigger[];
|
|
10
|
+
addOptionByEnter: boolean;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function useResolvedAddCustomOptionTriggers({
|
|
14
|
+
addCustomOptionTriggers,
|
|
15
|
+
addOptionByEnter,
|
|
16
|
+
}: UseResolvedAddCustomOptionTriggersParams): FieldSelectMultipleAddCustomOptionTrigger[] {
|
|
17
|
+
return useMemo(() => {
|
|
18
|
+
if (addCustomOptionTriggers !== undefined) {
|
|
19
|
+
return addCustomOptionTriggers;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return addOptionByEnter ? ['enter'] : [];
|
|
23
|
+
}, [addCustomOptionTriggers, addOptionByEnter]);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type UseFieldSelectMultipleCustomOptionParams = UseResolvedAddCustomOptionTriggersParams & {
|
|
27
|
+
inputValue: string;
|
|
28
|
+
value: SelectionSingleValueType[] | undefined;
|
|
29
|
+
setValue: Handler;
|
|
30
|
+
updateInputValue: () => void;
|
|
31
|
+
};
|
|
32
|
+
export function useFieldSelectMultipleCustomOption({
|
|
33
|
+
addCustomOptionTriggers,
|
|
34
|
+
addOptionByEnter,
|
|
35
|
+
inputValue,
|
|
36
|
+
value,
|
|
37
|
+
setValue,
|
|
38
|
+
updateInputValue,
|
|
39
|
+
}: UseFieldSelectMultipleCustomOptionParams) {
|
|
40
|
+
const resolvedAddCustomOptionTriggers = useResolvedAddCustomOptionTriggers({
|
|
41
|
+
addCustomOptionTriggers,
|
|
42
|
+
addOptionByEnter,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const tryCommitCustomOptionFromInput = useCallback(
|
|
46
|
+
(trigger: FieldSelectMultipleAddCustomOptionTrigger) => {
|
|
47
|
+
if (!resolvedAddCustomOptionTriggers.includes(trigger) || inputValue === '') {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!(value ?? []).includes(inputValue)) {
|
|
52
|
+
setValue((prev: SelectionSingleValueType[]) => (prev ?? []).concat(inputValue));
|
|
53
|
+
updateInputValue();
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
[resolvedAddCustomOptionTriggers, inputValue, value, setValue, updateInputValue],
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
return { resolvedAddCustomOptionTriggers, tryCommitCustomOptionFromInput };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
type UseFieldSelectSingleCustomOptionParams = UseResolvedAddCustomOptionTriggersParams & {
|
|
63
|
+
inputValue: string;
|
|
64
|
+
handleSelectionChange: (value: SelectionSingleValueType) => void;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export function useFieldSelectSingleCustomOption({
|
|
68
|
+
addCustomOptionTriggers,
|
|
69
|
+
addOptionByEnter,
|
|
70
|
+
inputValue,
|
|
71
|
+
handleSelectionChange,
|
|
72
|
+
}: UseFieldSelectSingleCustomOptionParams) {
|
|
73
|
+
const resolvedAddCustomOptionTriggers = useResolvedAddCustomOptionTriggers({
|
|
74
|
+
addCustomOptionTriggers,
|
|
75
|
+
addOptionByEnter,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const tryCommitCustomOptionFromInput = useCallback(
|
|
79
|
+
(trigger: FieldSelectMultipleAddCustomOptionTrigger) => {
|
|
80
|
+
if (!resolvedAddCustomOptionTriggers.includes(trigger) || inputValue === '') {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
handleSelectionChange(inputValue);
|
|
85
|
+
},
|
|
86
|
+
[resolvedAddCustomOptionTriggers, inputValue, handleSelectionChange],
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
return { resolvedAddCustomOptionTriggers, tryCommitCustomOptionFromInput };
|
|
90
|
+
}
|
|
@@ -70,10 +70,16 @@ export type SearchState = {
|
|
|
70
70
|
onChange?(value: string): void;
|
|
71
71
|
};
|
|
72
72
|
|
|
73
|
+
/** События, по которым произвольное значение из строки поиска фиксируется в значении поля */
|
|
74
|
+
export type FieldSelectMultipleAddCustomOptionTrigger = 'enter' | 'blur' | 'space' | 'comma';
|
|
75
|
+
export type FieldSelectSingleAddCustomOptionTrigger = Extract<
|
|
76
|
+
FieldSelectMultipleAddCustomOptionTrigger,
|
|
77
|
+
'enter' | 'blur'
|
|
78
|
+
>;
|
|
79
|
+
|
|
73
80
|
export type FieldSelectPrivateProps = InputProps &
|
|
74
81
|
WrapperProps & {
|
|
75
82
|
options: OptionProps[];
|
|
76
|
-
/** Отображать ли состояние загрузки данных в поле и списке */
|
|
77
83
|
loading?: boolean;
|
|
78
84
|
/** Произвольный префикс для поля */
|
|
79
85
|
prefix?: ReactNode;
|
|
@@ -114,6 +120,9 @@ type FiledSelectCommonProps = WithSupportProps<{
|
|
|
114
120
|
|
|
115
121
|
autocomplete?: boolean;
|
|
116
122
|
|
|
123
|
+
/**
|
|
124
|
+
* @deprecated Используйте `addCustomOptionTriggers`
|
|
125
|
+
*/
|
|
117
126
|
addOptionByEnter?: boolean;
|
|
118
127
|
|
|
119
128
|
open?: boolean;
|
|
@@ -148,12 +157,24 @@ type FiledSelectCommonProps = WithSupportProps<{
|
|
|
148
157
|
export type FieldSelectSingleProps = FieldSelectPrivateProps &
|
|
149
158
|
Omit<SelectionSingleState, 'mode'> &
|
|
150
159
|
WrapperProps &
|
|
151
|
-
FiledSelectCommonProps
|
|
160
|
+
FiledSelectCommonProps & {
|
|
161
|
+
/**
|
|
162
|
+
* Триггеры фиксации произвольного значения из строки поиска в режиме `single`.
|
|
163
|
+
* Если передан, имеет приоритет над устаревшим `addOptionByEnter`.
|
|
164
|
+
*/
|
|
165
|
+
addCustomOptionTriggers?: FieldSelectSingleAddCustomOptionTrigger[];
|
|
166
|
+
};
|
|
152
167
|
|
|
153
168
|
export type FieldSelectMultipleProps = FieldSelectPrivateProps & {
|
|
154
169
|
removeByBackspace?: boolean;
|
|
155
170
|
} & Omit<SelectionMultipleState, 'mode'> &
|
|
156
|
-
Omit<FiledSelectCommonProps, 'showCopyButton' | 'onCopyButtonClick'
|
|
171
|
+
Omit<FiledSelectCommonProps, 'showCopyButton' | 'onCopyButtonClick'> & {
|
|
172
|
+
/**
|
|
173
|
+
* Триггеры фиксации произвольного значения из строки поиска в режиме `multiple`.
|
|
174
|
+
* Если передан, имеет приоритет над устаревшим `addOptionByEnter`.
|
|
175
|
+
*/
|
|
176
|
+
addCustomOptionTriggers?: FieldSelectMultipleAddCustomOptionTrigger[];
|
|
177
|
+
};
|
|
157
178
|
|
|
158
179
|
export type FieldSelectProps =
|
|
159
180
|
| (FieldSelectSingleProps & { selection?: 'single' })
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { KeyboardEvent } from 'react';
|
|
2
|
+
|
|
3
|
+
import { FieldSelectMultipleAddCustomOptionTrigger } from '../types';
|
|
4
|
+
|
|
5
|
+
export const getCustomOptionTriggerByCode = (
|
|
6
|
+
code: KeyboardEvent<HTMLInputElement>['code'],
|
|
7
|
+
): FieldSelectMultipleAddCustomOptionTrigger | undefined => {
|
|
8
|
+
switch (code) {
|
|
9
|
+
case 'Enter':
|
|
10
|
+
return 'enter';
|
|
11
|
+
case 'Space':
|
|
12
|
+
return 'space';
|
|
13
|
+
case 'Comma':
|
|
14
|
+
return 'comma';
|
|
15
|
+
default:
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const shouldHandleCustomOptionTrigger = (
|
|
21
|
+
trigger: FieldSelectMultipleAddCustomOptionTrigger | undefined,
|
|
22
|
+
availableTriggers: FieldSelectMultipleAddCustomOptionTrigger[],
|
|
23
|
+
): trigger is FieldSelectMultipleAddCustomOptionTrigger => (trigger ? availableTriggers.includes(trigger) : false);
|
package/src/components/index.ts
CHANGED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { ButtonOutlineProps } from '@snack-uikit/button';
|
|
2
|
-
import { TooltipProps } from '@snack-uikit/tooltip';
|
|
3
|
-
import { FieldSelectSingleProps } from '../FieldSelect';
|
|
4
|
-
export type FieldAddButtonProps = Omit<ButtonOutlineProps, 'appearance' | 'icon' | 'size' | 'label'> & {
|
|
5
|
-
/** Текст действия для кнопки добавления; используется как fallback для `tooltip.tip` и `aria-label` */
|
|
6
|
-
label?: string;
|
|
7
|
-
/** Тултип кнопки добавления */
|
|
8
|
-
tooltip?: Omit<TooltipProps, 'children'>;
|
|
9
|
-
};
|
|
10
|
-
export type FieldAddProps = FieldSelectSingleProps & {
|
|
11
|
-
/** Настройки кнопки добавления справа от поля */
|
|
12
|
-
addButton?: FieldAddButtonProps;
|
|
13
|
-
/** Тултип над контролом выбора значения */
|
|
14
|
-
controlTooltip?: Omit<TooltipProps, 'children'>;
|
|
15
|
-
/** Показывать ли кнопку добавления; в `readonly` режиме кнопка скрывается независимо от значения пропса */
|
|
16
|
-
showAddButton?: boolean;
|
|
17
|
-
};
|
|
18
|
-
export declare const FieldAdd: import("react").ForwardRefExoticComponent<import("../FieldSelect/types").InputProps & import("../FieldSelect/types").WrapperProps & {
|
|
19
|
-
options: import("../FieldSelect").OptionProps[];
|
|
20
|
-
loading?: boolean;
|
|
21
|
-
prefix?: import("react").ReactNode;
|
|
22
|
-
postfix?: import("react").ReactNode;
|
|
23
|
-
} & Omit<import("@snack-uikit/list").SelectionSingleState, "mode"> & {
|
|
24
|
-
'data-test-id'?: string;
|
|
25
|
-
} & import("react").AriaAttributes & {
|
|
26
|
-
options: import("../FieldSelect").OptionProps[];
|
|
27
|
-
pinTop?: import("../FieldSelect").OptionProps[];
|
|
28
|
-
pinBottom?: import("../FieldSelect").OptionProps[];
|
|
29
|
-
searchable?: boolean;
|
|
30
|
-
showCopyButton?: boolean;
|
|
31
|
-
onCopyButtonClick?(): void;
|
|
32
|
-
showClearButton?: boolean;
|
|
33
|
-
readonly?: boolean;
|
|
34
|
-
prefixIcon?: import("react").ReactElement;
|
|
35
|
-
footer?: import("@snack-uikit/list").DroplistProps["footer"];
|
|
36
|
-
widthStrategy?: import("@snack-uikit/list").DroplistProps["widthStrategy"];
|
|
37
|
-
search?: import("../FieldSelect/types").SearchState;
|
|
38
|
-
autocomplete?: boolean;
|
|
39
|
-
addOptionByEnter?: boolean;
|
|
40
|
-
open?: boolean;
|
|
41
|
-
enableFuzzySearch?: boolean;
|
|
42
|
-
resetSearchOnOptionSelection?: boolean;
|
|
43
|
-
onOpenChange?(open: boolean): void;
|
|
44
|
-
selectedOptionFormatter?: import("../FieldSelect").SelectedOptionFormatter;
|
|
45
|
-
} & Pick<import("@snack-uikit/list").DroplistProps, "onScroll" | "scrollRef" | "scrollContainerRef" | "untouchableScrollbars" | "dataError" | "dataFiltered" | "scrollToSelectedItem" | "virtualized" | "noDataState" | "noResultsState" | "errorDataState"> & {
|
|
46
|
-
/** Настройки кнопки добавления справа от поля */
|
|
47
|
-
addButton?: FieldAddButtonProps;
|
|
48
|
-
/** Тултип над контролом выбора значения */
|
|
49
|
-
controlTooltip?: Omit<TooltipProps, "children">;
|
|
50
|
-
/** Показывать ли кнопку добавления; в `readonly` режиме кнопка скрывается независимо от значения пропса */
|
|
51
|
-
showAddButton?: boolean;
|
|
52
|
-
} & import("react").RefAttributes<HTMLInputElement>>;
|