@true-engineering/true-react-common-ui-kit 1.2.0 → 1.3.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.
Files changed (241) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +6 -0
  3. package/dist/components/Flag/augment.d.ts +1 -1
  4. package/dist/components/FlexibleTable/types.d.ts +2 -2
  5. package/dist/components/Icon/complexIcons/augment.d.ts +1 -1
  6. package/dist/components/Select/Select.d.ts +1 -1
  7. package/dist/components/Select/SelectList/SelectList.d.ts +1 -1
  8. package/dist/true-react-common-ui-kit.js +236 -181
  9. package/dist/true-react-common-ui-kit.js.map +1 -1
  10. package/dist/true-react-common-ui-kit.umd.cjs +236 -181
  11. package/dist/true-react-common-ui-kit.umd.cjs.map +1 -1
  12. package/dist/vite-env.d.ts +1 -1
  13. package/package.json +91 -90
  14. package/src/components/AccountInfo/AccountInfo.stories.tsx +35 -35
  15. package/src/components/AccountInfo/AccountInfo.styles.ts +55 -55
  16. package/src/components/AccountInfo/AccountInfo.tsx +106 -106
  17. package/src/components/AccountInfo/index.ts +2 -2
  18. package/src/components/AddButton/AddButton.stories.tsx +21 -21
  19. package/src/components/AddButton/AddButton.styles.ts +34 -34
  20. package/src/components/AddButton/AddButton.tsx +49 -49
  21. package/src/components/AddButton/index.ts +2 -2
  22. package/src/components/Button/Button.stories.tsx +61 -61
  23. package/src/components/Button/Button.styles.ts +196 -196
  24. package/src/components/Button/Button.tsx +195 -195
  25. package/src/components/Button/index.ts +2 -2
  26. package/src/components/Checkbox/Checkbox.stories.tsx +35 -35
  27. package/src/components/Checkbox/Checkbox.styles.ts +59 -59
  28. package/src/components/Checkbox/Checkbox.tsx +93 -93
  29. package/src/components/Checkbox/index.ts +2 -2
  30. package/src/components/CloseButton/CloseButton.styles.ts +34 -34
  31. package/src/components/CloseButton/CloseButton.tsx +37 -37
  32. package/src/components/CloseButton/index.ts +2 -2
  33. package/src/components/Colors/Colors.stories.tsx +7 -7
  34. package/src/components/Colors/Colors.styles.ts +38 -38
  35. package/src/components/Colors/Colors.tsx +34 -34
  36. package/src/components/Colors/index.ts +2 -2
  37. package/src/components/CssBaseline/CssBaseline.styles.ts +15 -15
  38. package/src/components/CssBaseline/CssBaseline.tsx +17 -17
  39. package/src/components/CssBaseline/index.ts +2 -2
  40. package/src/components/DateInput/DateInput.stories.tsx +63 -63
  41. package/src/components/DateInput/DateInput.styles.ts +14 -14
  42. package/src/components/DateInput/DateInput.tsx +60 -60
  43. package/src/components/DateInput/index.ts +2 -2
  44. package/src/components/DatePicker/DatePicker.stories.tsx +96 -96
  45. package/src/components/DatePicker/DatePicker.styles.ts +54 -54
  46. package/src/components/DatePicker/DatePicker.tsx +358 -358
  47. package/src/components/DatePicker/DatePickerHeader/DatePickerHeader.styles.ts +84 -84
  48. package/src/components/DatePicker/DatePickerHeader/DatePickerHeader.tsx +94 -94
  49. package/src/components/DatePicker/DatePickerHeader/index.ts +1 -1
  50. package/src/components/DatePicker/DatePickerInput/DatePickerInput.styles.ts +25 -25
  51. package/src/components/DatePicker/DatePickerInput/DatePickerInput.tsx +31 -31
  52. package/src/components/DatePicker/DatePickerInput/index.ts +1 -1
  53. package/src/components/DatePicker/index.ts +4 -4
  54. package/src/components/Description/Description.stories.tsx +29 -29
  55. package/src/components/Description/Description.styles.ts +31 -31
  56. package/src/components/Description/Description.tsx +69 -69
  57. package/src/components/Description/index.ts +2 -2
  58. package/src/components/FiltersPane/FilterInterval/FilterInterval.styles.ts +64 -64
  59. package/src/components/FiltersPane/FilterInterval/FilterInterval.tsx +162 -162
  60. package/src/components/FiltersPane/FilterInterval/index.ts +1 -1
  61. package/src/components/FiltersPane/FilterMultiSelect/FilterMultiSelect.tsx +14 -14
  62. package/src/components/FiltersPane/FilterMultiSelect/index.ts +1 -1
  63. package/src/components/FiltersPane/FilterSelect/FilterSelect.styles.ts +144 -144
  64. package/src/components/FiltersPane/FilterSelect/FilterSelect.tsx +397 -397
  65. package/src/components/FiltersPane/FilterSelect/index.ts +1 -1
  66. package/src/components/FiltersPane/FilterSelect/locales.ts +37 -37
  67. package/src/components/FiltersPane/FilterValueView/FilterValueView.styles.tsx +15 -15
  68. package/src/components/FiltersPane/FilterValueView/FilterValueView.tsx +186 -186
  69. package/src/components/FiltersPane/FilterValueView/index.tsx +1 -1
  70. package/src/components/FiltersPane/FilterWithDates/FilterWithDates.styles.ts +60 -60
  71. package/src/components/FiltersPane/FilterWithDates/FilterWithDates.tsx +222 -222
  72. package/src/components/FiltersPane/FilterWithDates/index.ts +1 -1
  73. package/src/components/FiltersPane/FilterWithPeriod/FilterWithPeriod.styles.ts +17 -17
  74. package/src/components/FiltersPane/FilterWithPeriod/FilterWithPeriod.tsx +231 -231
  75. package/src/components/FiltersPane/FilterWithPeriod/index.ts +1 -1
  76. package/src/components/FiltersPane/FilterWrapper/FilterWrapper.styles.ts +110 -110
  77. package/src/components/FiltersPane/FilterWrapper/FilterWrapper.tsx +360 -360
  78. package/src/components/FiltersPane/FilterWrapper/index.ts +1 -1
  79. package/src/components/FiltersPane/FiltersPane.stories.tsx +307 -307
  80. package/src/components/FiltersPane/FiltersPane.styles.ts +71 -71
  81. package/src/components/FiltersPane/FiltersPane.tsx +193 -193
  82. package/src/components/FiltersPane/FiltersPaneSearch/FiltersPaneSearch.styles.ts +109 -109
  83. package/src/components/FiltersPane/FiltersPaneSearch/FiltersPaneSearch.tsx +175 -175
  84. package/src/components/FiltersPane/FiltersPaneSearch/index.ts +1 -1
  85. package/src/components/FiltersPane/index.ts +20 -20
  86. package/src/components/FiltersPane/locales.ts +107 -107
  87. package/src/components/FiltersPane/types.ts +126 -126
  88. package/src/components/Flag/Flag.stories.tsx +29 -29
  89. package/src/components/Flag/Flag.styles.ts +18 -18
  90. package/src/components/Flag/Flag.tsx +28 -28
  91. package/src/components/Flag/augment.d.ts +1 -1
  92. package/src/components/Flag/index.ts +2 -2
  93. package/src/components/FlexibleTable/FlexibleTable.stories.tsx +80 -80
  94. package/src/components/FlexibleTable/FlexibleTable.styles.ts +131 -131
  95. package/src/components/FlexibleTable/FlexibleTable.tsx +243 -241
  96. package/src/components/FlexibleTable/TableRow.tsx +171 -164
  97. package/src/components/FlexibleTable/TableValue.tsx +83 -86
  98. package/src/components/FlexibleTable/fixture-test.ts +254 -254
  99. package/src/components/FlexibleTable/index.ts +3 -3
  100. package/src/components/FlexibleTable/types.ts +58 -58
  101. package/src/components/Icon/ComplexIconBoilerplate.tsx +17 -17
  102. package/src/components/Icon/Icon.stories.tsx +88 -88
  103. package/src/components/Icon/Icon.styles.ts +10 -10
  104. package/src/components/Icon/Icon.tsx +34 -34
  105. package/src/components/Icon/IconBoilerplate.tsx +42 -42
  106. package/src/components/Icon/complexIcons/augment.d.ts +1 -1
  107. package/src/components/Icon/complexIcons/avatarGreen.svg +57 -57
  108. package/src/components/Icon/complexIcons/icons.ts +7 -7
  109. package/src/components/Icon/complexIcons/index.ts +1 -1
  110. package/src/components/Icon/icons/icons.ts +838 -838
  111. package/src/components/Icon/icons/index.ts +1 -1
  112. package/src/components/Icon/index.ts +4 -4
  113. package/src/components/IncrementInput/ChangeButton.tsx +34 -34
  114. package/src/components/IncrementInput/IncrementInput.stories.tsx +34 -34
  115. package/src/components/IncrementInput/IncrementInput.styles.ts +77 -77
  116. package/src/components/IncrementInput/IncrementInput.tsx +95 -95
  117. package/src/components/IncrementInput/index.ts +2 -2
  118. package/src/components/Input/Input.stories.tsx +92 -92
  119. package/src/components/Input/Input.styles.ts +305 -305
  120. package/src/components/Input/Input.tsx +308 -308
  121. package/src/components/Input/index.ts +2 -2
  122. package/src/components/List/List.stories.tsx +62 -62
  123. package/src/components/List/List.styles.ts +52 -52
  124. package/src/components/List/List.tsx +82 -82
  125. package/src/components/List/index.ts +2 -2
  126. package/src/components/Modal/Modal.stories.tsx +113 -113
  127. package/src/components/Modal/Modal.styles.ts +308 -308
  128. package/src/components/Modal/Modal.tsx +210 -210
  129. package/src/components/Modal/index.ts +2 -2
  130. package/src/components/MoreMenu/MoreMenu.stories.tsx +46 -46
  131. package/src/components/MoreMenu/MoreMenu.styles.ts +70 -70
  132. package/src/components/MoreMenu/MoreMenu.tsx +102 -102
  133. package/src/components/MoreMenu/index.ts +2 -2
  134. package/src/components/MultiSelect/MultiSelect.stories.tsx +46 -46
  135. package/src/components/MultiSelect/MultiSelect.styles.ts +55 -55
  136. package/src/components/MultiSelect/MultiSelect.tsx +98 -98
  137. package/src/components/MultiSelect/MultiSelectInput/MultiSelectInput.styles.ts +73 -73
  138. package/src/components/MultiSelect/MultiSelectInput/MultiSelectInput.tsx +62 -62
  139. package/src/components/MultiSelect/MultiSelectInput/index.ts +1 -1
  140. package/src/components/MultiSelect/index.ts +3 -3
  141. package/src/components/MultiSelectList/MultiSelectList.styles.ts +125 -125
  142. package/src/components/MultiSelectList/MultiSelectList.tsx +519 -519
  143. package/src/components/MultiSelectList/index.ts +2 -2
  144. package/src/components/MultiSelectList/locales.ts +37 -37
  145. package/src/components/Notification/Notification.stories.tsx +51 -51
  146. package/src/components/Notification/Notification.styles.ts +50 -50
  147. package/src/components/Notification/Notification.tsx +84 -84
  148. package/src/components/Notification/index.ts +2 -2
  149. package/src/components/NumberInput/NumberInput.stories.tsx +35 -35
  150. package/src/components/NumberInput/NumberInput.tsx +142 -142
  151. package/src/components/NumberInput/helpers.ts +68 -68
  152. package/src/components/NumberInput/index.ts +1 -1
  153. package/src/components/PhoneInput/PhoneInput.stories.tsx +71 -71
  154. package/src/components/PhoneInput/PhoneInput.styles.ts +84 -84
  155. package/src/components/PhoneInput/PhoneInput.tsx +223 -223
  156. package/src/components/PhoneInput/PhoneInputCountryList/PhoneInputCountryList.stories.tsx +21 -21
  157. package/src/components/PhoneInput/PhoneInputCountryList/PhoneInputCountryList.styles.ts +100 -100
  158. package/src/components/PhoneInput/PhoneInputCountryList/PhoneInputCountryList.tsx +171 -171
  159. package/src/components/PhoneInput/PhoneInputCountryList/index.ts +2 -2
  160. package/src/components/PhoneInput/index.ts +6 -6
  161. package/src/components/PhoneInput/phone-info.ts +2167 -2167
  162. package/src/components/PhoneInput/types.ts +16 -16
  163. package/src/components/RadioButton/RadioButton.stories.tsx +46 -46
  164. package/src/components/RadioButton/RadioButton.styles.ts +37 -37
  165. package/src/components/RadioButton/RadioButton.tsx +56 -56
  166. package/src/components/RadioButton/index.ts +2 -2
  167. package/src/components/ScrollIntoViewIfNeeded/ScrollIntoViewIfNeeded.ts +66 -66
  168. package/src/components/ScrollIntoViewIfNeeded/index.ts +1 -1
  169. package/src/components/SearchInput/SearchInput.stories.tsx +24 -24
  170. package/src/components/SearchInput/SearchInput.styles.ts +50 -50
  171. package/src/components/SearchInput/SearchInput.tsx +63 -63
  172. package/src/components/SearchInput/index.ts +2 -2
  173. package/src/components/Select/Select.stories.tsx +242 -234
  174. package/src/components/Select/Select.styles.ts +74 -74
  175. package/src/components/Select/Select.tsx +452 -450
  176. package/src/components/Select/SelectList/SelectList.styles.ts +57 -57
  177. package/src/components/Select/SelectList/SelectList.tsx +130 -130
  178. package/src/components/Select/SelectList/index.ts +1 -1
  179. package/src/components/Select/helpers.ts +21 -21
  180. package/src/components/Select/index.ts +3 -3
  181. package/src/components/SmartInput/SmartInput.stories.tsx +63 -63
  182. package/src/components/SmartInput/SmartInput.tsx +180 -180
  183. package/src/components/SmartInput/helpers.ts +85 -85
  184. package/src/components/SmartInput/index.ts +1 -1
  185. package/src/components/Switch/Switch.stories.tsx +40 -40
  186. package/src/components/Switch/Switch.styles.ts +75 -75
  187. package/src/components/Switch/Switch.tsx +89 -89
  188. package/src/components/Switch/index.ts +2 -2
  189. package/src/components/TextArea/TextArea.stories.tsx +35 -35
  190. package/src/components/TextArea/TextArea.styles.ts +153 -153
  191. package/src/components/TextArea/TextArea.tsx +178 -178
  192. package/src/components/TextArea/index.ts +2 -2
  193. package/src/components/TextWithInfo/TextWithInfo.stories.tsx +53 -53
  194. package/src/components/TextWithInfo/TextWithInfo.styles.ts +60 -60
  195. package/src/components/TextWithInfo/TextWithInfo.tsx +67 -67
  196. package/src/components/TextWithInfo/index.ts +2 -2
  197. package/src/components/TextWithTooltip/TextWithTooltip.stories.tsx +58 -58
  198. package/src/components/TextWithTooltip/TextWithTooltip.styles.ts +19 -19
  199. package/src/components/TextWithTooltip/TextWithTooltip.tsx +163 -163
  200. package/src/components/TextWithTooltip/index.ts +2 -2
  201. package/src/components/ThemedPreloader/ThemedPreloader.stories.tsx +41 -41
  202. package/src/components/ThemedPreloader/ThemedPreloader.styles.ts +21 -21
  203. package/src/components/ThemedPreloader/ThemedPreloader.tsx +56 -56
  204. package/src/components/ThemedPreloader/components/DefaultPreloader/DefaultPreloader.tsx +34 -34
  205. package/src/components/ThemedPreloader/components/DefaultPreloader/index.ts +1 -1
  206. package/src/components/ThemedPreloader/components/DotsPreloader/DotsPreloader.styles.ts +54 -54
  207. package/src/components/ThemedPreloader/components/DotsPreloader/DotsPreloader.tsx +18 -18
  208. package/src/components/ThemedPreloader/components/DotsPreloader/index.ts +2 -2
  209. package/src/components/ThemedPreloader/components/SvgPreloader/SvgPreloader.styles.ts +11 -11
  210. package/src/components/ThemedPreloader/components/SvgPreloader/SvgPreloader.tsx +32 -32
  211. package/src/components/ThemedPreloader/components/SvgPreloader/index.ts +2 -2
  212. package/src/components/ThemedPreloader/components/index.ts +2 -2
  213. package/src/components/ThemedPreloader/index.ts +2 -2
  214. package/src/components/Toaster/Toaster.stories.tsx +34 -34
  215. package/src/components/Toaster/Toaster.styles.ts +59 -59
  216. package/src/components/Toaster/Toaster.tsx +113 -113
  217. package/src/components/Toaster/index.ts +2 -2
  218. package/src/components/Tooltip/Tooltip.stories.tsx +21 -21
  219. package/src/components/Tooltip/Tooltip.styles.ts +45 -45
  220. package/src/components/Tooltip/Tooltip.tsx +40 -40
  221. package/src/components/Tooltip/index.ts +3 -3
  222. package/src/components/Tooltip/types.ts +1 -1
  223. package/src/components/index.ts +36 -36
  224. package/src/helpers/colors.ts +2 -2
  225. package/src/helpers/data-attributes.ts +18 -18
  226. package/src/helpers/dateHelpers/date-helpers.ts +9 -9
  227. package/src/helpers/index.ts +5 -5
  228. package/src/helpers/phone.ts +106 -106
  229. package/src/helpers/popper-helpers.ts +17 -17
  230. package/src/helpers/utils.ts +164 -164
  231. package/src/hooks/index.ts +6 -6
  232. package/src/hooks/use-did-mount-effect.ts +21 -21
  233. package/src/hooks/use-dropdown.ts +85 -85
  234. package/src/hooks/use-is-mounted.ts +15 -15
  235. package/src/hooks/use-on-click-outside.ts +92 -92
  236. package/src/hooks/use-theme.ts +36 -36
  237. package/src/hooks/use-tweak-styles.ts +14 -14
  238. package/src/index.ts +5 -5
  239. package/src/theme.ts +155 -155
  240. package/src/types.ts +106 -106
  241. package/src/vite-env.d.ts +1 -1
