@snack-uikit/fields 0.14.2 → 0.15.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.
Files changed (62) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +15 -14
  3. package/dist/components/FieldDecorator/Header.js +1 -1
  4. package/dist/components/FieldDecorator/styles.module.css +7 -1
  5. package/dist/components/FieldSelect/FieldSelect.d.ts +1 -7
  6. package/dist/components/FieldSelect/FieldSelect.js +9 -21
  7. package/dist/components/FieldSelect/FieldSelectMultiple.d.ts +17 -0
  8. package/dist/components/FieldSelect/FieldSelectMultiple.js +118 -0
  9. package/dist/components/FieldSelect/FieldSelectSingle.d.ts +9 -28
  10. package/dist/components/FieldSelect/FieldSelectSingle.js +69 -55
  11. package/dist/components/FieldSelect/hooks.d.ts +30 -0
  12. package/dist/components/FieldSelect/hooks.js +72 -0
  13. package/dist/components/FieldSelect/index.d.ts +2 -1
  14. package/dist/components/FieldSelect/index.js +1 -1
  15. package/dist/components/FieldSelect/styles.module.css +129 -27
  16. package/dist/components/FieldSelect/types.d.ts +42 -37
  17. package/dist/components/FieldSelect/utils.d.ts +19 -0
  18. package/dist/components/FieldSelect/utils.js +112 -0
  19. package/dist/helperComponents/FieldContainerPrivate/styles.module.css +30 -6
  20. package/package.json +5 -3
  21. package/src/components/FieldDecorator/Header.tsx +6 -1
  22. package/src/components/FieldDecorator/styles.module.scss +38 -30
  23. package/src/components/FieldSelect/FieldSelect.tsx +13 -21
  24. package/src/components/FieldSelect/FieldSelectMultiple.tsx +255 -0
  25. package/src/components/FieldSelect/FieldSelectSingle.tsx +159 -99
  26. package/src/components/FieldSelect/hooks.ts +125 -0
  27. package/src/components/FieldSelect/index.ts +12 -1
  28. package/src/components/FieldSelect/styles.module.scss +71 -31
  29. package/src/components/FieldSelect/types.ts +55 -40
  30. package/src/components/FieldSelect/utils.ts +163 -0
  31. package/src/helperComponents/FieldContainerPrivate/FieldContainerPrivate.tsx +2 -0
  32. package/src/helperComponents/FieldContainerPrivate/styles.module.scss +32 -11
  33. package/dist/components/FieldSelect/FieldSelectBase.d.ts +0 -31
  34. package/dist/components/FieldSelect/FieldSelectBase.js +0 -51
  35. package/dist/components/FieldSelect/FieldSelectMulti.d.ts +0 -37
  36. package/dist/components/FieldSelect/FieldSelectMulti.js +0 -89
  37. package/dist/components/FieldSelect/constants.d.ts +0 -7
  38. package/dist/components/FieldSelect/constants.js +0 -6
  39. package/dist/components/FieldSelect/helpers/getArrowIcon.d.ts +0 -8
  40. package/dist/components/FieldSelect/helpers/getArrowIcon.js +0 -8
  41. package/dist/components/FieldSelect/helpers/getDisplayedValue.d.ts +0 -10
  42. package/dist/components/FieldSelect/helpers/getDisplayedValue.js +0 -12
  43. package/dist/components/FieldSelect/helpers/index.d.ts +0 -2
  44. package/dist/components/FieldSelect/helpers/index.js +0 -2
  45. package/dist/components/FieldSelect/hooks/index.d.ts +0 -3
  46. package/dist/components/FieldSelect/hooks/index.js +0 -3
  47. package/dist/components/FieldSelect/hooks/useFilteredOptions.d.ts +0 -7
  48. package/dist/components/FieldSelect/hooks/useFilteredOptions.js +0 -6
  49. package/dist/components/FieldSelect/hooks/useList.d.ts +0 -37
  50. package/dist/components/FieldSelect/hooks/useList.js +0 -52
  51. package/dist/components/FieldSelect/hooks/useListNavigation.d.ts +0 -26
  52. package/dist/components/FieldSelect/hooks/useListNavigation.js +0 -48
  53. package/src/components/FieldSelect/FieldSelectBase.tsx +0 -222
  54. package/src/components/FieldSelect/FieldSelectMulti.tsx +0 -163
  55. package/src/components/FieldSelect/constants.ts +0 -9
  56. package/src/components/FieldSelect/helpers/getArrowIcon.ts +0 -9
  57. package/src/components/FieldSelect/helpers/getDisplayedValue.ts +0 -25
  58. package/src/components/FieldSelect/helpers/index.ts +0 -2
  59. package/src/components/FieldSelect/hooks/index.ts +0 -3
  60. package/src/components/FieldSelect/hooks/useFilteredOptions.ts +0 -23
  61. package/src/components/FieldSelect/hooks/useList.ts +0 -87
  62. package/src/components/FieldSelect/hooks/useListNavigation.ts +0 -81
