@alfalab/core-components-input-autocomplete 10.2.3 → 11.0.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 (262) hide show
  1. package/Component.desktop.d.ts +3 -70
  2. package/Component.desktop.js +2 -1
  3. package/Component.mobile.d.ts +5 -95
  4. package/Component.mobile.js +42 -45
  5. package/Component.modal.mobile.d.ts +5 -88
  6. package/Component.modal.mobile.js +9 -62
  7. package/Component.responsive.d.ts +3 -46
  8. package/Component.responsive.js +9 -12
  9. package/autocomplete-field/Component.d.ts +3 -3
  10. package/autocomplete-field/Component.js +1 -1
  11. package/autocomplete-field/index.css +2 -2
  12. package/autocomplete-mobile-field/Component.d.ts +2 -2
  13. package/autocomplete-mobile-field/Component.js +6 -3
  14. package/autocomplete-mobile-field/index.css +10 -10
  15. package/cssm/Component.desktop.d.ts +3 -70
  16. package/cssm/Component.desktop.js +2 -1
  17. package/cssm/Component.mobile.d.ts +5 -95
  18. package/cssm/Component.mobile.js +39 -44
  19. package/cssm/Component.modal.mobile.d.ts +5 -88
  20. package/cssm/Component.modal.mobile.js +10 -63
  21. package/cssm/Component.responsive.d.ts +3 -46
  22. package/cssm/Component.responsive.js +9 -11
  23. package/cssm/autocomplete-field/Component.d.ts +3 -3
  24. package/cssm/autocomplete-mobile-field/Component.d.ts +2 -2
  25. package/cssm/autocomplete-mobile-field/Component.js +5 -2
  26. package/cssm/autocomplete-mobile-field/index.module.css +2 -2
  27. package/cssm/desktop/index.d.ts +2 -0
  28. package/cssm/{desktop.js → desktop/index.js} +4 -3
  29. package/cssm/index.d.ts +2 -2
  30. package/cssm/index.js +5 -4
  31. package/cssm/mobile/index.d.ts +3 -0
  32. package/cssm/{mobile.js → mobile/index.js} +7 -7
  33. package/cssm/mobile.module.css +3 -10
  34. package/cssm/types.d.ts +58 -0
  35. package/cssm/types.js +2 -0
  36. package/cssm/utils.d.ts +2 -0
  37. package/cssm/utils.js +9 -0
  38. package/desktop/index.d.ts +2 -0
  39. package/{desktop.js → desktop/index.js} +3 -2
  40. package/desktop/package.json +3 -0
  41. package/esm/Component.desktop.d.ts +3 -70
  42. package/esm/Component.desktop.js +2 -1
  43. package/esm/Component.mobile.d.ts +5 -95
  44. package/esm/Component.mobile.js +43 -46
  45. package/esm/Component.modal.mobile.d.ts +5 -88
  46. package/esm/Component.modal.mobile.js +11 -61
  47. package/esm/Component.responsive.d.ts +3 -46
  48. package/esm/Component.responsive.js +9 -12
  49. package/esm/autocomplete-field/Component.d.ts +3 -3
  50. package/esm/autocomplete-field/Component.js +1 -1
  51. package/esm/autocomplete-field/index.css +2 -2
  52. package/esm/autocomplete-mobile-field/Component.d.ts +2 -2
  53. package/esm/autocomplete-mobile-field/Component.js +6 -3
  54. package/esm/autocomplete-mobile-field/index.css +10 -10
  55. package/esm/desktop/index.d.ts +2 -0
  56. package/esm/{desktop.js → desktop/index.js} +3 -2
  57. package/esm/index.d.ts +2 -2
  58. package/esm/index.js +5 -5
  59. package/esm/mobile/index.d.ts +3 -0
  60. package/esm/mobile/index.js +14 -0
  61. package/esm/mobile.css +4 -11
  62. package/esm/types.d.ts +58 -0
  63. package/esm/types.js +1 -0
  64. package/esm/utils.d.ts +2 -0
  65. package/esm/utils.js +5 -0
  66. package/index.d.ts +2 -2
  67. package/index.js +5 -5
  68. package/mobile/index.d.ts +3 -0
  69. package/{mobile.js → mobile/index.js} +5 -6
  70. package/mobile/package.json +3 -0
  71. package/mobile.css +4 -11
  72. package/modern/Component.desktop.d.ts +3 -70
  73. package/modern/Component.desktop.js +2 -1
  74. package/modern/Component.mobile.d.ts +5 -95
  75. package/modern/Component.mobile.js +54 -56
  76. package/modern/Component.modal.mobile.d.ts +5 -88
  77. package/modern/Component.modal.mobile.js +10 -68
  78. package/modern/Component.responsive.d.ts +3 -46
  79. package/modern/Component.responsive.js +8 -11
  80. package/modern/autocomplete-field/Component.d.ts +3 -3
  81. package/modern/autocomplete-field/Component.js +1 -1
  82. package/modern/autocomplete-field/index.css +2 -2
  83. package/modern/autocomplete-mobile-field/Component.d.ts +2 -2
  84. package/modern/autocomplete-mobile-field/Component.js +5 -2
  85. package/modern/autocomplete-mobile-field/index.css +10 -10
  86. package/modern/desktop/index.d.ts +2 -0
  87. package/modern/{desktop.js → desktop/index.js} +3 -2
  88. package/modern/index.d.ts +2 -2
  89. package/modern/index.js +5 -5
  90. package/modern/mobile/index.d.ts +3 -0
  91. package/modern/mobile/index.js +13 -0
  92. package/modern/mobile.css +4 -11
  93. package/modern/types.d.ts +58 -0
  94. package/modern/types.js +1 -0
  95. package/modern/utils.d.ts +2 -0
  96. package/modern/utils.js +5 -0
  97. package/package.json +7 -6
  98. package/src/Component.desktop.tsx +6 -44
  99. package/src/Component.mobile.tsx +90 -188
  100. package/src/Component.modal.mobile.tsx +5 -223
  101. package/src/Component.responsive.tsx +10 -30
  102. package/src/autocomplete-field/Component.tsx +2 -2
  103. package/src/autocomplete-mobile-field/Component.tsx +5 -1
  104. package/src/desktop/index.ts +2 -0
  105. package/src/desktop/package.json +3 -0
  106. package/src/index.ts +2 -4
  107. package/src/mobile/index.ts +4 -0
  108. package/src/mobile/package.json +3 -0
  109. package/src/mobile.module.css +2 -11
  110. package/src/types.ts +77 -0
  111. package/src/utils.ts +3 -0
  112. package/types.d.ts +58 -0
  113. package/types.js +2 -0
  114. package/utils.d.ts +2 -0
  115. package/utils.js +9 -0
  116. package/Component-0f4b9bed.d.ts +0 -72
  117. package/Component-69921c40.d.ts +0 -184
  118. package/Component-89f0cb07.d.ts +0 -38
  119. package/Component-bdb4c6b9.d.ts +0 -12
  120. package/Component-c76d6398.d.ts +0 -5
  121. package/Component-ebda875c.d.ts +0 -12
  122. package/Component.desktop-ebda875c.d.ts +0 -6
  123. package/Component.mobile-f1f15074.d.ts +0 -34
  124. package/Component.mobile-f28cbba0.d.ts +0 -41
  125. package/Component.modal.mobile-0f4b9bed.d.ts +0 -60
  126. package/Context-bdb4c6b9.d.ts +0 -4
  127. package/ResponsiveContext-baf4875b.d.ts +0 -5
  128. package/consts-f777ba1a.d.ts +0 -2
  129. package/cssm/Component-0f4b9bed.d.ts +0 -72
  130. package/cssm/Component-69921c40.d.ts +0 -184
  131. package/cssm/Component-89f0cb07.d.ts +0 -38
  132. package/cssm/Component-bdb4c6b9.d.ts +0 -12
  133. package/cssm/Component-c76d6398.d.ts +0 -5
  134. package/cssm/Component-ebda875c.d.ts +0 -12
  135. package/cssm/Component.desktop-ebda875c.d.ts +0 -6
  136. package/cssm/Component.mobile-f1f15074.d.ts +0 -34
  137. package/cssm/Component.mobile-f28cbba0.d.ts +0 -41
  138. package/cssm/Component.modal.mobile-0f4b9bed.d.ts +0 -60
  139. package/cssm/Context-bdb4c6b9.d.ts +0 -4
  140. package/cssm/ResponsiveContext-baf4875b.d.ts +0 -5
  141. package/cssm/consts-f777ba1a.d.ts +0 -2
  142. package/cssm/desktop-69921c40.d.ts +0 -6
  143. package/cssm/desktop.d.ts +0 -2
  144. package/cssm/hook-8abfea97.d.ts +0 -90
  145. package/cssm/index-136acbb1.d.ts +0 -28
  146. package/cssm/index-72dda473.d.ts +0 -12
  147. package/cssm/index-bdb4c6b9.d.ts +0 -2
  148. package/cssm/index-c76d6398.d.ts +0 -185
  149. package/cssm/index-ebda875c.d.ts +0 -146
  150. package/cssm/index-f12ee135.d.ts +0 -52
  151. package/cssm/mobile-89f0cb07.d.ts +0 -6
  152. package/cssm/mobile-96988a65.d.ts +0 -6
  153. package/cssm/mobile-f28cbba0.d.ts +0 -5
  154. package/cssm/mobile.d.ts +0 -3
  155. package/cssm/shared-4cd3936b.d.ts +0 -65
  156. package/cssm/types-1b036d4b.d.ts +0 -13
  157. package/cssm/types-72dda473.d.ts +0 -276
  158. package/cssm/typings-0f4b9bed.d.ts +0 -576
  159. package/cssm/typings-89f0cb07.d.ts +0 -93
  160. package/cssm/typings-bdb4c6b9.d.ts +0 -52
  161. package/cssm/useSkeleton-ebda875c.d.ts +0 -12
  162. package/cssm/utils-0f4b9bed.d.ts +0 -41
  163. package/cssm/utils-1574ad8b.d.ts +0 -29
  164. package/desktop-69921c40.d.ts +0 -6
  165. package/desktop.d.ts +0 -2
  166. package/esm/Component-0f4b9bed.d.ts +0 -72
  167. package/esm/Component-69921c40.d.ts +0 -184
  168. package/esm/Component-89f0cb07.d.ts +0 -38
  169. package/esm/Component-bdb4c6b9.d.ts +0 -12
  170. package/esm/Component-c76d6398.d.ts +0 -5
  171. package/esm/Component-ebda875c.d.ts +0 -12
  172. package/esm/Component.desktop-ebda875c.d.ts +0 -6
  173. package/esm/Component.mobile-f1f15074.d.ts +0 -34
  174. package/esm/Component.mobile-f28cbba0.d.ts +0 -41
  175. package/esm/Component.modal.mobile-0f4b9bed.d.ts +0 -60
  176. package/esm/Context-bdb4c6b9.d.ts +0 -4
  177. package/esm/ResponsiveContext-baf4875b.d.ts +0 -5
  178. package/esm/consts-f777ba1a.d.ts +0 -2
  179. package/esm/desktop-69921c40.d.ts +0 -6
  180. package/esm/desktop.d.ts +0 -2
  181. package/esm/hook-8abfea97.d.ts +0 -90
  182. package/esm/index-136acbb1.d.ts +0 -28
  183. package/esm/index-72dda473.d.ts +0 -12
  184. package/esm/index-bdb4c6b9.d.ts +0 -2
  185. package/esm/index-c76d6398.d.ts +0 -185
  186. package/esm/index-ebda875c.d.ts +0 -146
  187. package/esm/index-f12ee135.d.ts +0 -52
  188. package/esm/mobile-89f0cb07.d.ts +0 -6
  189. package/esm/mobile-96988a65.d.ts +0 -6
  190. package/esm/mobile-f28cbba0.d.ts +0 -5
  191. package/esm/mobile.d.ts +0 -3
  192. package/esm/mobile.js +0 -15
  193. package/esm/mobile.module-c5b1b036.js +0 -4
  194. package/esm/shared-4cd3936b.d.ts +0 -65
  195. package/esm/types-1b036d4b.d.ts +0 -13
  196. package/esm/types-72dda473.d.ts +0 -276
  197. package/esm/typings-0f4b9bed.d.ts +0 -576
  198. package/esm/typings-89f0cb07.d.ts +0 -93
  199. package/esm/typings-bdb4c6b9.d.ts +0 -52
  200. package/esm/useSkeleton-ebda875c.d.ts +0 -12
  201. package/esm/utils-0f4b9bed.d.ts +0 -41
  202. package/esm/utils-1574ad8b.d.ts +0 -29
  203. package/hook-8abfea97.d.ts +0 -90
  204. package/index-136acbb1.d.ts +0 -28
  205. package/index-72dda473.d.ts +0 -12
  206. package/index-bdb4c6b9.d.ts +0 -2
  207. package/index-c76d6398.d.ts +0 -185
  208. package/index-ebda875c.d.ts +0 -146
  209. package/index-f12ee135.d.ts +0 -52
  210. package/mobile-89f0cb07.d.ts +0 -6
  211. package/mobile-96988a65.d.ts +0 -6
  212. package/mobile-f28cbba0.d.ts +0 -5
  213. package/mobile.d.ts +0 -3
  214. package/mobile.module-c08975ff.js +0 -6
  215. package/modern/Component-0f4b9bed.d.ts +0 -72
  216. package/modern/Component-69921c40.d.ts +0 -184
  217. package/modern/Component-89f0cb07.d.ts +0 -38
  218. package/modern/Component-bdb4c6b9.d.ts +0 -12
  219. package/modern/Component-c76d6398.d.ts +0 -5
  220. package/modern/Component-ebda875c.d.ts +0 -12
  221. package/modern/Component.desktop-ebda875c.d.ts +0 -6
  222. package/modern/Component.mobile-f1f15074.d.ts +0 -34
  223. package/modern/Component.mobile-f28cbba0.d.ts +0 -41
  224. package/modern/Component.modal.mobile-0f4b9bed.d.ts +0 -60
  225. package/modern/Context-bdb4c6b9.d.ts +0 -4
  226. package/modern/ResponsiveContext-baf4875b.d.ts +0 -5
  227. package/modern/consts-f777ba1a.d.ts +0 -2
  228. package/modern/desktop-69921c40.d.ts +0 -6
  229. package/modern/desktop.d.ts +0 -2
  230. package/modern/hook-8abfea97.d.ts +0 -90
  231. package/modern/index-136acbb1.d.ts +0 -28
  232. package/modern/index-72dda473.d.ts +0 -12
  233. package/modern/index-bdb4c6b9.d.ts +0 -2
  234. package/modern/index-c76d6398.d.ts +0 -185
  235. package/modern/index-ebda875c.d.ts +0 -146
  236. package/modern/index-f12ee135.d.ts +0 -52
  237. package/modern/mobile-89f0cb07.d.ts +0 -6
  238. package/modern/mobile-96988a65.d.ts +0 -6
  239. package/modern/mobile-f28cbba0.d.ts +0 -5
  240. package/modern/mobile.d.ts +0 -3
  241. package/modern/mobile.js +0 -14
  242. package/modern/mobile.module-1bc0502b.js +0 -4
  243. package/modern/shared-4cd3936b.d.ts +0 -65
  244. package/modern/types-1b036d4b.d.ts +0 -13
  245. package/modern/types-72dda473.d.ts +0 -276
  246. package/modern/typings-0f4b9bed.d.ts +0 -576
  247. package/modern/typings-89f0cb07.d.ts +0 -93
  248. package/modern/typings-bdb4c6b9.d.ts +0 -52
  249. package/modern/useSkeleton-ebda875c.d.ts +0 -12
  250. package/modern/utils-0f4b9bed.d.ts +0 -41
  251. package/modern/utils-1574ad8b.d.ts +0 -29
  252. package/shared-4cd3936b.d.ts +0 -65
  253. package/src/desktop.ts +0 -1
  254. package/src/mobile.ts +0 -5
  255. package/types-1b036d4b.d.ts +0 -13
  256. package/types-72dda473.d.ts +0 -276
  257. package/typings-0f4b9bed.d.ts +0 -576
  258. package/typings-89f0cb07.d.ts +0 -93
  259. package/typings-bdb4c6b9.d.ts +0 -52
  260. package/useSkeleton-ebda875c.d.ts +0 -12
  261. package/utils-0f4b9bed.d.ts +0 -41
  262. package/utils-1574ad8b.d.ts +0 -29