@@ -1,450 +1,452 @@
1
- import {
2
- ReactNode,
3
- FocusEvent,
4
- KeyboardEvent,
5
- MouseEvent,
6
- useCallback,
7
- useEffect,
8
- useMemo,
9
- useRef,
10
- useState,
11
- } from 'react';
12
- import { Styles } from 'jss';
13
- import clsx from 'clsx';
14
- import { merge } from 'lodash';
15
- import { debounce } from 'ts-debounce';
16
- import { Portal } from 'react-overlays';
17
- import { SelectList } from './SelectList';
18
- import { IInputProps, Input } from '../Input';
19
- import { IIconType, Icon } from '../Icon';
20
- import {
21
- useIsMounted,
22
- useTheme,
23
- useOnClickOutsideWithRef,
24
- useDropdown,
25
- } from '../../hooks';
26
- import { IDropdownWithPopperOptions } from '../../types';
27
- import { isNotEmpty } from '../../helpers';
28
- import {
29
- defaultConvertFunction,
30
- defaultCompareFunction,
31
- getActiveValueIndex,
32
- defaultIsOptionDisabled,
33
- } from './helpers';
34
- import { SelectStyles, styles } from './Select.styles';
35
-
36
- export interface ISelectProps<Value>
37
- extends Omit<IInputProps, 'value' | 'onChange' | 'type'> {
38
- tweakStyles?: SelectStyles;
39
- defaultOptionLabel?: string;
40
- noMatchesLabel?: string;
41
- loadingLabel?: ReactNode;
42
- optionsMode?: 'search' | 'dynamic' | 'normal';
43
- debounceTime?: number;
44
- minSymbolsCountToOpenList?: number;
45
- dropdownOptions?: IDropdownWithPopperOptions;
46
- dropdownIcon?: IIconType;
47
- options: Value[];
48
- value: Value | undefined;
49
- shouldScrollToList?: boolean;
50
- isOptionDisabled?(option: Value): boolean;
51
- onChange(value: Value | undefined): void; // подумать как возвращать индекс
52
- onType?(value: string): Promise<void>;
53
- optionsFilter?(options: Value[], query: string): Value[];
54
- onOpen?(): void;
55
- compareValuesOnChange?(v1: Value | undefined, v2: Value | undefined): boolean;
56
- // Для избежания проблем юзайте useCallback на эти функции
57
- // или выносите их из компонента (чтобы не было сайдэфектов от перерендеринга их)
58
- convertValueToString?(value: Value): string | undefined;
59
- convertValueToReactNode?(value: Value): ReactNode;
60
- convertValueToId?(value: Value): string | undefined;
61
- }
62
-
63
- export function Select<Value>({
64
- options,
65
- value,
66
- defaultOptionLabel,
67
- debounceTime = 400,
68
- optionsMode = 'normal',
69
- noMatchesLabel,
70
- loadingLabel,
71
- tweakStyles,
72
- testId,
73
- isDisabled,
74
- dropdownOptions,
75
- minSymbolsCountToOpenList = 0,
76
- dropdownIcon = 'chevron-down',
77
- shouldScrollToList = true,
78
- onChange,
79
- onFocus,
80
- onBlur,
81
- onType,
82
- onOpen,
83
- isOptionDisabled = defaultIsOptionDisabled,
84
- compareValuesOnChange = defaultCompareFunction,
85
- convertValueToString = defaultConvertFunction,
86
- convertValueToId,
87
- convertValueToReactNode,
88
- optionsFilter,
89
- ...inputProps
90
- }: ISelectProps<Value>): JSX.Element {
91
- const { classes, componentStyles } = useTheme('Select', styles, tweakStyles);
92
- const isMounted = useIsMounted();
93
- const [isListOpen, setIsListOpen] = useState(false);
94
- const [areOptionsLoading, setAreOptionsLoading] = useState(false);
95
- const hasDefaultOption = defaultOptionLabel !== undefined;
96
-
97
- const [focusedListCellIndex, setFocusedListCellIndex] = useState(-1);
98
- const [searchValue, setSearchValue] = useState('');
99
-
100
- // если мы ввели что то в строку поиска - то этот булеан будет отключаться
101
- // вынесен отдельно, из-за проблем с дебаунсом при динамич. опциях
102
- const [shouldShowDefaultOption, setShouldShowDefaultOption] = useState(true);
103
-
104
- const inputWrapper = useRef<HTMLDivElement>(null);
105
- const list = useRef<HTMLDivElement>(null);
106
- const input = useRef<HTMLInputElement>(null); // TODO ref снаружи?
107
-
108
- const stringValue = isNotEmpty(value)
109
- ? convertValueToString(value)
110
- : undefined;
111
-
112
- const filteredOptions = useMemo(() => {
113
- if (optionsMode !== 'search') {
114
- return options;
115
- }
116
- if (optionsFilter) {
117
- return optionsFilter(options, searchValue);
118
- }
119
- const lowerCaseValue = searchValue?.toLowerCase();
120
- return options.filter((option) => {
121
- const convertedOption = convertValueToString(option);
122
- return (
123
- convertedOption !== undefined &&
124
- convertedOption.toLowerCase().includes(lowerCaseValue)
125
- );
126
- });
127
- }, [optionsMode, optionsFilter, options, convertValueToString, searchValue]);
128
-
129
- useEffect(() => {
130
- setFocusedListCellIndex(
131
- getActiveValueIndex(filteredOptions, value, convertValueToString),
132
- );
133
- }, [filteredOptions, value, convertValueToString]);
134
-
135
- const handleListClose = () => {
136
- setIsListOpen(false);
137
- setSearchValue('');
138
- setShouldShowDefaultOption(true);
139
- };
140
-
141
- const handleListOpen = async () => {
142
- if (isListOpen) {
143
- return;
144
- }
145
- setIsListOpen(true);
146
- };
147
-
148
- const handleFocus = (event: FocusEvent<HTMLInputElement>) => {
149
- if (onFocus !== undefined) {
150
- onFocus(event);
151
- }
152
- handleListOpen();
153
- };
154
-
155
- const handleOnClick = () => {
156
- handleListOpen();
157
- };
158
-
159
- const handleBlur = (event: FocusEvent<HTMLInputElement>) => {
160
- if (onBlur !== undefined) {
161
- onBlur(event);
162
- }
163
- handleListClose();
164
- };
165
-
166
- const handleOnChange = useCallback(
167
- (newValue: Value | undefined) => {
168
- const areValuesEqual = compareValuesOnChange(value, newValue);
169
- if (areValuesEqual) {
170
- return;
171
- }
172
- onChange(newValue);
173
- },
174
- [value, onChange],
175
- );
176
-
177
- const handleOptionClick = useCallback(
178
- (index: number) => {
179
- handleOnChange(index === -1 ? undefined : filteredOptions[index]);
180
- handleListClose();
181
- input.current?.blur();
182
- },
183
- [handleOnChange, filteredOptions],
184
- );
185
-
186
- const handleOnType = useCallback(
187
- async (v: string) => {
188
- if (onType === undefined) {
189
- return;
190
- }
191
- if (isMounted()) {
192
- setAreOptionsLoading(true);
193
- }
194
- await onType(v);
195
- if (isMounted()) {
196
- setAreOptionsLoading(false);
197
- }
198
- if (optionsMode === 'dynamic') {
199
- setShouldShowDefaultOption(v === '');
200
- }
201
- },
202
- [onType, optionsMode],
203
- );
204
-
205
- const debounceHandleOnType = useCallback(
206
- debounce(handleOnType, debounceTime),
207
- [handleOnType, debounceTime],
208
- );
209
-
210
- const handleInputChange = (v: string) => {
211
- if (onType !== undefined) {
212
- debounceHandleOnType(v);
213
- }
214
-
215
- if (optionsMode !== 'dynamic') {
216
- setShouldShowDefaultOption(v === '');
217
- }
218
-
219
- if (v === '') {
220
- handleOnChange(undefined);
221
- }
222
-
223
- setSearchValue(v);
224
- };
225
-
226
- const handleKeyDown = (event: KeyboardEvent) => {
227
- if (!isListOpen) {
228
- return;
229
- }
230
-
231
- event.stopPropagation();
232
-
233
- switch (event.code) {
234
- case 'Enter':
235
- case 'NumpadEnter': {
236
- let indexToClick = focusedListCellIndex;
237
-
238
- // если осталось одна опция в списке,
239
- // то выбираем ее нажатием на enter
240
- if (indexToClick === -1 && filteredOptions.length === 1) {
241
- indexToClick = 0;
242
- }
243
-
244
- handleOptionClick(indexToClick);
245
- break;
246
- }
247
-
248
- case 'ArrowDown': {
249
- // чтобы убрать перемещение курсора в инпуте
250
- event.preventDefault();
251
- let newIndex: number = focusedListCellIndex ?? -1;
252
- // ищем первый незадизейбленный вариант, либо дефолтную опцию
253
- for (let i = newIndex; i < newIndex + filteredOptions.length; i++) {
254
- const targetIndex = (i + 1) % filteredOptions.length;
255
- if (
256
- shouldShowDefaultOption &&
257
- hasDefaultOption &&
258
- newIndex > -1 &&
259
- targetIndex === 0
260
- ) {
261
- newIndex = -1;
262
- break;
263
- }
264
- if (!isOptionDisabled(filteredOptions[targetIndex])) {
265
- newIndex = targetIndex;
266
- break;
267
- }
268
- }
269
- setFocusedListCellIndex(newIndex);
270
- break;
271
- }
272
-
273
- case 'ArrowUp': {
274
- // чтобы убрать перемещение курсора в инпуте
275
- event.preventDefault();
276
- const newIndex: number =
277
- (focusedListCellIndex ?? -1) === -1 ? 0 : focusedListCellIndex;
278
- // ищем первый незадизейбленный вариант, либо дефолтную опцию
279
- for (let i = newIndex; i > newIndex - filteredOptions.length; i--) {
280
- const targetIndex = (i > 0 ? i : filteredOptions.length + i) - 1;
281
- if (
282
- shouldShowDefaultOption &&
283
- hasDefaultOption &&
284
- focusedListCellIndex !== -1 &&
285
- targetIndex === filteredOptions.length - 1
286
- ) {
287
- // не выносить сет наружу (приведет к багу)
288
- setFocusedListCellIndex(-1);
289
- break;
290
- }
291
- if (!isOptionDisabled(filteredOptions[targetIndex])) {
292
- // не выносить сет наружу (приведет к багу)
293
- setFocusedListCellIndex(targetIndex);
294
- break;
295
- }
296
- }
297
- break;
298
- }
299
- }
300
- };
301
-
302
- const onArrowClick = () => {
303
- if (isListOpen) {
304
- input.current?.blur();
305
- } else {
306
- input.current?.focus();
307
- }
308
- };
309
-
310
- useOnClickOutsideWithRef(
311
- list,
312
- () => {
313
- handleListClose();
314
- },
315
- inputWrapper,
316
- );
317
-
318
- const isOpen =
319
- isListOpen &&
320
- (inputProps.isLoading ||
321
- filteredOptions.length > 0 ||
322
- (defaultOptionLabel !== undefined && searchValue === '') ||
323
- noMatchesLabel !== undefined) &&
324
- searchValue.trim().length >= minSymbolsCountToOpenList;
325
-
326
- const { isReadonly = true } = inputProps;
327
- const shouldUsePointerCursor = optionsMode === 'normal' && isReadonly;
328
-
329
- const tweakInputStyles = useMemo(
330
- () =>
331
- merge(
332
- {},
333
- componentStyles.tweakInput,
334
- {
335
- ...(shouldUsePointerCursor
336
- ? { input: { cursor: 'pointer' } }
337
- : undefined),
338
- },
339
- tweakStyles?.tweakInput,
340
- ) as Styles,
341
- [tweakStyles?.tweakInput, shouldUsePointerCursor],
342
- );
343
-
344
- // Эти значения ставятся в false по дефолту также в useDropdown
345
- const {
346
- shouldUsePopper = false,
347
- shouldRenderInBody = false,
348
- shouldHideOnScroll = false,
349
- } = dropdownOptions ?? {};
350
-
351
- const popperData = useDropdown({
352
- isOpen,
353
- onDropdownClose: handleListClose,
354
- referenceElement: inputWrapper.current,
355
- dropdownElement: list.current,
356
- options: dropdownOptions,
357
- dependenciesForPositionUpdating: [
358
- inputProps.isLoading,
359
- filteredOptions.length,
360
- ],
361
- });
362
-
363
- useEffect(() => {
364
- if (isOpen && onOpen !== undefined) {
365
- onOpen();
366
- }
367
- }, [isOpen, onOpen]);
368
-
369
- return (
370
- <div className={classes.root} onKeyDown={handleKeyDown}>
371
- <div
372
- className={clsx(classes.inputWrapper, isDisabled && classes.disabled)}
373
- onClick={isDisabled ? undefined : handleOnClick}
374
- ref={inputWrapper}
375
- >
376
- <Input
377
- value={searchValue !== '' ? searchValue : stringValue}
378
- onChange={handleInputChange}
379
- isActive={isListOpen}
380
- isReadonly={optionsMode === 'normal'}
381
- onFocus={handleFocus}
382
- onBlur={handleBlur}
383
- isDisabled={isDisabled}
384
- ref={input}
385
- isLoading={areOptionsLoading}
386
- tweakStyles={tweakInputStyles}
387
- testId={testId}
388
- {...inputProps}
389
- />
390
- <div
391
- onMouseDown={(event: MouseEvent) => {
392
- event.preventDefault();
393
- }}
394
- onClick={onArrowClick}
395
- className={clsx(classes.arrow, isListOpen && classes.activeArrow)}
396
- >
397
- <Icon type={dropdownIcon} />
398
- </div>
399
- </div>
400
- <Portal
401
- container={shouldRenderInBody ? document.body : inputWrapper.current}
402
- >
403
- <>
404
- {(shouldUsePopper || isOpen) && (
405
- <div
406
- className={clsx(classes.listWrapper, {
407
- [classes.withoutPopper]: !shouldUsePopper,
408
- [classes.listWrapperInBody]: shouldRenderInBody,
409
- })}
410
- ref={list}
411
- // чтобы предотвратить onBlur на инпуте
412
- onMouseDown={(event) => event.preventDefault()}
413
- style={popperData?.styles.popper as Styles}
414
- {...popperData?.attributes.popper}
415
- >
416
- {isOpen && (
417
- <SelectList
418
- options={filteredOptions}
419
- defaultOptionLabel={
420
- hasDefaultOption && shouldShowDefaultOption
421
- ? defaultOptionLabel
422
- : undefined
423
- }
424
- noMatchesLabel={noMatchesLabel}
425
- focusedIndex={focusedListCellIndex}
426
- activeValue={value}
427
- isLoading={inputProps.isLoading}
428
- loadingLabel={loadingLabel}
429
- tweakStyles={tweakStyles?.tweakSelectList as Styles}
430
- testId={testId !== undefined ? `${testId}-list` : undefined}
431
- // скролл не работает с включеным поппером
432
- shouldScrollToList={
433
- shouldScrollToList &&
434
- !shouldUsePopper &&
435
- !shouldHideOnScroll
436
- }
437
- isOptionDisabled={isOptionDisabled}
438
- convertValueToString={convertValueToString}
439
- convertValueToReactNode={convertValueToReactNode}
440
- convertValueToId={convertValueToId}
441
- onOptionClick={handleOptionClick}
442
- />
443
- )}
444
- </div>
445
- )}
446
- </>
447
- </Portal>
448
- </div>
449
- );
450
- }
1
+ import {
2
+ ReactNode,
3
+ FocusEvent,
4
+ KeyboardEvent,
5
+ MouseEvent,
6
+ useCallback,
7
+ useEffect,
8
+ useMemo,
9
+ useRef,
10
+ useState,
11
+ } from 'react';
12
+ import { Styles } from 'jss';
13
+ import clsx from 'clsx';
14
+ import { merge } from 'lodash';
15
+ import { debounce } from 'ts-debounce';
16
+ import { Portal } from 'react-overlays';
17
+ import { SelectList } from './SelectList';
18
+ import { IInputProps, Input } from '../Input';
19
+ import { IIconType, Icon } from '../Icon';
20
+ import {
21
+ useIsMounted,
22
+ useTheme,
23
+ useOnClickOutsideWithRef,
24
+ useDropdown,
25
+ } from '../../hooks';
26
+ import { IDropdownWithPopperOptions } from '../../types';
27
+ import { isNotEmpty } from '../../helpers';
28
+ import {
29
+ defaultConvertFunction,
30
+ defaultCompareFunction,
31
+ getActiveValueIndex,
32
+ defaultIsOptionDisabled,
33
+ } from './helpers';
34
+ import { SelectStyles, styles } from './Select.styles';
35
+
36
+ export interface ISelectProps<Value>
37
+ extends Omit<IInputProps, 'value' | 'onChange' | 'type'> {
38
+ tweakStyles?: SelectStyles;
39
+ defaultOptionLabel?: string;
40
+ noMatchesLabel?: string;
41
+ loadingLabel?: ReactNode;
42
+ optionsMode?: 'search' | 'dynamic' | 'normal';
43
+ debounceTime?: number;
44
+ minSymbolsCountToOpenList?: number;
45
+ dropdownOptions?: IDropdownWithPopperOptions;
46
+ dropdownIcon?: IIconType;
47
+ options: Value[];
48
+ value: Value | undefined;
49
+ shouldScrollToList?: boolean;
50
+ isOptionDisabled?(option: Value): boolean;
51
+ onChange(value: Value | undefined): void; // подумать как возвращать индекс
52
+ onType?(value: string): Promise<void>;
53
+ optionsFilter?(options: Value[], query: string): Value[];
54
+ onOpen?(): void;
55
+ compareValuesOnChange?(v1: Value | undefined, v2: Value | undefined): boolean;
56
+ // Для избежания проблем юзайте useCallback на эти функции
57
+ // или выносите их из компонента (чтобы не было сайдэфектов от перерендеринга их)
58
+ convertValueToString?(value: Value): string | undefined;
59
+ convertValueToReactNode?(value: Value, isDisabled: boolean): ReactNode;
60
+ convertValueToId?(value: Value): string | undefined;
61
+ }
62
+
63
+ export function Select<Value>({
64
+ options,
65
+ value,
66
+ defaultOptionLabel,
67
+ debounceTime = 400,
68
+ optionsMode = 'normal',
69
+ noMatchesLabel,
70
+ loadingLabel,
71
+ tweakStyles,
72
+ testId,
73
+ isDisabled,
74
+ dropdownOptions,
75
+ minSymbolsCountToOpenList = 0,
76
+ dropdownIcon = 'chevron-down',
77
+ shouldScrollToList = true,
78
+ onChange,
79
+ onFocus,
80
+ onBlur,
81
+ onType,
82
+ onOpen,
83
+ isOptionDisabled = defaultIsOptionDisabled,
84
+ compareValuesOnChange = defaultCompareFunction,
85
+ convertValueToString = defaultConvertFunction,
86
+ convertValueToId,
87
+ convertValueToReactNode,
88
+ optionsFilter,
89
+ ...inputProps
90
+ }: ISelectProps<Value>): JSX.Element {
91
+ const { classes, componentStyles } = useTheme('Select', styles, tweakStyles);
92
+ const isMounted = useIsMounted();
93
+ const [isListOpen, setIsListOpen] = useState(false);
94
+ const [areOptionsLoading, setAreOptionsLoading] = useState(false);
95
+ const hasDefaultOption = defaultOptionLabel !== undefined;
96
+
97
+ const [focusedListCellIndex, setFocusedListCellIndex] = useState(-1);
98
+ const [searchValue, setSearchValue] = useState('');
99
+
100
+ // если мы ввели что то в строку поиска - то этот булеан будет отключаться
101
+ // вынесен отдельно, из-за проблем с дебаунсом при динамич. опциях
102
+ const [shouldShowDefaultOption, setShouldShowDefaultOption] = useState(true);
103
+
104
+ const inputWrapper = useRef<HTMLDivElement>(null);
105
+ const list = useRef<HTMLDivElement>(null);
106
+ const input = useRef<HTMLInputElement>(null); // TODO ref снаружи?
107
+
108
+ const stringValue = isNotEmpty(value)
109
+ ? convertValueToString(value)
110
+ : undefined;
111
+
112
+ const filteredOptions = useMemo(() => {
113
+ if (optionsMode !== 'search') {
114
+ return options;
115
+ }
116
+ if (optionsFilter) {
117
+ return optionsFilter(options, searchValue);
118
+ }
119
+ const lowerCaseValue = searchValue?.toLowerCase();
120
+ return options.filter((option) => {
121
+ const convertedOption = convertValueToString(option);
122
+ return (
123
+ convertedOption !== undefined &&
124
+ convertedOption.toLowerCase().includes(lowerCaseValue)
125
+ );
126
+ });
127
+ }, [optionsMode, optionsFilter, options, convertValueToString, searchValue]);
128
+
129
+ useEffect(() => {
130
+ setFocusedListCellIndex(
131
+ getActiveValueIndex(filteredOptions, value, convertValueToString),
132
+ );
133
+ }, [filteredOptions, value, convertValueToString]);
134
+
135
+ const handleListClose = () => {
136
+ setIsListOpen(false);
137
+ setSearchValue('');
138
+ setShouldShowDefaultOption(true);
139
+ };
140
+
141
+ const handleListOpen = async () => {
142
+ if (isListOpen) {
143
+ return;
144
+ }
145
+ setIsListOpen(true);
146
+ };
147
+
148
+ const handleFocus = (event: FocusEvent<HTMLInputElement>) => {
149
+ if (onFocus !== undefined) {
150
+ onFocus(event);
151
+ }
152
+ handleListOpen();
153
+ };
154
+
155
+ const handleOnClick = () => {
156
+ handleListOpen();
157
+ };
158
+
159
+ const handleBlur = (event: FocusEvent<HTMLInputElement>) => {
160
+ if (onBlur !== undefined) {
161
+ onBlur(event);
162
+ }
163
+ handleListClose();
164
+ };
165
+
166
+ const handleOnChange = useCallback(
167
+ (newValue: Value | undefined) => {
168
+ const areValuesEqual = compareValuesOnChange(value, newValue);
169
+ if (areValuesEqual) {
170
+ return;
171
+ }
172
+ onChange(newValue);
173
+ },
174
+ [value, onChange],
175
+ );
176
+
177
+ const handleOptionClick = useCallback(
178
+ (index: number) => {
179
+ handleOnChange(index === -1 ? undefined : filteredOptions[index]);
180
+ handleListClose();
181
+ input.current?.blur();
182
+ },
183
+ [handleOnChange, filteredOptions],
184
+ );
185
+
186
+ const handleOnType = useCallback(
187
+ async (v: string) => {
188
+ if (onType === undefined) {
189
+ return;
190
+ }
191
+ if (isMounted()) {
192
+ setAreOptionsLoading(true);
193
+ }
194
+ await onType(v);
195
+ if (isMounted()) {
196
+ setAreOptionsLoading(false);
197
+ }
198
+ if (optionsMode === 'dynamic') {
199
+ setShouldShowDefaultOption(v === '');
200
+ }
201
+ },
202
+ [onType, optionsMode],
203
+ );
204
+
205
+ const debounceHandleOnType = useCallback(
206
+ debounce(handleOnType, debounceTime),
207
+ [handleOnType, debounceTime],
208
+ );
209
+
210
+ const handleInputChange = (v: string) => {
211
+ if (onType !== undefined) {
212
+ debounceHandleOnType(v);
213
+ }
214
+
215
+ if (optionsMode !== 'dynamic') {
216
+ setShouldShowDefaultOption(v === '');
217
+ }
218
+
219
+ if (v === '') {
220
+ handleOnChange(undefined);
221
+ }
222
+
223
+ setSearchValue(v);
224
+ };
225
+
226
+ const handleKeyDown = (event: KeyboardEvent) => {
227
+ if (!isListOpen) {
228
+ return;
229
+ }
230
+
231
+ event.stopPropagation();
232
+
233
+ switch (event.code) {
234
+ case 'Enter':
235
+ case 'NumpadEnter': {
236
+ let indexToClick = focusedListCellIndex;
237
+
238
+ // если осталось одна опция в списке,
239
+ // то выбираем ее нажатием на enter
240
+ if (indexToClick === -1 && filteredOptions.length === 1) {
241
+ indexToClick = 0;
242
+ }
243
+
244
+ handleOptionClick(indexToClick);
245
+ break;
246
+ }
247
+
248
+ case 'ArrowDown': {
249
+ // чтобы убрать перемещение курсора в инпуте
250
+ event.preventDefault();
251
+ let newIndex: number = focusedListCellIndex ?? -1;
252
+ // ищем первый незадизейбленный вариант, либо дефолтную опцию
253
+ for (let i = newIndex; i < newIndex + filteredOptions.length; i++) {
254
+ const targetIndex = (i + 1) % filteredOptions.length;
255
+ if (
256
+ shouldShowDefaultOption &&
257
+ hasDefaultOption &&
258
+ newIndex > -1 &&
259
+ targetIndex === 0
260
+ ) {
261
+ newIndex = -1;
262
+ break;
263
+ }
264
+ if (!isOptionDisabled(filteredOptions[targetIndex])) {
265
+ newIndex = targetIndex;
266
+ break;
267
+ }
268
+ }
269
+ setFocusedListCellIndex(newIndex);
270
+ break;
271
+ }
272
+
273
+ case 'ArrowUp': {
274
+ // чтобы убрать перемещение курсора в инпуте
275
+ event.preventDefault();
276
+ const newIndex: number =
277
+ (focusedListCellIndex ?? -1) === -1 ? 0 : focusedListCellIndex;
278
+ // ищем первый незадизейбленный вариант, либо дефолтную опцию
279
+ for (let i = newIndex; i > newIndex - filteredOptions.length; i--) {
280
+ const targetIndex = (i > 0 ? i : filteredOptions.length + i) - 1;
281
+ if (
282
+ shouldShowDefaultOption &&
283
+ hasDefaultOption &&
284
+ focusedListCellIndex !== -1 &&
285
+ targetIndex === filteredOptions.length - 1
286
+ ) {
287
+ // не выносить сет наружу (приведет к багу)
288
+ setFocusedListCellIndex(-1);
289
+ break;
290
+ }
291
+ if (!isOptionDisabled(filteredOptions[targetIndex])) {
292
+ // не выносить сет наружу (приведет к багу)
293
+ setFocusedListCellIndex(targetIndex);
294
+ break;
295
+ }
296
+ }
297
+ break;
298
+ }
299
+ }
300
+ };
301
+
302
+ const onArrowClick = () => {
303
+ if (isListOpen) {
304
+ input.current?.blur();
305
+ } else {
306
+ input.current?.focus();
307
+ }
308
+ };
309
+
310
+ useOnClickOutsideWithRef(
311
+ list,
312
+ () => {
313
+ handleListClose();
314
+ },
315
+ inputWrapper,
316
+ );
317
+
318
+ const isOpen =
319
+ isListOpen &&
320
+ (inputProps.isLoading ||
321
+ filteredOptions.length > 0 ||
322
+ (defaultOptionLabel !== undefined && searchValue === '') ||
323
+ noMatchesLabel !== undefined) &&
324
+ searchValue.trim().length >= minSymbolsCountToOpenList;
325
+
326
+ const { isReadonly = true } = inputProps;
327
+ const shouldUsePointerCursor = optionsMode === 'normal' && isReadonly;
328
+
329
+ const tweakInputStyles = useMemo(
330
+ () =>
331
+ merge(
332
+ {},
333
+ componentStyles.tweakInput,
334
+ {
335
+ ...(shouldUsePointerCursor
336
+ ? { input: { cursor: 'pointer' } }
337
+ : undefined),
338
+ },
339
+ tweakStyles?.tweakInput,
340
+ ) as Styles,
341
+ [tweakStyles?.tweakInput, shouldUsePointerCursor],
342
+ );
343
+
344
+ // Эти значения ставятся в false по дефолту также в useDropdown
345
+ const {
346
+ shouldUsePopper = false,
347
+ shouldRenderInBody = false,
348
+ shouldHideOnScroll = false,
349
+ } = dropdownOptions ?? {};
350
+
351
+ const popperData = useDropdown({
352
+ isOpen,
353
+ onDropdownClose: handleListClose,
354
+ referenceElement: inputWrapper.current,
355
+ dropdownElement: list.current,
356
+ options: dropdownOptions,
357
+ dependenciesForPositionUpdating: [
358
+ inputProps.isLoading,
359
+ filteredOptions.length,
360
+ ],
361
+ });
362
+
363
+ useEffect(() => {
364
+ if (isOpen && onOpen !== undefined) {
365
+ onOpen();
366
+ }
367
+ }, [isOpen, onOpen]);
368
+
369
+ const listEl = (
370
+ <div
371
+ className={clsx(classes.listWrapper, {
372
+ [classes.withoutPopper]: !shouldUsePopper,
373
+ [classes.listWrapperInBody]: shouldRenderInBody,
374
+ })}
375
+ ref={list}
376
+ // чтобы предотвратить onBlur на инпуте
377
+ onMouseDown={(event) => event.preventDefault()}
378
+ style={popperData?.styles.popper as Styles}
379
+ {...popperData?.attributes.popper}
380
+ >
381
+ {isOpen && (
382
+ <SelectList
383
+ options={filteredOptions}
384
+ defaultOptionLabel={
385
+ hasDefaultOption && shouldShowDefaultOption
386
+ ? defaultOptionLabel
387
+ : undefined
388
+ }
389
+ noMatchesLabel={noMatchesLabel}
390
+ focusedIndex={focusedListCellIndex}
391
+ activeValue={value}
392
+ isLoading={inputProps.isLoading}
393
+ loadingLabel={loadingLabel}
394
+ tweakStyles={tweakStyles?.tweakSelectList as Styles}
395
+ testId={testId !== undefined ? `${testId}-list` : undefined}
396
+ // скролл не работает с включеным поппером
397
+ shouldScrollToList={
398
+ shouldScrollToList && !shouldUsePopper && !shouldHideOnScroll
399
+ }
400
+ isOptionDisabled={isOptionDisabled}
401
+ convertValueToString={convertValueToString}
402
+ convertValueToReactNode={convertValueToReactNode}
403
+ convertValueToId={convertValueToId}
404
+ onOptionClick={handleOptionClick}
405
+ />
406
+ )}
407
+ </div>
408
+ );
409
+
410
+ return (
411
+ <div className={classes.root} onKeyDown={handleKeyDown}>
412
+ <div
413
+ className={clsx(classes.inputWrapper, isDisabled && classes.disabled)}
414
+ onClick={isDisabled ? undefined : handleOnClick}
415
+ ref={inputWrapper}
416
+ >
417
+ <Input
418
+ value={searchValue !== '' ? searchValue : stringValue}
419
+ onChange={handleInputChange}
420
+ isActive={isListOpen}
421
+ isReadonly={optionsMode === 'normal'}
422
+ onFocus={handleFocus}
423
+ onBlur={handleBlur}
424
+ isDisabled={isDisabled}
425
+ ref={input}
426
+ isLoading={areOptionsLoading}
427
+ tweakStyles={tweakInputStyles}
428
+ testId={testId}
429
+ {...inputProps}
430
+ />
431
+ <div
432
+ onMouseDown={(event: MouseEvent) => {
433
+ event.preventDefault();
434
+ }}
435
+ onClick={onArrowClick}
436
+ className={clsx(classes.arrow, isListOpen && classes.activeArrow)}
437
+ >
438
+ <Icon type={dropdownIcon} />
439
+ </div>
440
+ </div>
441
+ {shouldUsePopper ? (
442
+ <Portal
443
+ container={shouldRenderInBody ? document.body : inputWrapper.current}
444
+ >
445
+ <>{listEl}</>
446
+ </Portal>
447
+ ) : (
448
+ <>{isOpen && listEl}</>
449
+ )}
450
+ </div>
451
+ );
452
+ }