@@ -1,10 +0,0 @@
1
- import { SELECTION_MODE } from '../constants';
2
- import { Option } from '../types';
3
- export declare const getDisplayedValue: (props: {
4
- selectionMode: typeof SELECTION_MODE.Single;
5
- selected: Option;
6
- } | {
7
- selectionMode: typeof SELECTION_MODE.Multi;
8
- selected: Option[];
9
- getSelectedItemsText(amount: number): string;
10
- }) => string;
@@ -1,12 +0,0 @@
1
- import { SELECTION_MODE } from '../constants';
2
- export const getDisplayedValue = (props) => {
3
- if (props.selectionMode === SELECTION_MODE.Single) {
4
- return props.selected.label;
5
- }
6
- const { selected, getSelectedItemsText } = props;
7
- const selectedOptions = selected;
8
- if (selectedOptions.length > 1) {
9
- return getSelectedItemsText(selectedOptions.length);
10
- }
11
- return selectedOptions.reduce((res, cur, index, arr) => `${res}${cur.label}${index === arr.length - 1 ? '' : ','}`, '');
12
- };
@@ -1,2 +0,0 @@
1
- export * from './getArrowIcon';
2
- export * from './getDisplayedValue';
@@ -1,2 +0,0 @@
1
- export * from './getArrowIcon';
2
- export * from './getDisplayedValue';
@@ -1,3 +0,0 @@
1
- export * from './useFilteredOptions';
2
- export * from './useList';
3
- export * from './useListNavigation';
@@ -1,3 +0,0 @@
1
- export * from './useFilteredOptions';
2
- export * from './useList';
3
- export * from './useListNavigation';
@@ -1,7 +0,0 @@
1
- import { Option } from '../types';
2
- export declare function useFilteredOptions({ searchable, touched, inputValue, options, }: {
3
- searchable: boolean;
4
- touched: boolean;
5
- inputValue: string;
6
- options: Option[];
7
- }): Option[];
@@ -1,6 +0,0 @@
1
- import { useMemo } from 'react';
2
- export function useFilteredOptions({ searchable, touched, inputValue, options, }) {
3
- return useMemo(() => searchable && touched && inputValue
4
- ? options.filter(item => item.label.toLowerCase().includes(inputValue.toLowerCase().trim()))
5
- : options, [inputValue, options, searchable, touched]);
6
- }
@@ -1,37 +0,0 @@
1
- /// <reference types="react" />
2
- import { FieldSelectProps } from '../FieldSelect';
3
- import { Option } from '../types';
4
- type UseListProps = Pick<FieldSelectProps, 'readonly' | 'disabled' | 'open' | 'onOpenChange' | 'options' | 'showCopyButton'> & {
5
- searchable: NonNullable<FieldSelectProps['searchable']>;
6
- showAdditionalButton: boolean;
7
- showClearButton: boolean;
8
- inputValue: string;
9
- setInputValue(value: string): void;
10
- isChecked(option: Option): boolean;
11
- };
12
- export declare function useList({ open, onOpenChange, readonly, disabled, searchable, showAdditionalButton, showCopyButton: showCopyButtonProp, showClearButton: showClearButtonProp, inputValue, setInputValue, options, isChecked, }: UseListProps): {
13
- isOpen: boolean;
14
- setIsOpen: (value: boolean) => void;
15
- localRef: import("react").RefObject<HTMLInputElement>;
16
- clearButtonRef: import("react").RefObject<HTMLButtonElement>;
17
- showClearButton: boolean;
18
- copyButtonRef: import("react").RefObject<HTMLButtonElement>;
19
- showCopyButton: boolean;
20
- extendedOptions: {
21
- checked: boolean;
22
- disabled?: boolean | undefined;
23
- caption?: string | undefined;
24
- icon?: import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>> | undefined;
25
- description?: string | undefined;
26
- tagLabel?: string | undefined;
27
- avatar?: Omit<import("@snack-uikit/avatar").AvatarProps, "size"> | undefined;
28
- value: string;
29
- label: string;
30
- }[];
31
- onInputKeyDown: import("react").KeyboardEventHandler<HTMLInputElement>;
32
- onButtonKeyDown: (event: import("react").KeyboardEvent<Element>) => void;
33
- onInputValueChange: (value: string) => void;
34
- firstDroplistItemRefCallback: (element: HTMLButtonElement | null) => void;
35
- onDroplistFocusLeave: (direction: string) => void;
36
- };
37
- export {};
@@ -1,52 +0,0 @@
1
- import { useEffect, useRef } from 'react';
2
- import { useUncontrolledProp } from 'uncontrollable';
3
- import { useFilteredOptions } from './useFilteredOptions';
4
- import { useListNavigation } from './useListNavigation';
5
- export function useList({ open, onOpenChange, readonly, disabled, searchable, showAdditionalButton, showCopyButton: showCopyButtonProp, showClearButton: showClearButtonProp, inputValue, setInputValue, options, isChecked, }) {
6
- const touched = useRef(false);
7
- const localRef = useRef(null);
8
- const clearButtonRef = useRef(null);
9
- const copyButtonRef = useRef(null);
10
- const [isOpen, setIsOpen] = useUncontrolledProp(open, false, onOpenChange);
11
- const showDropList = isOpen && !readonly && !disabled;
12
- const showClearButton = showClearButtonProp && !readonly && (showAdditionalButton || inputValue.length > 0);
13
- const showCopyButton = Boolean(showAdditionalButton && showCopyButtonProp && readonly);
14
- const { extendedOptions, onInputKeyDown, onButtonKeyDown, onDroplistFocusLeave, firstDroplistItemRefCallback } = useListNavigation({
15
- inputRef: localRef,
16
- options: useFilteredOptions({ searchable, options, touched: touched.current, inputValue }),
17
- toggleListOpen: setIsOpen,
18
- isChecked,
19
- });
20
- const handleOpenChange = (value) => {
21
- if (!value) {
22
- touched.current = false;
23
- }
24
- setIsOpen(value);
25
- };
26
- const handleInputValueChange = (value) => {
27
- touched.current = true;
28
- setIsOpen(true);
29
- setInputValue(value);
30
- };
31
- useEffect(() => {
32
- var _a;
33
- if (open) {
34
- (_a = localRef.current) === null || _a === void 0 ? void 0 : _a.focus();
35
- }
36
- }, [localRef, open]);
37
- return {
38
- isOpen: showDropList,
39
- setIsOpen: handleOpenChange,
40
- localRef,
41
- clearButtonRef,
42
- showClearButton,
43
- copyButtonRef,
44
- showCopyButton,
45
- extendedOptions,
46
- onInputKeyDown,
47
- onButtonKeyDown,
48
- onInputValueChange: handleInputValueChange,
49
- firstDroplistItemRefCallback,
50
- onDroplistFocusLeave,
51
- };
52
- }
@@ -1,26 +0,0 @@
1
- import { KeyboardEvent, KeyboardEventHandler, RefObject } from 'react';
2
- import { Option } from '../types';
3
- type UseListNavigationProps = {
4
- options: Option[];
5
- toggleListOpen(value: boolean): void;
6
- inputRef: RefObject<HTMLInputElement>;
7
- isChecked(option: Option): boolean;
8
- };
9
- export declare function useListNavigation({ options, toggleListOpen, inputRef, isChecked }: UseListNavigationProps): {
10
- onInputKeyDown: KeyboardEventHandler<HTMLInputElement>;
11
- onButtonKeyDown: (event: KeyboardEvent) => void;
12
- onDroplistFocusLeave: (direction: string) => void;
13
- extendedOptions: {
14
- checked: boolean;
15
- disabled?: boolean | undefined;
16
- caption?: string | undefined;
17
- icon?: import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>> | undefined;
18
- description?: string | undefined;
19
- tagLabel?: string | undefined;
20
- avatar?: Omit<import("@snack-uikit/avatar").AvatarProps, "size"> | undefined;
21
- value: string;
22
- label: string;
23
- }[];
24
- firstDroplistItemRefCallback: (element: HTMLButtonElement | null) => void;
25
- };
26
- export {};
@@ -1,48 +0,0 @@
1
- import { useCallback, useMemo, useRef } from 'react';
2
- import { moveCursorToEnd, runAfterRerender } from '@snack-uikit/input-private';
3
- export function useListNavigation({ options, toggleListOpen, inputRef, isChecked }) {
4
- const setInputFocus = useCallback(() => {
5
- runAfterRerender(() => {
6
- var _a;
7
- (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
8
- moveCursorToEnd(inputRef.current);
9
- });
10
- }, [inputRef]);
11
- const firstDroplistItemRef = useRef(null);
12
- const firstDroplistItemRefCallback = useCallback((element) => {
13
- firstDroplistItemRef.current = element;
14
- }, [firstDroplistItemRef]);
15
- const setOptionFocus = useCallback(() => {
16
- runAfterRerender(() => { var _a; return (_a = firstDroplistItemRef.current) === null || _a === void 0 ? void 0 : _a.focus(); });
17
- }, [firstDroplistItemRef]);
18
- const leaveElement = (event) => {
19
- if (event.key === 'ArrowDown') {
20
- event.preventDefault();
21
- toggleListOpen(true);
22
- setOptionFocus();
23
- }
24
- if (event.key === 'Tab') {
25
- toggleListOpen(false);
26
- }
27
- };
28
- const handleInputKeyDown = event => {
29
- leaveElement(event);
30
- if (event.key === 'Enter') {
31
- toggleListOpen(true);
32
- setOptionFocus();
33
- }
34
- };
35
- const onDroplistFocusLeave = useCallback((direction) => {
36
- if (['top', 'common'].includes(direction)) {
37
- setInputFocus();
38
- }
39
- }, [setInputFocus]);
40
- const extendedOptions = useMemo(() => options.map(option => (Object.assign(Object.assign({}, option), { checked: isChecked(option) }))), [isChecked, options]);
41
- return {
42
- onInputKeyDown: handleInputKeyDown,
43
- onButtonKeyDown: leaveElement,
44
- onDroplistFocusLeave,
45
- extendedOptions,
46
- firstDroplistItemRefCallback,
47
- };
48
- }
@@ -1,222 +0,0 @@
1
- import mergeRefs from 'merge-refs';
2
- import {
3
- forwardRef,
4
- KeyboardEvent,
5
- KeyboardEventHandler,
6
- MouseEventHandler,
7
- RefCallback,
8
- RefObject,
9
- useMemo,
10
- } from 'react';
11
-
12
- import { Droplist } from '@snack-uikit/droplist';
13
- import { InputPrivate, SIZE, useButtonNavigation, useClearButton } from '@snack-uikit/input-private';
14
- import { extractSupportProps } from '@snack-uikit/utils';
15
-
16
- import { CONTAINER_VARIANT, VALIDATION_STATE } from '../../constants';
17
- import { FieldContainerPrivate } from '../../helperComponents';
18
- import { useCopyButton } from '../../hooks';
19
- import { useHandlers } from '../FieldDate/hooks/useHandlers';
20
- import { FieldDecorator } from '../FieldDecorator';
21
- import { SELECTION_MODE } from './constants';
22
- import { getArrowIcon } from './helpers';
23
- import styles from './styles.module.scss';
24
- import { ExtendedOption, FieldSelectBaseProps, Option } from './types';
25
-
26
- type BaseProps = Omit<FieldSelectBaseProps, 'open' | 'onOpenChange' | 'options'> &
27
- Required<Pick<FieldSelectBaseProps, 'open' | 'onOpenChange'>> & {
28
- localRef: RefObject<HTMLInputElement>;
29
- options: ExtendedOption[];
30
- onClear: MouseEventHandler<HTMLButtonElement>;
31
- onChange(option: Option): () => void;
32
- inputValue: string;
33
- onInputValueChange(value: string): void;
34
- displayedValue?: string;
35
- valueToCopy: string;
36
- onInputKeyDown: KeyboardEventHandler<HTMLInputElement>;
37
- onButtonKeyDown: KeyboardEventHandler<HTMLButtonElement>;
38
- showClearButton: boolean;
39
- clearButtonRef: RefObject<HTMLButtonElement>;
40
- copyButtonRef: RefObject<HTMLButtonElement>;
41
- onClick?: MouseEventHandler<HTMLElement>;
42
- onContainerPrivateMouseDown?: MouseEventHandler<HTMLElement>;
43
- onDroplistFocusLeave(direction: string): void;
44
- firstDroplistItemRefCallback: RefCallback<HTMLButtonElement>;
45
- };
46
-
47
- type Props =
48
- | ({
49
- selectionMode: typeof SELECTION_MODE.Single;
50
- selected: Option;
51
- } & BaseProps)
52
- | ({
53
- selectionMode: typeof SELECTION_MODE.Multi;
54
- selected: Option[];
55
- } & BaseProps);
56
-
57
- export const FieldSelectBase = forwardRef<HTMLInputElement, Props>(
58
- (
59
- {
60
- selectionMode,
61
- onClear,
62
- showClearButton,
63
- clearButtonRef,
64
- copyButtonRef,
65
- onChange,
66
- inputValue,
67
- onInputValueChange,
68
- displayedValue,
69
- valueToCopy,
70
- localRef,
71
- onInputKeyDown: onInputKeyDownProp,
72
- onButtonKeyDown: onButtonKeyDownProp,
73
- id,
74
- name,
75
- placeholder,
76
- options,
77
- disabled = false,
78
- readonly = false,
79
- searchable = true,
80
- showCopyButton = true,
81
- open,
82
- onOpenChange,
83
- onFocus,
84
- onBlur,
85
- onContainerPrivateMouseDown,
86
- className,
87
- label,
88
- labelTooltip,
89
- labelTooltipPlacement,
90
- required = false,
91
- hint,
92
- showHintIcon,
93
- size = SIZE.S,
94
- validationState = VALIDATION_STATE.Default,
95
- prefixIcon,
96
- locale,
97
- noDataText = locale?.language === 'ru' ? 'Нет данных' : 'No data',
98
- firstDroplistItemRefCallback,
99
- onDroplistFocusLeave,
100
- ...rest
101
- },
102
- ref,
103
- ) => {
104
- const { ArrowIcon, arrowIconSize } = getArrowIcon({ size, open });
105
- const Item = selectionMode === SELECTION_MODE.Single ? Droplist.ItemSingle : Droplist.ItemMultiple;
106
-
107
- const clearButtonSettings = useClearButton({ clearButtonRef, showClearButton, size, onClear });
108
- const copyButtonSettings = useCopyButton({ copyButtonRef, showCopyButton, size, valueToCopy });
109
- const {
110
- onInputKeyDown: inputKeyDownNavigationHandler,
111
- inputTabIndex,
112
- setInitialTabIndices,
113
- buttons,
114
- } = useButtonNavigation({
115
- inputRef: localRef,
116
- buttons: useMemo(() => [clearButtonSettings, copyButtonSettings], [clearButtonSettings, copyButtonSettings]),
117
- onButtonKeyDown: onButtonKeyDownProp,
118
- readonly,
119
- submitKeys: ['Enter', 'Space', 'Tab'],
120
- });
121
-
122
- const onInputKeyDownHandler = useHandlers<KeyboardEvent<HTMLInputElement>>([
123
- inputKeyDownNavigationHandler,
124
- onInputKeyDownProp,
125
- ]);
126
-
127
- const onFocusLeaveHandler = useHandlers([
128
- (direction: string) => {
129
- if (direction === 'common') setInitialTabIndices();
130
- },
131
- onDroplistFocusLeave,
132
- ]);
133
-
134
- return (
135
- <FieldDecorator
136
- className={className}
137
- label={label}
138
- labelTooltip={labelTooltip}
139
- labelTooltipPlacement={labelTooltipPlacement}
140
- labelFor={id}
141
- required={required}
142
- hint={hint}
143
- disabled={disabled}
144
- readonly={readonly}
145
- showHintIcon={showHintIcon}
146
- size={size}
147
- validationState={validationState}
148
- {...extractSupportProps(rest)}
149
- >
150
- <Droplist
151
- trigger='click'
152
- triggerClassName={styles.triggerClassName}
153
- triggerClickByKeys={!searchable}
154
- placement='bottom'
155
- onFocusLeave={onFocusLeaveHandler}
156
- firstElementRefCallback={firstDroplistItemRefCallback}
157
- data-test-id='field-select__list'
158
- {...(readonly || disabled ? { open: false } : { open, onOpenChange })}
159
- triggerElement={
160
- <FieldContainerPrivate
161
- className={styles.container}
162
- size={size}
163
- validationState={validationState}
164
- disabled={disabled}
165
- readonly={readonly}
166
- variant={CONTAINER_VARIANT.SingleLine}
167
- focused={open}
168
- selectable={!searchable}
169
- inputRef={localRef}
170
- prefix={prefixIcon}
171
- onMouseDown={onContainerPrivateMouseDown}
172
- postfix={
173
- <>
174
- {buttons}
175
- <ArrowIcon size={arrowIconSize} className={styles.arrowIcon} />
176
- </>
177
- }
178
- >
179
- <InputPrivate
180
- id={id}
181
- name={name}
182
- type='text'
183
- placeholder={displayedValue ? undefined : placeholder}
184
- ref={mergeRefs(ref, localRef)}
185
- onFocus={onFocus}
186
- onBlur={onBlur}
187
- disabled={disabled}
188
- data-size={size}
189
- data-test-id='field-select__input'
190
- onKeyDown={onInputKeyDownHandler}
191
- onChange={searchable ? onInputValueChange : undefined}
192
- readonly={!searchable || readonly}
193
- value={inputValue}
194
- tabIndex={inputTabIndex}
195
- />
196
-
197
- {displayedValue && (
198
- <span className={styles.displayValue} data-test-id='field-select__displayed-value'>
199
- {displayedValue}
200
- </span>
201
- )}
202
- </FieldContainerPrivate>
203
- }
204
- >
205
- {options.length === 0 ? (
206
- <Droplist.NoData size={size} label={noDataText} data-test-id='field-select__no-data' />
207
- ) : (
208
- options.map(option => (
209
- <Item
210
- onChange={onChange(option)}
211
- key={option.value}
212
- data-test-id={'field-select__list-option-' + option.value}
213
- {...option}
214
- option={option.label}
215
- />
216
- ))
217
- )}
218
- </Droplist>
219
- </FieldDecorator>
220
- );
221
- },
222
- );
@@ -1,163 +0,0 @@
1
- import { FocusEventHandler, forwardRef, MouseEventHandler, useCallback, useMemo, useRef, useState } from 'react';
2
- import { useUncontrolledProp } from 'uncontrollable';
3
-
4
- import { DEFAULT_LOCALE, SELECTION_MODE } from './constants';
5
- import { FieldSelectBase } from './FieldSelectBase';
6
- import { getDisplayedValue } from './helpers';
7
- import { useList } from './hooks';
8
- import { FieldSelectMultiProps, Option } from './types';
9
-
10
- export const FieldSelectMulti = forwardRef<HTMLInputElement, FieldSelectMultiProps>(
11
- (
12
- {
13
- value: valueProp,
14
- onChange,
15
- options,
16
- disabled = false,
17
- readonly = false,
18
- searchable = true,
19
- required = false,
20
- open,
21
- onOpenChange,
22
- locale = DEFAULT_LOCALE,
23
- getSelectedItemsText = number => (locale?.language === 'ru' ? `Выбрано: ${number}` : `Selected: ${number}`),
24
- showCopyButton: showCopyButtonProp = true,
25
- showClearButton: showClearButtonProp = true,
26
- onFocus,
27
- onBlur,
28
- ...rest
29
- },
30
- ref,
31
- ) => {
32
- const selectionMode = SELECTION_MODE.Multi;
33
- const [value, setValue] = useUncontrolledProp(valueProp, [], onChange);
34
- const selected = useMemo(() => options.filter(option => value?.includes(option.value)), [options, value]);
35
- const displayedValue = getDisplayedValue({ selectionMode, selected, getSelectedItemsText });
36
- const valueToCopy = selected.map(op => op.label).join(', ');
37
- const [inputValue, setInputValue] = useState('');
38
- const showAdditionalButton = Boolean(value?.length && value.length > 0 && !disabled);
39
- const isChecked = useCallback(
40
- (option: Option) => Boolean(selected.find(op => op.value === option.value)),
41
- [selected],
42
- );
43
- const [isFocused, setIsFocused] = useState(false);
44
- const stayOpen = useRef(false);
45
- const waitingForSearchStart = searchable && !isFocused;
46
- const showDisplayValue = displayedValue && (!searchable || waitingForSearchStart);
47
-
48
- const {
49
- isOpen,
50
- setIsOpen,
51
- localRef,
52
- extendedOptions,
53
- onInputKeyDown,
54
- onInputValueChange,
55
- onButtonKeyDown,
56
- clearButtonRef,
57
- copyButtonRef,
58
- showClearButton,
59
- showCopyButton,
60
- onDroplistFocusLeave,
61
- firstDroplistItemRefCallback,
62
- } = useList({
63
- open,
64
- onOpenChange,
65
- disabled,
66
- readonly,
67
- inputValue,
68
- setInputValue,
69
- searchable,
70
- options,
71
- isChecked,
72
- showAdditionalButton,
73
- showCopyButton: showCopyButtonProp,
74
- showClearButton: showClearButtonProp,
75
- });
76
-
77
- const handleOpenChange = (isOpen: boolean) => {
78
- if (stayOpen.current) {
79
- stayOpen.current = false;
80
- return;
81
- }
82
-
83
- setInputValue('');
84
- setIsOpen(isOpen);
85
- };
86
-
87
- const handlePreventListClose: MouseEventHandler<HTMLElement> = event => {
88
- event.preventDefault();
89
-
90
- if (isOpen && waitingForSearchStart) {
91
- stayOpen.current = true;
92
- }
93
- };
94
-
95
- const handleClear = () => {
96
- setValue([]);
97
- setInputValue('');
98
- stayOpen.current = false;
99
-
100
- if (required) {
101
- localRef.current?.focus();
102
- setIsOpen(true);
103
- } else {
104
- localRef.current?.blur();
105
- setIsOpen(false);
106
- }
107
- };
108
-
109
- const handleChange = (option: Option) => () => {
110
- setValue(
111
- (selected.find(op => op.value === option.value)
112
- ? selected.filter(op => op.value !== option.value)
113
- : [...selected, option]
114
- ).map(op => op.value),
115
- );
116
- };
117
-
118
- const handleFocus: FocusEventHandler<HTMLInputElement> = event => {
119
- setIsFocused(true);
120
- onFocus?.(event);
121
- };
122
-
123
- const handleBlur: FocusEventHandler<HTMLInputElement> = event => {
124
- setIsFocused(false);
125
- onBlur?.(event);
126
- };
127
-
128
- return (
129
- <FieldSelectBase
130
- {...rest}
131
- ref={ref}
132
- localRef={localRef}
133
- selectionMode={selectionMode}
134
- options={extendedOptions}
135
- selected={selected}
136
- disabled={disabled}
137
- readonly={readonly}
138
- required={required}
139
- searchable={searchable}
140
- onChange={handleChange}
141
- onClear={handleClear}
142
- displayedValue={showDisplayValue ? displayedValue : ''}
143
- valueToCopy={valueToCopy}
144
- inputValue={inputValue}
145
- onInputValueChange={onInputValueChange}
146
- onInputKeyDown={onInputKeyDown}
147
- clearButtonRef={clearButtonRef}
148
- copyButtonRef={copyButtonRef}
149
- onButtonKeyDown={onButtonKeyDown}
150
- open={isOpen}
151
- onOpenChange={handleOpenChange}
152
- locale={locale}
153
- showCopyButton={showCopyButton}
154
- showClearButton={showClearButton}
155
- onFocus={handleFocus}
156
- onBlur={handleBlur}
157
- onContainerPrivateMouseDown={handlePreventListClose}
158
- onDroplistFocusLeave={onDroplistFocusLeave}
159
- firstDroplistItemRefCallback={firstDroplistItemRefCallback}
160
- />
161
- );
162
- },
163
- );
@@ -1,9 +0,0 @@
1
- import { Option } from './types';
2
-
3
- export const EMPTY_OPTION: Option = { value: '', label: '' };
4
- export const DEFAULT_LOCALE = new Intl.Locale('ru-RU');
5
-
6
- export const SELECTION_MODE = {
7
- Single: 'single',
8
- Multi: 'multi',
9
- } as const;
@@ -1,9 +0,0 @@
1
- import { ChevronDownSVG, ChevronUpSVG } from '@snack-uikit/icons';
2
- import { ICON_SIZE, SIZE, Size } from '@snack-uikit/input-private';
3
-
4
- export function getArrowIcon({ size, open }: { size: Size; open: boolean }) {
5
- return {
6
- ArrowIcon: open ? ChevronUpSVG : ChevronDownSVG,
7
- arrowIconSize: size === SIZE.S ? ICON_SIZE.Xs : ICON_SIZE.S,
8
- };
9
- }
@@ -1,25 +0,0 @@
1
- import { SELECTION_MODE } from '../constants';
2
- import { Option } from '../types';
3
-
4
- export const getDisplayedValue = (
5
- props:
6
- | { selectionMode: typeof SELECTION_MODE.Single; selected: Option }
7
- | { selectionMode: typeof SELECTION_MODE.Multi; selected: Option[]; getSelectedItemsText(amount: number): string },
8
- ) => {
9
- if (props.selectionMode === SELECTION_MODE.Single) {
10
- return props.selected.label;
11
- }
12
-
13
- const { selected, getSelectedItemsText } = props;
14
-
15
- const selectedOptions = selected;
16
-
17
- if (selectedOptions.length > 1) {
18
- return getSelectedItemsText(selectedOptions.length);
19
- }
20
-
21
- return selectedOptions.reduce(
22
- (res, cur, index, arr) => `${res}${cur.label}${index === arr.length - 1 ? '' : ','}`,
23
- '',
24
- );
25
- };
@@ -1,2 +0,0 @@
1
- export * from './getArrowIcon';
2
- export * from './getDisplayedValue';
@@ -1,3 +0,0 @@
1
- export * from './useFilteredOptions';
2
- export * from './useList';
3
- export * from './useListNavigation';