@@ -1,140 +1,75 @@
1
- import React, { ChangeEvent, ElementType, RefObject, useMemo, useRef, useState } from 'react';
1
+ import React, { Ref, useMemo, useRef, useState } from 'react';
2
2
  import mergeRefs from 'react-merge-refs';
3
3
  import cn from 'classnames';
4
4
  import throttle from 'lodash.throttle';
5
5
 
6
- import { ButtonMobile, ButtonMobileProps } from '@alfalab/core-components-button/mobile';
7
- import { Input as CoreInput } from '@alfalab/core-components-input';
8
- import { SelectMobile, SelectMobileProps } from '@alfalab/core-components-select/mobile';
9
- import type {
10
- AdditionalMobileProps,
11
- BaseSelectChangePayload,
12
- BaseSelectProps,
6
+ import {
7
+ SelectMobile,
8
+ SelectMobileProps,
9
+ SelectModalMobile,
10
+ } from '@alfalab/core-components-select/mobile';
11
+ import {
12
+ AnyObject,
13
+ BottomSheetSelectMobileProps,
14
+ Footer,
15
+ ModalSelectMobileProps,
13
16
  } from '@alfalab/core-components-select/shared';
14
17
 
15
18
  import { AutocompleteMobileField } from './autocomplete-mobile-field';
19
+ import { InputAutocompleteMobileProps } from './types';
20
+ import { searchFilterStub } from './utils';
16
21
 
17
22
  import styles from './mobile.module.css';
18
23
 
19
- export type InputAutocompleteMobileProps = Omit<
20
- BaseSelectProps,
21
- | 'OptionsList'
22
- | 'Checkmark'
23
- | 'onScroll'
24
- | 'nativeSelect'
25
- | 'autocomplete'
26
- | 'valueRenderer'
27
- | 'searchProps'
28
- | 'showSearch'
29
- | 'Search'
30
- > & {
31
- /**
32
- * Обработчик выбора
33
- */
34
- onChange: (payload: string | BaseSelectChangePayload) => void;
35
-
36
- /**
37
- * Обработчик ввода фильтра.
38
- */
39
- onFilter: (event: ChangeEvent<HTMLInputElement>) => void;
40
-
41
- /**
42
- * Значение поля ввода
43
- */
44
- value?: string;
45
-
46
- /**
47
- * Значение фильтра.
48
- */
49
- filter?: string;
50
-
51
- /**
52
- * Обработчик нажатия на кнопку "Отмена".
53
- */
54
- onCancel?: () => void;
55
-
56
- /**
57
- * Обработчик нажатия на крестик в инпуте фильтра.
58
- */
59
- onClearFilter?: () => void;
60
-
61
- /**
62
- * Дополнительные пропсы компонента BottomSheet
63
- */
64
- bottomSheetProps?: AdditionalMobileProps['bottomSheetProps'];
65
-
66
- /**
67
- * Дополнительные пропсы на слот под заголовком компонента BottomSheet
68
- */
69
- bottomSheetHeaderAddonsProps?: Record<string, unknown>;
70
-
71
- /**
72
- * Дополнительные пропсы на кнопку "продолжить"
73
- */
74
- continueButtonProps?: ButtonMobileProps;
75
-
76
- /**
77
- * Дополнительные пропсы на кнопку "отмена"
78
- */
79
- cancelButtonProps?: ButtonMobileProps;
80
-
81
- /**
82
- * Кастомный инпут
83
- */
84
- Input?: ElementType;
85
- };
86
-
87
- const SELECTED: string[] = [];
88
-
89
24
  export const InputAutocompleteMobile = React.forwardRef(
90
25
  (
91
26
  {
92
27
  Input,
93
- bottomSheetProps = {},
94
- bottomSheetHeaderAddonsProps = {},
95
- value = '',
96
- filter = '',
28
+ value,
97
29
  name,
98
30
  Arrow = null,
99
31
  label,
100
- placeholder,
32
+ placeholder = '',
101
33
  size = 's',
102
34
  open: openProp,
103
- onFilter,
104
- onChange,
35
+ onInput,
105
36
  onOpen,
106
- onCancel,
107
- onClearFilter,
108
- continueButtonProps,
109
- cancelButtonProps,
110
- selected,
111
37
  multiple,
38
+ inputProps,
39
+ isBottomSheet = true,
40
+ dataTestId,
41
+ transitionProps,
112
42
  ...restProps
113
43
  }: InputAutocompleteMobileProps,
114
44
  ref,
115
45
  ) => {
116
46
  const [open, setOpen] = useState(false);
117
- const bottomSheetInputRef = useRef<HTMLInputElement>(null);
47
+ const frozenValue = useRef<string>('');
48
+ const searchInputRef = useRef<HTMLInputElement>(null);
118
49
  const targetRef = useRef<HTMLDivElement>(null);
119
50
 
120
- const setBottomSheetVisibility = (isOpen: boolean) => {
51
+ const restorePrevValue = () => onInput?.(null, { value: frozenValue.current });
52
+
53
+ const setModalVisibility = (isOpen: boolean) => {
121
54
  if (openProp === undefined) {
122
55
  setOpen(isOpen);
123
56
  }
124
57
 
125
- if (onOpen) {
126
- onOpen({ open: isOpen, name });
58
+ if (isOpen) {
59
+ frozenValue.current = value || '';
127
60
  }
61
+
62
+ onOpen?.({ open: isOpen, name });
128
63
  };
129
64
 
130
65
  const handleOpen: SelectMobileProps['onOpen'] = (payload) => {
131
- setBottomSheetVisibility(Boolean(payload.open));
66
+ setModalVisibility(Boolean(payload.open));
132
67
  };
133
68
 
134
69
  const handleOptionsListTouchMove = useMemo(
135
70
  () =>
136
71
  throttle(() => {
137
- const input = bottomSheetInputRef.current;
72
+ const input = searchInputRef.current;
138
73
 
139
74
  if (input && document.activeElement === input) {
140
75
  input.blur();
@@ -143,119 +78,86 @@ export const InputAutocompleteMobile = React.forwardRef(
143
78
  [],
144
79
  );
145
80
 
146
- const handleApply = () => {
147
- setBottomSheetVisibility(false);
148
- onChange(filter);
149
- };
150
-
151
- const handleChange: SelectMobileProps['onChange'] = (payload) => {
152
- onChange(payload);
153
-
154
- if (multiple) {
155
- // После выбора опции возвращаем фокус в поле ввода.
156
- bottomSheetInputRef.current?.focus();
157
- }
158
- };
81
+ const handleApply = () => setModalVisibility(false);
159
82
 
160
83
  const handleCancel = () => {
161
- setBottomSheetVisibility(false);
162
-
163
- if (onCancel) {
164
- onCancel();
165
- }
84
+ setModalVisibility(false);
85
+ restorePrevValue();
166
86
  };
167
87
 
168
- const handleInputFocus = (event: React.FocusEvent<HTMLInputElement>) => {
169
- const input = bottomSheetInputRef.current;
170
-
171
- // Перед закрытием шторки снимаем фокус с инпута, чтобы предотвратить скачок шторки.
172
- if (
173
- event.relatedTarget === targetRef.current &&
174
- input &&
175
- input === document.activeElement
176
- ) {
177
- input.blur();
178
- }
88
+ const handleExiting = (node: HTMLElement) => {
89
+ targetRef.current?.focus();
90
+ transitionProps?.onExiting?.(node);
179
91
  };
180
92
 
181
- const getBottomSheetProps = (): InputAutocompleteMobileProps['bottomSheetProps'] => {
182
- const Component = Input || CoreInput;
183
-
184
- return {
185
- actionButton: (
186
- <div className={styles.footer}>
187
- <ButtonMobile
188
- block={true}
189
- view='secondary'
190
- size='m'
191
- onClick={handleCancel}
192
- {...cancelButtonProps}
193
- >
194
- Отмена
195
- </ButtonMobile>
196
- <ButtonMobile
197
- block={true}
198
- view='primary'
199
- size='m'
200
- onClick={handleApply}
201
- {...continueButtonProps}
202
- >
203
- Продолжить
204
- </ButtonMobile>
205
- </div>
206
- ),
207
- title: label || placeholder,
208
- bottomAddons: (
209
- <Component
210
- block={true}
211
- clear={!!onClearFilter}
212
- onClear={onClearFilter}
213
- value={filter}
214
- onInput={onFilter}
215
- placeholder={placeholder}
216
- onFocus={handleInputFocus}
217
- {...bottomSheetHeaderAddonsProps}
218
- className={cn(
219
- styles.bottomAddonInput,
220
- bottomSheetHeaderAddonsProps.className as string,
221
- )}
222
- ref={mergeRefs([
223
- bottomSheetInputRef,
224
- bottomSheetHeaderAddonsProps.ref as RefObject<HTMLInputElement>,
225
- ])}
226
- />
227
- ),
228
- initialHeight: 'full',
229
- ...bottomSheetProps,
230
- containerProps: {
231
- onTouchMove: handleOptionsListTouchMove,
232
- ...bottomSheetProps.containerProps,
233
- },
234
- };
93
+ const isOpen = Boolean(open || openProp);
94
+
95
+ const Component = isBottomSheet ? SelectMobile : SelectModalMobile;
96
+
97
+ const componentProps:
98
+ | ModalSelectMobileProps['modalProps']
99
+ | BottomSheetSelectMobileProps['bottomSheetProps'] = {
100
+ title: label || placeholder,
101
+ onClose: restorePrevValue,
102
+ transitionProps: {
103
+ ...transitionProps,
104
+ onExiting: handleExiting,
105
+ },
106
+ [isBottomSheet ? 'containerProps' : 'componentDivProps']: {
107
+ onTouchMove: handleOptionsListTouchMove,
108
+ },
235
109
  };
236
110
 
237
111
  return (
238
- <SelectMobile
239
- useWithApplyHook={false}
112
+ <Component
240
113
  Field={AutocompleteMobileField}
241
114
  {...restProps}
115
+ {...(isBottomSheet
116
+ ? { bottomSheetProps: componentProps }
117
+ : { modalProps: componentProps })}
118
+ dataTestId={dataTestId}
119
+ useWithApplyHook={false}
120
+ showSearch={true}
121
+ searchProps={{
122
+ value,
123
+ filterFn: searchFilterStub,
124
+ componentProps: {
125
+ leftAddons: null,
126
+ placeholder,
127
+ ...inputProps,
128
+ className: cn(styles.input, inputProps?.className),
129
+ clear: inputProps?.clear ?? false,
130
+ ref: mergeRefs([searchInputRef, inputProps?.ref as Ref<HTMLInputElement>]),
131
+ onChange: onInput,
132
+ },
133
+ }}
134
+ Search={Input}
242
135
  ref={mergeRefs([targetRef, ref])}
243
- selected={selected || SELECTED}
244
- open={Boolean(open || openProp)}
136
+ open={isOpen}
245
137
  onOpen={handleOpen}
246
138
  Arrow={Arrow}
247
- onChange={handleChange}
248
139
  placeholder={placeholder}
249
140
  label={label}
250
141
  size={size}
251
142
  name={name}
252
143
  multiple={multiple}
253
- bottomSheetProps={getBottomSheetProps()}
254
144
  optionsListProps={{
255
- showFooter: false,
256
- ...(restProps.optionsListProps as Record<string, unknown>),
145
+ footer: (
146
+ <Footer
147
+ showClear={true}
148
+ handleClear={handleCancel}
149
+ handleApply={handleApply}
150
+ clearText='Отмена'
151
+ applyText='Продолжить'
152
+ dataTestId={dataTestId}
153
+ />
154
+ ),
155
+ ...(restProps.optionsListProps as AnyObject),
156
+ }}
157
+ fieldProps={{
158
+ value: isOpen ? frozenValue.current : value,
159
+ ...(restProps.fieldProps as AnyObject),
257
160
  }}
258
- fieldProps={{ value, ...(restProps.fieldProps as Record<string, unknown>) }}
259
161
  />
260
162
  );
261
163
  },
@@ -1,227 +1,9 @@
1
- import React, { ChangeEvent, ElementType, RefObject, useEffect, useRef } from 'react';
2
- import mergeRefs from 'react-merge-refs';
3
- import cn from 'classnames';
4
- import throttle from 'lodash.throttle';
1
+ import React from 'react';
5
2
 
6
- import { ButtonMobile, ButtonMobileProps } from '@alfalab/core-components-button/mobile';
7
- import { Input as CoreInput } from '@alfalab/core-components-input';
8
- import {
9
- SelectMobileProps,
10
- SelectModalMobile,
11
- SelectModalMobileProps,
12
- } from '@alfalab/core-components-select/mobile';
13
- import type { BaseSelectChangePayload } from '@alfalab/core-components-select/shared';
14
-
15
- import { AutocompleteMobileField } from './autocomplete-mobile-field';
16
-
17
- import styles from './mobile.module.css';
18
-
19
- export type InputAutocompleteModalMobileProps = Omit<
20
- SelectModalMobileProps,
21
- 'OptionsList' | 'Checkmark' | 'onScroll' | 'nativeSelect' | 'autocomplete' | 'valueRenderer'
22
- > & {
23
- /**
24
- * Обработчик выбора
25
- */
26
- onChange: (payload: string | BaseSelectChangePayload) => void;
27
-
28
- /**
29
- * Обработчик ввода фильтра.
30
- */
31
- onFilter: (event: ChangeEvent<HTMLInputElement>) => void;
32
-
33
- /**
34
- * Значение поля ввода
35
- */
36
- value?: string;
37
-
38
- /**
39
- * Значение фильтра.
40
- */
41
- filter?: string;
42
-
43
- /**
44
- * Обработчик нажатия на кнопку "Отмена".
45
- */
46
- onCancel?: () => void;
47
-
48
- /**
49
- * Обработчик нажатия на крестик в инпуте фильтра.
50
- */
51
- onClearFilter?: () => void;
52
-
53
- /**
54
- * Дополнительные пропсы на слот под заголовком
55
- */
56
- bottomAddonProps?: Record<string, unknown>;
57
-
58
- /**
59
- * Дополнительные пропсы на кнопку "продолжить"
60
- */
61
- continueButtonProps?: ButtonMobileProps;
62
-
63
- /**
64
- * Дополнительные пропсы на кнопку "отмена"
65
- */
66
- cancelButtonProps?: ButtonMobileProps;
67
-
68
- /**
69
- * Кастомный инпут
70
- */
71
- Input?: ElementType;
72
- };
3
+ import { InputAutocompleteMobile } from './Component.mobile';
4
+ import { InputAutocompleteMobileProps } from './types';
73
5
 
74
6
  export const InputAutocompleteModalMobile = React.forwardRef<
75
7
  HTMLDivElement,
76
- InputAutocompleteModalMobileProps
77
- >(
78
- (
79
- {
80
- open: openProp,
81
- onOpen,
82
- name,
83
- size = 's',
84
- Input = CoreInput,
85
- Arrow = null,
86
- onClearFilter,
87
- filter = '',
88
- onFilter,
89
- placeholder,
90
- bottomAddonProps,
91
- multiple,
92
- onChange,
93
- onCancel,
94
- value,
95
- cancelButtonProps,
96
- continueButtonProps,
97
- modalProps,
98
- modalHeaderProps,
99
- modalFooterProps,
100
- ...restProps
101
- }: InputAutocompleteModalMobileProps,
102
- ref,
103
- ) => {
104
- const [open, setOpen] = React.useState(false);
105
- const targetRef = useRef<HTMLDivElement>(null);
106
- const modalScrollableRef = useRef<HTMLDivElement>(null);
107
- const bottomAddonRef = useRef<HTMLInputElement>(null);
108
-
109
- useEffect(() => {
110
- const modalContentNode = modalScrollableRef.current;
111
-
112
- const handleListTouchMove = throttle(() => {
113
- const input = bottomAddonRef.current;
114
-
115
- if (input && document.activeElement === input) {
116
- input.blur();
117
- }
118
- }, 300);
119
-
120
- modalContentNode?.addEventListener('touchmove', handleListTouchMove);
121
-
122
- return () => modalContentNode?.removeEventListener('touchmove', handleListTouchMove);
123
- }, [open]);
124
-
125
- const setOpenModalSelect = (isOpen: boolean) => {
126
- if (openProp === undefined) {
127
- setOpen(isOpen);
128
- }
129
-
130
- onOpen?.({ open: isOpen, name });
131
- };
132
-
133
- const handleOpen: SelectMobileProps['onOpen'] = (payload) => {
134
- setOpenModalSelect(Boolean(payload.open));
135
- };
136
-
137
- const handleApply = () => {
138
- setOpenModalSelect(false);
139
- onChange(filter);
140
- };
141
-
142
- const handleCancel = () => {
143
- setOpenModalSelect(false);
144
- onCancel?.();
145
- };
146
-
147
- const handleChange: SelectMobileProps['onChange'] = (payload) => {
148
- onChange(payload);
149
-
150
- if (multiple) {
151
- // После выбора опции возвращаем фокус в поле ввода.
152
- bottomAddonRef.current?.focus();
153
- }
154
- };
155
-
156
- const renderBottomAddon = () => (
157
- <Input
158
- block={true}
159
- clear={!!onClearFilter}
160
- onClear={onClearFilter}
161
- value={filter}
162
- onInput={onFilter}
163
- placeholder={placeholder}
164
- {...bottomAddonProps}
165
- className={cn(styles.bottomAddonInput, bottomAddonProps?.className as string)}
166
- ref={mergeRefs([
167
- bottomAddonRef,
168
- bottomAddonProps?.ref as RefObject<HTMLInputElement>,
169
- ])}
170
- />
171
- );
172
-
173
- const renderFooter = () => (
174
- <React.Fragment>
175
- <ButtonMobile
176
- block={true}
177
- view='secondary'
178
- size='m'
179
- onClick={handleCancel}
180
- {...cancelButtonProps}
181
- >
182
- Отмена
183
- </ButtonMobile>
184
- <ButtonMobile
185
- block={true}
186
- view='primary'
187
- size='m'
188
- onClick={handleApply}
189
- {...continueButtonProps}
190
- >
191
- Продолжить
192
- </ButtonMobile>
193
- </React.Fragment>
194
- );
195
-
196
- return (
197
- <SelectModalMobile
198
- Field={AutocompleteMobileField}
199
- useWithApplyHook={false}
200
- {...restProps}
201
- ref={mergeRefs([ref, targetRef])}
202
- Arrow={Arrow}
203
- onOpen={handleOpen}
204
- onChange={handleChange}
205
- multiple={multiple}
206
- open={openProp ?? open}
207
- size={size}
208
- fieldProps={{ value, ...(restProps.fieldProps as Record<string, unknown>) }}
209
- placeholder={placeholder}
210
- name={name}
211
- modalProps={{
212
- ...modalProps,
213
- componentRef: modalScrollableRef,
214
- }}
215
- modalHeaderProps={{
216
- ...modalHeaderProps,
217
- bottomAddons: renderBottomAddon(),
218
- }}
219
- modalFooterProps={{
220
- sticky: true,
221
- ...modalFooterProps,
222
- children: renderFooter(),
223
- }}
224
- />
225
- );
226
- },
227
- );
8
+ InputAutocompleteMobileProps
9
+ >((props, ref) => <InputAutocompleteMobile {...props} ref={ref} isBottomSheet={false} />);
@@ -1,40 +1,20 @@
1
1
  import React, { forwardRef } from 'react';
2
2
 
3
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
4
- import { ButtonMobileProps } from '@alfalab/core-components-button/mobile';
5
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
6
- import type { BaseSelectProps } from '@alfalab/core-components-select/shared';
7
- import { useMedia } from '@alfalab/hooks';
3
+ import { useMatchMedia } from '@alfalab/core-components-mq';
8
4
 
9
- import { InputAutocompleteDesktop, InputAutocompleteDesktopProps } from './Component.desktop';
10
- import { InputAutocompleteMobile, InputAutocompleteMobileProps } from './Component.mobile';
5
+ import { InputAutocompleteDesktop } from './Component.desktop';
6
+ import { InputAutocompleteMobile } from './Component.mobile';
7
+ import { InputAutocompleteProps } from './types';
11
8
 
12
- export type InputAutocompleteResponsiveProps = InputAutocompleteDesktopProps &
13
- InputAutocompleteMobileProps & {
14
- /**
15
- * Контрольная точка, с нее начинается desktop версия
16
- * @default 1024
17
- */
18
- breakpoint?: number;
19
- };
20
-
21
- export type InputAutocompleteMedia = 'desktop' | 'mobile';
22
-
23
- export const InputAutocompleteResponsive = forwardRef<
9
+ export const InputAutocomplete = forwardRef<
24
10
  HTMLInputElement | HTMLDivElement,
25
- InputAutocompleteResponsiveProps
26
- >(({ breakpoint = 1024, ...restProps }, ref) => {
27
- const [view] = useMedia<InputAutocompleteMedia>(
28
- [
29
- ['mobile', `(max-width: ${breakpoint - 1}px)`],
30
- ['desktop', `(min-width: ${breakpoint}px)`],
31
- ],
32
- 'desktop',
33
- );
11
+ InputAutocompleteProps
12
+ >(({ breakpoint = 1024, defaultMatchMediaValue = true, mobileProps, ...restProps }, ref) => {
13
+ const [isDesktop] = useMatchMedia(`(min-width: ${breakpoint}px)`, defaultMatchMediaValue);
34
14
 
35
- return view === 'desktop' ? (
15
+ return isDesktop ? (
36
16
  <InputAutocompleteDesktop {...restProps} ref={ref as React.Ref<HTMLInputElement>} />
37
17
  ) : (
38
- <InputAutocompleteMobile {...restProps} ref={ref} />
18
+ <InputAutocompleteMobile {...restProps} {...mobileProps} ref={ref} />
39
19
  );
40
20
  });
@@ -5,12 +5,12 @@ import cn from 'classnames';
5
5
  import { InputDesktop as DefaultInput } from '@alfalab/core-components-input/desktop';
6
6
  import type { FieldProps } from '@alfalab/core-components-select/shared';
7
7
 
8
- import { InputAutocompleteDesktopProps } from '../Component.desktop';
8
+ import { InputAutocompleteCommonProps } from '../types';
9
9
 
10
10
  import styles from './index.module.css';
11
11
 
12
12
  export type AutocompleteFieldProps = FieldProps &
13
- Pick<InputAutocompleteDesktopProps, 'Input' | 'inputProps' | 'value' | 'onInput' | 'readOnly'>;
13
+ Pick<InputAutocompleteCommonProps, 'Input' | 'inputProps' | 'value' | 'onInput' | 'readOnly'>;
14
14
 
15
15
  export const AutocompleteField = ({
16
16
  label,
@@ -53,6 +53,8 @@ export const AutocompleteMobileField = ({
53
53
  const filled = Boolean(value);
54
54
  const showPlaceholder = placeholder && !filled && labelView === 'outer';
55
55
 
56
+ const { tabIndex, ...restInnerProps } = innerProps;
57
+
56
58
  return (
57
59
  <div
58
60
  className={styles.component}
@@ -73,8 +75,10 @@ export const AutocompleteMobileField = ({
73
75
  labelView={labelView}
74
76
  rightAddons={Arrow}
75
77
  dataTestId={getDataTestId(dataTestId, 'form-control')}
78
+ // downshift устанавливает фокус на таргет поле после выбора опции, не даем ему это сделать пока открыт список, иначе поле поиска будет терять фокус
79
+ tabIndex={open ? undefined : tabIndex}
76
80
  {...restProps}
77
- {...innerProps}
81
+ {...restInnerProps}
78
82
  >
79
83
  <div className={styles.contentWrapper}>
80
84
  {showPlaceholder && <span className={styles.placeholder}>{placeholder}</span>}
@@ -0,0 +1,2 @@
1
+ export { InputAutocompleteDesktop } from '../Component.desktop';
2
+ export type { InputAutocompleteCommonProps as InputAutocompleteDesktopProps } from '../types';
@@ -0,0 +1,3 @@
1
+ {
2
+ "module": "../esm/desktop/index.js"
3
+ }
package/src/index.ts CHANGED
@@ -1,4 +1,2 @@
1
- export {
2
- InputAutocompleteResponsive as InputAutocomplete,
3
- InputAutocompleteResponsiveProps as InputAutocompleteProps,
4
- } from './Component.responsive';
1
+ export { InputAutocomplete } from './Component.responsive';
2
+ export type { InputAutocompleteProps } from './types';