@megafon/ui-core 5.5.0 → 5.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [5.6.0](https://hq-gitlab.megafon.ru/site-portal/services/megafon-ui/compare/@megafon/ui-core@5.5.0...@megafon/ui-core@5.6.0) (2024-02-20)
7
+
8
+
9
+ ### Features
10
+
11
+ * **search:** added results for not found ([1a62ab7](https://hq-gitlab.megafon.ru/site-portal/services/megafon-ui/commit/1a62ab78071e2b58235d93f403512a987a3c5ce1))
12
+
13
+
14
+
15
+
16
+
6
17
  # [5.5.0](https://hq-gitlab.megafon.ru/site-portal/services/megafon-ui/compare/@megafon/ui-core@5.4.6...@megafon/ui-core@5.5.0) (2024-02-13)
7
18
 
8
19
 
@@ -213,10 +213,43 @@
213
213
  padding-right: 12px;
214
214
  padding-left: 12px;
215
215
  }
216
- .mfui-search__list-item_active {
216
+ .mfui-search__popular-item {
217
+ display: -webkit-box;
218
+ display: -ms-flexbox;
219
+ display: flex;
220
+ -webkit-box-align: center;
221
+ -ms-flex-align: center;
222
+ align-items: center;
223
+ min-height: 48px;
224
+ padding-right: 16px;
225
+ padding-left: 12px;
226
+ border-radius: 16px;
227
+ text-decoration: none;
228
+ background-color: var(--base);
229
+ cursor: pointer;
230
+ }
231
+ .mfui-search__popular-icon {
232
+ display: -webkit-box;
233
+ display: -ms-flexbox;
234
+ display: flex;
235
+ -ms-flex-negative: 0;
236
+ flex-shrink: 0;
237
+ -webkit-box-align: center;
238
+ -ms-flex-align: center;
239
+ align-items: center;
240
+ -webkit-box-pack: center;
241
+ -ms-flex-pack: center;
242
+ justify-content: center;
243
+ width: 32px;
244
+ height: 32px;
245
+ margin-right: 16px;
246
+ }
247
+ .mfui-search__list-item_active,
248
+ .mfui-search__popular-item_active {
217
249
  background-color: var(--spbSky0);
218
250
  }
219
- .mfui-search__item-title {
251
+ .mfui-search__item-title,
252
+ .mfui-search__popular-item-title {
220
253
  position: relative;
221
254
  -webkit-box-flex: 1;
222
255
  -ms-flex-positive: 1;
@@ -224,7 +257,8 @@
224
257
  overflow: hidden;
225
258
  white-space: nowrap;
226
259
  }
227
- .mfui-search__item-title:after {
260
+ .mfui-search__item-title:after,
261
+ .mfui-search__popular-item-title:after {
228
262
  content: '';
229
263
  position: absolute;
230
264
  top: 0;
@@ -234,7 +268,8 @@
234
268
  background: -webkit-gradient(linear, left top, right top, from(rgba(255, 255, 255, 0)), to(var(--base)));
235
269
  background: linear-gradient(to right, rgba(255, 255, 255, 0), var(--base));
236
270
  }
237
- .mfui-search__list-item_active .mfui-search__item-title:after {
271
+ .mfui-search__list-item_active .mfui-search__item-title:after,
272
+ .mfui-search__popular-item_active .mfui-search__popular-item-title:after {
238
273
  background: -webkit-gradient(linear, left top, right top, from(rgba(255, 255, 255, 0)), to(var(--spbSky0)));
239
274
  background: linear-gradient(to right, rgba(255, 255, 255, 0), var(--spbSky0));
240
275
  }
@@ -261,6 +296,23 @@
261
296
  align-items: center;
262
297
  height: 100%;
263
298
  }
299
+ .mfui-search__list_not-found {
300
+ cursor: default;
301
+ }
302
+ .mfui-search__not-found-text {
303
+ font-family: inherit;
304
+ padding: 12px 32px;
305
+ color: var(--spbSky3);
306
+ background-color: var(--base);
307
+ }
308
+ .mfui-search__not-found-text_show-popular {
309
+ padding: 0 0 16px 0;
310
+ border-bottom: 1px solid var(--spbSky2);
311
+ }
312
+ .mfui-search__popular-title {
313
+ padding-top: 16px;
314
+ padding-bottom: 8px;
315
+ }
264
316
  .mfui-search_open {
265
317
  z-index: 12;
266
318
  }
@@ -18,6 +18,14 @@ export declare type SearchItem = {
18
18
  /** Размер горизонтальных отступов элемента */
19
19
  paddings?: typeof SearchItemsPaddings[keyof typeof SearchItemsPaddings];
20
20
  };
21
+ export declare type PopularItem = {
22
+ /** Название элемента */
23
+ title: string;
24
+ /** Ссылка элемента */
25
+ href?: string;
26
+ /** Иконка элемента */
27
+ icon?: JSX.Element;
28
+ };
21
29
  export interface ISearchProps {
22
30
  /** Дополнительные data атрибуты к внутренним элементам */
23
31
  dataAttrs?: {
@@ -28,6 +36,7 @@ export interface ISearchProps {
28
36
  item?: Record<string, string>;
29
37
  itemTitle?: Record<string, string>;
30
38
  notice?: Record<string, string>;
39
+ popularItem?: Record<string, string>;
31
40
  };
32
41
  /** Значение */
33
42
  value?: string;
@@ -63,9 +72,20 @@ export interface ISearchProps {
63
72
  control?: string;
64
73
  icon?: string;
65
74
  submitButton?: string;
75
+ textNotFound?: string;
76
+ popularTitle?: string;
77
+ popularItem?: string;
66
78
  };
67
79
  /** Отображение загрузки */
68
80
  showLoader?: boolean;
81
+ /** Выводить результат, когда нет результатов поиска */
82
+ showNotFound?: boolean;
83
+ /** Текст, когда нет результатов поиска */
84
+ textNotFound?: string;
85
+ /** Популярные элементы (max 5) */
86
+ popularItems?: PopularItem[];
87
+ /** Заголовок перед популярными элементами */
88
+ popularTitle?: string;
69
89
  /** Обработчик изменения поля */
70
90
  onChange?: (value: string) => void;
71
91
  /** Обработчик нажатия на enter */
@@ -74,6 +94,8 @@ export interface ISearchProps {
74
94
  onBlur?: (e: React.FocusEvent<HTMLInputElement>) => void;
75
95
  /** Обработчик входа в фокус */
76
96
  onFocus?: (e: React.FocusEvent<HTMLInputElement>) => void;
97
+ /** Обработчик нажатия на популярный элемент */
98
+ onPopularItemClick?: (popularItemValue?: string) => void;
77
99
  }
78
100
  declare const Search: React.FC<ISearchProps>;
79
101
  export default Search;
@@ -1,5 +1,6 @@
1
1
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
2
  import _extends from "@babel/runtime/helpers/extends";
3
+ import "core-js/modules/es.array.slice.js";
3
4
  import "core-js/modules/es.regexp.exec.js";
4
5
  import "core-js/modules/es.string.replace.js";
5
6
  import "core-js/modules/es.string.split.js";
@@ -11,6 +12,8 @@ import React from 'react';
11
12
  import { cnCreate, filterDataAttrs } from '@megafon/ui-helpers';
12
13
  import debounce from 'lodash.debounce';
13
14
  import * as PropTypes from 'prop-types';
15
+ import Caption from "../Caption/Caption";
16
+ import Header from "../Header/Header";
14
17
  import Preloader from "../Preloader/Preloader";
15
18
  import "./Search.css";
16
19
 
@@ -40,6 +43,7 @@ var ClearIcon = function ClearIcon(props) {
40
43
  };
41
44
 
42
45
  var SEARCH_QUERY_REGEX = /[^A-Z-a-zА-ЯЁа-яё0-9]/g;
46
+ var MAX_POPULAR_ITEMS_LENGTH = 5;
43
47
  export var Verification = {
44
48
  VALID: 'valid',
45
49
  ERROR: 'error'
@@ -72,10 +76,16 @@ var Search = function Search(_ref) {
72
76
  _ref$type = _ref.type,
73
77
  type = _ref$type === void 0 ? 'textfield' : _ref$type,
74
78
  showLoader = _ref.showLoader,
79
+ showNotFound = _ref.showNotFound,
80
+ textNotFound = _ref.textNotFound,
81
+ _ref$popularItems = _ref.popularItems,
82
+ popularItems = _ref$popularItems === void 0 ? [] : _ref$popularItems,
83
+ popularTitle = _ref.popularTitle,
75
84
  onChange = _ref.onChange,
76
85
  onSubmit = _ref.onSubmit,
77
86
  onBlur = _ref.onBlur,
78
- onFocus = _ref.onFocus;
87
+ onFocus = _ref.onFocus,
88
+ onPopularItemClick = _ref.onPopularItemClick;
79
89
 
80
90
  var _React$useState = React.useState(value),
81
91
  _React$useState2 = _slicedToArray(_React$useState, 2),
@@ -92,15 +102,24 @@ var Search = function Search(_ref) {
92
102
  isFocused = _React$useState6[0],
93
103
  setFocus = _React$useState6[1];
94
104
 
105
+ var _React$useState7 = React.useState(-1),
106
+ _React$useState8 = _slicedToArray(_React$useState7, 2),
107
+ popularActiveIndex = _React$useState8[0],
108
+ setPopularActiveIndex = _React$useState8[1];
109
+
95
110
  var debouncedOnChange = React.useRef(debounce(function (inputValue) {
96
111
  return onChange && onChange(inputValue);
97
112
  }, changeDelay));
98
113
  var highlightedItem = React.useRef(null);
99
114
  var fieldNode = React.useRef(null);
115
+ var correctPopularItems = React.useMemo(function () {
116
+ return popularItems.slice(0, MAX_POPULAR_ITEMS_LENGTH);
117
+ }, [popularItems]);
100
118
  var isCompact = type === 'compact';
101
119
  var showClearIcon = isCompact && !!searchQuery;
102
120
  var showTextFieldSubmit = !hideIcon && !isCompact && !showLoader;
103
121
  var showAdditionalElement = !hideIcon || showLoader || showClearIcon;
122
+ var showPopularItems = !!correctPopularItems.length;
104
123
  var handleChange = React.useCallback(function (e) {
105
124
  var _e$target$value = e.target.value,
106
125
  inputValue = _e$target$value === void 0 ? '' : _e$target$value;
@@ -120,6 +139,11 @@ var Search = function Search(_ref) {
120
139
  setActiveIndex(index);
121
140
  };
122
141
  }, []);
142
+ var handlePopularItemHover = React.useCallback(function (index) {
143
+ return function () {
144
+ setPopularActiveIndex(index);
145
+ };
146
+ }, []);
123
147
  var handleSearchSubmit = React.useCallback(function () {
124
148
  onSubmit && searchQuery && onSubmit(searchQuery);
125
149
  }, [searchQuery, onSubmit]);
@@ -149,26 +173,90 @@ var Search = function Search(_ref) {
149
173
  setActiveIndex(-1);
150
174
  }
151
175
  }, [activeIndex]);
152
- var handleKeyDown = React.useCallback(function (e) {
153
- if (e.key === 'ArrowDown' && activeIndex < items.length - 1) {
154
- setActiveIndex(function (index) {
155
- return index + 1;
156
- });
157
- e.preventDefault();
158
- } else if (e.key === 'ArrowUp' && activeIndex > -1) {
159
- setActiveIndex(function (index) {
160
- return index - 1;
161
- });
162
- e.preventDefault();
163
- } else if (e.key === 'Enter' && activeIndex > -1) {
164
- handleItemSubmit(activeIndex);
165
- e.preventDefault();
166
- } else if (e.key === 'Enter' && activeIndex === -1) {
167
- handleSearchSubmit();
176
+ var handlePopularItemMouseDown = React.useCallback(function (e) {
177
+ e.preventDefault();
178
+ }, []);
179
+ var handlePopularItemMouseUp = React.useCallback(function () {
180
+ var _a;
181
+
182
+ var chosenValue = popularItems[popularActiveIndex].title;
183
+ (_a = fieldNode === null || fieldNode === void 0 ? void 0 : fieldNode.current) === null || _a === void 0 ? void 0 : _a.blur();
184
+ onPopularItemClick === null || onPopularItemClick === void 0 ? void 0 : onPopularItemClick(chosenValue);
185
+ }, [onPopularItemClick, popularActiveIndex, popularItems]);
186
+ var handleKeyDownPopularItems = React.useCallback(function (e) {
187
+ var _a;
188
+
189
+ switch (true) {
190
+ case e.key === 'ArrowDown' && popularActiveIndex < popularItems.length - 1:
191
+ setPopularActiveIndex(function (index) {
192
+ return index + 1;
193
+ });
194
+ e.preventDefault();
195
+ break;
196
+
197
+ case e.key === 'ArrowUp' && popularActiveIndex > -1:
198
+ setPopularActiveIndex(function (index) {
199
+ return index - 1;
200
+ });
201
+ e.preventDefault();
202
+ break;
203
+
204
+ case e.key === 'Enter' && popularActiveIndex > -1:
205
+ {
206
+ var currentHref = (_a = popularItems[popularActiveIndex]) === null || _a === void 0 ? void 0 : _a.href;
207
+ handlePopularItemMouseUp();
208
+
209
+ if (currentHref) {
210
+ window.location.href = currentHref;
211
+ }
212
+
213
+ break;
214
+ }
215
+
216
+ default:
217
+ break;
218
+ }
219
+
220
+ return false;
221
+ }, [popularActiveIndex, popularItems, handlePopularItemMouseUp]);
222
+ var handleKeyDownItems = React.useCallback(function (e) {
223
+ switch (true) {
224
+ case e.key === 'ArrowDown' && activeIndex < items.length - 1:
225
+ setActiveIndex(function (index) {
226
+ return index + 1;
227
+ });
228
+ e.preventDefault();
229
+ break;
230
+
231
+ case e.key === 'ArrowUp' && activeIndex > -1:
232
+ setActiveIndex(function (index) {
233
+ return index - 1;
234
+ });
235
+ e.preventDefault();
236
+ break;
237
+
238
+ case e.key === 'Enter' && activeIndex > -1:
239
+ handleItemSubmit(activeIndex);
240
+ e.preventDefault();
241
+ break;
242
+
243
+ case e.key === 'Enter' && activeIndex === -1:
244
+ handleSearchSubmit();
245
+ break;
246
+
247
+ default:
248
+ break;
168
249
  }
169
250
 
170
251
  return false;
171
- }, [activeIndex, items, handleItemSubmit, handleSearchSubmit]);
252
+ }, [activeIndex, handleItemSubmit, handleSearchSubmit, items.length]);
253
+ var handleKeyDown = React.useCallback(function (e) {
254
+ if (showNotFound && showPopularItems) {
255
+ return handleKeyDownPopularItems(e);
256
+ }
257
+
258
+ return handleKeyDownItems(e);
259
+ }, [handleKeyDownItems, handleKeyDownPopularItems, showNotFound, showPopularItems]);
172
260
 
173
261
  var handleClearClick = function handleClearClick() {
174
262
  var _a; // TODO: После обновления TS до 4 версии, установить @types/lodash.debounce и убрать disable
@@ -266,6 +354,53 @@ var Search = function Search(_ref) {
266
354
  })));
267
355
  };
268
356
 
357
+ var renderPopularItems = function renderPopularItems() {
358
+ return /*#__PURE__*/React.createElement("div", {
359
+ className: cn('popular-list')
360
+ }, correctPopularItems.map(function (_ref3, i) {
361
+ var title = _ref3.title,
362
+ href = _ref3.href,
363
+ icon = _ref3.icon;
364
+ var Elem = href ? 'a' : 'div';
365
+ return /*#__PURE__*/React.createElement(Elem, _extends({}, filterDataAttrs(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.popularItem, i + 1), {
366
+ className: cn('popular-item', {
367
+ active: popularActiveIndex === i
368
+ }, [classes === null || classes === void 0 ? void 0 : classes.popularItem]),
369
+ key: i,
370
+ href: href,
371
+ onMouseDown: handlePopularItemMouseDown,
372
+ onMouseUp: handlePopularItemMouseUp,
373
+ onMouseEnter: handlePopularItemHover(i)
374
+ }), icon && /*#__PURE__*/React.createElement("span", {
375
+ className: cn('popular-icon')
376
+ }, icon), /*#__PURE__*/React.createElement(Header, {
377
+ className: cn('popular-item-title'),
378
+ as: "h5"
379
+ }, title));
380
+ }));
381
+ };
382
+
383
+ var renderNotFound = function renderNotFound() {
384
+ return /*#__PURE__*/React.createElement("div", {
385
+ className: cn('list', {
386
+ 'not-found': true
387
+ })
388
+ }, /*#__PURE__*/React.createElement("div", {
389
+ className: cn('list-inner')
390
+ }, /*#__PURE__*/React.createElement("div", {
391
+ className: cn('not-found-text', {
392
+ 'show-popular': showPopularItems
393
+ }, [classes === null || classes === void 0 ? void 0 : classes.textNotFound])
394
+ }, textNotFound || 'Ничего не нашлось'), showPopularItems && /*#__PURE__*/React.createElement("div", {
395
+ className: cn('popular-wrap')
396
+ }, /*#__PURE__*/React.createElement(Caption, {
397
+ className: cn('popular-title', [classes === null || classes === void 0 ? void 0 : classes.popularTitle]),
398
+ variant: "medium",
399
+ color: "gray",
400
+ hasMargin: false
401
+ }, popularTitle || 'Популярные действия'), renderPopularItems())));
402
+ };
403
+
269
404
  return /*#__PURE__*/React.createElement("div", _extends({}, filterDataAttrs(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.root), {
270
405
  className: cn({
271
406
  open: isFocused,
@@ -304,7 +439,7 @@ var Search = function Search(_ref) {
304
439
  delay: false,
305
440
  className: cn('loader'),
306
441
  sizeAll: "small"
307
- }), showClearIcon && renderClearButton()), !!items.length && renderList()), noticeText && /*#__PURE__*/React.createElement("div", _extends({}, filterDataAttrs(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.notice), {
442
+ }), showClearIcon && renderClearButton()), !!items.length && !showNotFound && renderList(), showNotFound && renderNotFound()), noticeText && /*#__PURE__*/React.createElement("div", _extends({}, filterDataAttrs(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.notice), {
308
443
  className: cn('notice', {
309
444
  error: verification === Verification.ERROR,
310
445
  success: verification === Verification.VALID
@@ -320,7 +455,8 @@ Search.propTypes = {
320
455
  submit: PropTypes.objectOf(PropTypes.string.isRequired),
321
456
  item: PropTypes.objectOf(PropTypes.string.isRequired),
322
457
  itemTitle: PropTypes.objectOf(PropTypes.string.isRequired),
323
- notice: PropTypes.objectOf(PropTypes.string.isRequired)
458
+ notice: PropTypes.objectOf(PropTypes.string.isRequired),
459
+ popularItem: PropTypes.objectOf(PropTypes.string.isRequired)
324
460
  }),
325
461
  value: PropTypes.string,
326
462
  label: PropTypes.string,
@@ -343,11 +479,23 @@ Search.propTypes = {
343
479
  input: PropTypes.string,
344
480
  listItemTitle: PropTypes.string,
345
481
  control: PropTypes.string,
346
- icon: PropTypes.string
482
+ icon: PropTypes.string,
483
+ textNotFound: PropTypes.string,
484
+ popularTitle: PropTypes.string,
485
+ popularItem: PropTypes.string
347
486
  }),
487
+ showNotFound: PropTypes.bool,
488
+ textNotFound: PropTypes.string,
489
+ popularItems: PropTypes.arrayOf(PropTypes.shape({
490
+ title: PropTypes.string.isRequired,
491
+ href: PropTypes.string,
492
+ icon: PropTypes.element
493
+ }).isRequired),
494
+ popularTitle: PropTypes.string,
348
495
  onChange: PropTypes.func,
349
496
  onSubmit: PropTypes.func,
350
497
  onBlur: PropTypes.func,
351
- onFocus: PropTypes.func
498
+ onFocus: PropTypes.func,
499
+ onPopularItemClick: PropTypes.func
352
500
  };
353
501
  export default Search;
@@ -213,10 +213,43 @@
213
213
  padding-right: 12px;
214
214
  padding-left: 12px;
215
215
  }
216
- .mfui-search__list-item_active {
216
+ .mfui-search__popular-item {
217
+ display: -webkit-box;
218
+ display: -ms-flexbox;
219
+ display: flex;
220
+ -webkit-box-align: center;
221
+ -ms-flex-align: center;
222
+ align-items: center;
223
+ min-height: 48px;
224
+ padding-right: 16px;
225
+ padding-left: 12px;
226
+ border-radius: 16px;
227
+ text-decoration: none;
228
+ background-color: var(--base);
229
+ cursor: pointer;
230
+ }
231
+ .mfui-search__popular-icon {
232
+ display: -webkit-box;
233
+ display: -ms-flexbox;
234
+ display: flex;
235
+ -ms-flex-negative: 0;
236
+ flex-shrink: 0;
237
+ -webkit-box-align: center;
238
+ -ms-flex-align: center;
239
+ align-items: center;
240
+ -webkit-box-pack: center;
241
+ -ms-flex-pack: center;
242
+ justify-content: center;
243
+ width: 32px;
244
+ height: 32px;
245
+ margin-right: 16px;
246
+ }
247
+ .mfui-search__list-item_active,
248
+ .mfui-search__popular-item_active {
217
249
  background-color: var(--spbSky0);
218
250
  }
219
- .mfui-search__item-title {
251
+ .mfui-search__item-title,
252
+ .mfui-search__popular-item-title {
220
253
  position: relative;
221
254
  -webkit-box-flex: 1;
222
255
  -ms-flex-positive: 1;
@@ -224,7 +257,8 @@
224
257
  overflow: hidden;
225
258
  white-space: nowrap;
226
259
  }
227
- .mfui-search__item-title:after {
260
+ .mfui-search__item-title:after,
261
+ .mfui-search__popular-item-title:after {
228
262
  content: '';
229
263
  position: absolute;
230
264
  top: 0;
@@ -234,7 +268,8 @@
234
268
  background: -webkit-gradient(linear, left top, right top, from(rgba(255, 255, 255, 0)), to(var(--base)));
235
269
  background: linear-gradient(to right, rgba(255, 255, 255, 0), var(--base));
236
270
  }
237
- .mfui-search__list-item_active .mfui-search__item-title:after {
271
+ .mfui-search__list-item_active .mfui-search__item-title:after,
272
+ .mfui-search__popular-item_active .mfui-search__popular-item-title:after {
238
273
  background: -webkit-gradient(linear, left top, right top, from(rgba(255, 255, 255, 0)), to(var(--spbSky0)));
239
274
  background: linear-gradient(to right, rgba(255, 255, 255, 0), var(--spbSky0));
240
275
  }
@@ -261,6 +296,23 @@
261
296
  align-items: center;
262
297
  height: 100%;
263
298
  }
299
+ .mfui-search__list_not-found {
300
+ cursor: default;
301
+ }
302
+ .mfui-search__not-found-text {
303
+ font-family: inherit;
304
+ padding: 12px 32px;
305
+ color: var(--spbSky3);
306
+ background-color: var(--base);
307
+ }
308
+ .mfui-search__not-found-text_show-popular {
309
+ padding: 0 0 16px 0;
310
+ border-bottom: 1px solid var(--spbSky2);
311
+ }
312
+ .mfui-search__popular-title {
313
+ padding-top: 16px;
314
+ padding-bottom: 8px;
315
+ }
264
316
  .mfui-search_open {
265
317
  z-index: 12;
266
318
  }
@@ -18,6 +18,14 @@ export declare type SearchItem = {
18
18
  /** Размер горизонтальных отступов элемента */
19
19
  paddings?: typeof SearchItemsPaddings[keyof typeof SearchItemsPaddings];
20
20
  };
21
+ export declare type PopularItem = {
22
+ /** Название элемента */
23
+ title: string;
24
+ /** Ссылка элемента */
25
+ href?: string;
26
+ /** Иконка элемента */
27
+ icon?: JSX.Element;
28
+ };
21
29
  export interface ISearchProps {
22
30
  /** Дополнительные data атрибуты к внутренним элементам */
23
31
  dataAttrs?: {
@@ -28,6 +36,7 @@ export interface ISearchProps {
28
36
  item?: Record<string, string>;
29
37
  itemTitle?: Record<string, string>;
30
38
  notice?: Record<string, string>;
39
+ popularItem?: Record<string, string>;
31
40
  };
32
41
  /** Значение */
33
42
  value?: string;
@@ -63,9 +72,20 @@ export interface ISearchProps {
63
72
  control?: string;
64
73
  icon?: string;
65
74
  submitButton?: string;
75
+ textNotFound?: string;
76
+ popularTitle?: string;
77
+ popularItem?: string;
66
78
  };
67
79
  /** Отображение загрузки */
68
80
  showLoader?: boolean;
81
+ /** Выводить результат, когда нет результатов поиска */
82
+ showNotFound?: boolean;
83
+ /** Текст, когда нет результатов поиска */
84
+ textNotFound?: string;
85
+ /** Популярные элементы (max 5) */
86
+ popularItems?: PopularItem[];
87
+ /** Заголовок перед популярными элементами */
88
+ popularTitle?: string;
69
89
  /** Обработчик изменения поля */
70
90
  onChange?: (value: string) => void;
71
91
  /** Обработчик нажатия на enter */
@@ -74,6 +94,8 @@ export interface ISearchProps {
74
94
  onBlur?: (e: React.FocusEvent<HTMLInputElement>) => void;
75
95
  /** Обработчик входа в фокус */
76
96
  onFocus?: (e: React.FocusEvent<HTMLInputElement>) => void;
97
+ /** Обработчик нажатия на популярный элемент */
98
+ onPopularItemClick?: (popularItemValue?: string) => void;
77
99
  }
78
100
  declare const Search: React.FC<ISearchProps>;
79
101
  export default Search;
@@ -11,6 +11,8 @@ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/sli
11
11
 
12
12
  var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
13
13
 
14
+ require("core-js/modules/es.array.slice.js");
15
+
14
16
  require("core-js/modules/es.regexp.exec.js");
15
17
 
16
18
  require("core-js/modules/es.string.replace.js");
@@ -33,6 +35,10 @@ var _lodash = _interopRequireDefault(require("lodash.debounce"));
33
35
 
34
36
  var PropTypes = _interopRequireWildcard(require("prop-types"));
35
37
 
38
+ var _Caption = _interopRequireDefault(require("../Caption/Caption"));
39
+
40
+ var _Header = _interopRequireDefault(require("../Header/Header"));
41
+
36
42
  var _Preloader = _interopRequireDefault(require("../Preloader/Preloader"));
37
43
 
38
44
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
@@ -67,6 +73,7 @@ var ClearIcon = function ClearIcon(props) {
67
73
  };
68
74
 
69
75
  var SEARCH_QUERY_REGEX = /[^A-Z-a-zА-ЯЁа-яё0-9]/g;
76
+ var MAX_POPULAR_ITEMS_LENGTH = 5;
70
77
  var Verification = {
71
78
  VALID: 'valid',
72
79
  ERROR: 'error'
@@ -101,10 +108,16 @@ var Search = function Search(_ref) {
101
108
  _ref$type = _ref.type,
102
109
  type = _ref$type === void 0 ? 'textfield' : _ref$type,
103
110
  showLoader = _ref.showLoader,
111
+ showNotFound = _ref.showNotFound,
112
+ textNotFound = _ref.textNotFound,
113
+ _ref$popularItems = _ref.popularItems,
114
+ popularItems = _ref$popularItems === void 0 ? [] : _ref$popularItems,
115
+ popularTitle = _ref.popularTitle,
104
116
  onChange = _ref.onChange,
105
117
  onSubmit = _ref.onSubmit,
106
118
  onBlur = _ref.onBlur,
107
- onFocus = _ref.onFocus;
119
+ onFocus = _ref.onFocus,
120
+ onPopularItemClick = _ref.onPopularItemClick;
108
121
 
109
122
  var _React$useState = _react["default"].useState(value),
110
123
  _React$useState2 = (0, _slicedToArray2["default"])(_React$useState, 2),
@@ -121,6 +134,11 @@ var Search = function Search(_ref) {
121
134
  isFocused = _React$useState6[0],
122
135
  setFocus = _React$useState6[1];
123
136
 
137
+ var _React$useState7 = _react["default"].useState(-1),
138
+ _React$useState8 = (0, _slicedToArray2["default"])(_React$useState7, 2),
139
+ popularActiveIndex = _React$useState8[0],
140
+ setPopularActiveIndex = _React$useState8[1];
141
+
124
142
  var debouncedOnChange = _react["default"].useRef((0, _lodash["default"])(function (inputValue) {
125
143
  return onChange && onChange(inputValue);
126
144
  }, changeDelay));
@@ -129,10 +147,15 @@ var Search = function Search(_ref) {
129
147
 
130
148
  var fieldNode = _react["default"].useRef(null);
131
149
 
150
+ var correctPopularItems = _react["default"].useMemo(function () {
151
+ return popularItems.slice(0, MAX_POPULAR_ITEMS_LENGTH);
152
+ }, [popularItems]);
153
+
132
154
  var isCompact = type === 'compact';
133
155
  var showClearIcon = isCompact && !!searchQuery;
134
156
  var showTextFieldSubmit = !hideIcon && !isCompact && !showLoader;
135
157
  var showAdditionalElement = !hideIcon || showLoader || showClearIcon;
158
+ var showPopularItems = !!correctPopularItems.length;
136
159
 
137
160
  var handleChange = _react["default"].useCallback(function (e) {
138
161
  var _e$target$value = e.target.value,
@@ -155,6 +178,12 @@ var Search = function Search(_ref) {
155
178
  };
156
179
  }, []);
157
180
 
181
+ var handlePopularItemHover = _react["default"].useCallback(function (index) {
182
+ return function () {
183
+ setPopularActiveIndex(index);
184
+ };
185
+ }, []);
186
+
158
187
  var handleSearchSubmit = _react["default"].useCallback(function () {
159
188
  onSubmit && searchQuery && onSubmit(searchQuery);
160
189
  }, [searchQuery, onSubmit]);
@@ -190,26 +219,94 @@ var Search = function Search(_ref) {
190
219
  }
191
220
  }, [activeIndex]);
192
221
 
193
- var handleKeyDown = _react["default"].useCallback(function (e) {
194
- if (e.key === 'ArrowDown' && activeIndex < items.length - 1) {
195
- setActiveIndex(function (index) {
196
- return index + 1;
197
- });
198
- e.preventDefault();
199
- } else if (e.key === 'ArrowUp' && activeIndex > -1) {
200
- setActiveIndex(function (index) {
201
- return index - 1;
202
- });
203
- e.preventDefault();
204
- } else if (e.key === 'Enter' && activeIndex > -1) {
205
- handleItemSubmit(activeIndex);
206
- e.preventDefault();
207
- } else if (e.key === 'Enter' && activeIndex === -1) {
208
- handleSearchSubmit();
222
+ var handlePopularItemMouseDown = _react["default"].useCallback(function (e) {
223
+ e.preventDefault();
224
+ }, []);
225
+
226
+ var handlePopularItemMouseUp = _react["default"].useCallback(function () {
227
+ var _a;
228
+
229
+ var chosenValue = popularItems[popularActiveIndex].title;
230
+ (_a = fieldNode === null || fieldNode === void 0 ? void 0 : fieldNode.current) === null || _a === void 0 ? void 0 : _a.blur();
231
+ onPopularItemClick === null || onPopularItemClick === void 0 ? void 0 : onPopularItemClick(chosenValue);
232
+ }, [onPopularItemClick, popularActiveIndex, popularItems]);
233
+
234
+ var handleKeyDownPopularItems = _react["default"].useCallback(function (e) {
235
+ var _a;
236
+
237
+ switch (true) {
238
+ case e.key === 'ArrowDown' && popularActiveIndex < popularItems.length - 1:
239
+ setPopularActiveIndex(function (index) {
240
+ return index + 1;
241
+ });
242
+ e.preventDefault();
243
+ break;
244
+
245
+ case e.key === 'ArrowUp' && popularActiveIndex > -1:
246
+ setPopularActiveIndex(function (index) {
247
+ return index - 1;
248
+ });
249
+ e.preventDefault();
250
+ break;
251
+
252
+ case e.key === 'Enter' && popularActiveIndex > -1:
253
+ {
254
+ var currentHref = (_a = popularItems[popularActiveIndex]) === null || _a === void 0 ? void 0 : _a.href;
255
+ handlePopularItemMouseUp();
256
+
257
+ if (currentHref) {
258
+ window.location.href = currentHref;
259
+ }
260
+
261
+ break;
262
+ }
263
+
264
+ default:
265
+ break;
266
+ }
267
+
268
+ return false;
269
+ }, [popularActiveIndex, popularItems, handlePopularItemMouseUp]);
270
+
271
+ var handleKeyDownItems = _react["default"].useCallback(function (e) {
272
+ switch (true) {
273
+ case e.key === 'ArrowDown' && activeIndex < items.length - 1:
274
+ setActiveIndex(function (index) {
275
+ return index + 1;
276
+ });
277
+ e.preventDefault();
278
+ break;
279
+
280
+ case e.key === 'ArrowUp' && activeIndex > -1:
281
+ setActiveIndex(function (index) {
282
+ return index - 1;
283
+ });
284
+ e.preventDefault();
285
+ break;
286
+
287
+ case e.key === 'Enter' && activeIndex > -1:
288
+ handleItemSubmit(activeIndex);
289
+ e.preventDefault();
290
+ break;
291
+
292
+ case e.key === 'Enter' && activeIndex === -1:
293
+ handleSearchSubmit();
294
+ break;
295
+
296
+ default:
297
+ break;
209
298
  }
210
299
 
211
300
  return false;
212
- }, [activeIndex, items, handleItemSubmit, handleSearchSubmit]);
301
+ }, [activeIndex, handleItemSubmit, handleSearchSubmit, items.length]);
302
+
303
+ var handleKeyDown = _react["default"].useCallback(function (e) {
304
+ if (showNotFound && showPopularItems) {
305
+ return handleKeyDownPopularItems(e);
306
+ }
307
+
308
+ return handleKeyDownItems(e);
309
+ }, [handleKeyDownItems, handleKeyDownPopularItems, showNotFound, showPopularItems]);
213
310
 
214
311
  var handleClearClick = function handleClearClick() {
215
312
  var _a; // TODO: После обновления TS до 4 версии, установить @types/lodash.debounce и убрать disable
@@ -310,6 +407,53 @@ var Search = function Search(_ref) {
310
407
  })));
311
408
  };
312
409
 
410
+ var renderPopularItems = function renderPopularItems() {
411
+ return /*#__PURE__*/_react["default"].createElement("div", {
412
+ className: cn('popular-list')
413
+ }, correctPopularItems.map(function (_ref3, i) {
414
+ var title = _ref3.title,
415
+ href = _ref3.href,
416
+ icon = _ref3.icon;
417
+ var Elem = href ? 'a' : 'div';
418
+ return /*#__PURE__*/_react["default"].createElement(Elem, (0, _extends2["default"])({}, (0, _uiHelpers.filterDataAttrs)(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.popularItem, i + 1), {
419
+ className: cn('popular-item', {
420
+ active: popularActiveIndex === i
421
+ }, [classes === null || classes === void 0 ? void 0 : classes.popularItem]),
422
+ key: i,
423
+ href: href,
424
+ onMouseDown: handlePopularItemMouseDown,
425
+ onMouseUp: handlePopularItemMouseUp,
426
+ onMouseEnter: handlePopularItemHover(i)
427
+ }), icon && /*#__PURE__*/_react["default"].createElement("span", {
428
+ className: cn('popular-icon')
429
+ }, icon), /*#__PURE__*/_react["default"].createElement(_Header["default"], {
430
+ className: cn('popular-item-title'),
431
+ as: "h5"
432
+ }, title));
433
+ }));
434
+ };
435
+
436
+ var renderNotFound = function renderNotFound() {
437
+ return /*#__PURE__*/_react["default"].createElement("div", {
438
+ className: cn('list', {
439
+ 'not-found': true
440
+ })
441
+ }, /*#__PURE__*/_react["default"].createElement("div", {
442
+ className: cn('list-inner')
443
+ }, /*#__PURE__*/_react["default"].createElement("div", {
444
+ className: cn('not-found-text', {
445
+ 'show-popular': showPopularItems
446
+ }, [classes === null || classes === void 0 ? void 0 : classes.textNotFound])
447
+ }, textNotFound || 'Ничего не нашлось'), showPopularItems && /*#__PURE__*/_react["default"].createElement("div", {
448
+ className: cn('popular-wrap')
449
+ }, /*#__PURE__*/_react["default"].createElement(_Caption["default"], {
450
+ className: cn('popular-title', [classes === null || classes === void 0 ? void 0 : classes.popularTitle]),
451
+ variant: "medium",
452
+ color: "gray",
453
+ hasMargin: false
454
+ }, popularTitle || 'Популярные действия'), renderPopularItems())));
455
+ };
456
+
313
457
  return /*#__PURE__*/_react["default"].createElement("div", (0, _extends2["default"])({}, (0, _uiHelpers.filterDataAttrs)(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.root), {
314
458
  className: cn({
315
459
  open: isFocused,
@@ -348,7 +492,7 @@ var Search = function Search(_ref) {
348
492
  delay: false,
349
493
  className: cn('loader'),
350
494
  sizeAll: "small"
351
- }), showClearIcon && renderClearButton()), !!items.length && renderList()), noticeText && /*#__PURE__*/_react["default"].createElement("div", (0, _extends2["default"])({}, (0, _uiHelpers.filterDataAttrs)(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.notice), {
495
+ }), showClearIcon && renderClearButton()), !!items.length && !showNotFound && renderList(), showNotFound && renderNotFound()), noticeText && /*#__PURE__*/_react["default"].createElement("div", (0, _extends2["default"])({}, (0, _uiHelpers.filterDataAttrs)(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.notice), {
352
496
  className: cn('notice', {
353
497
  error: verification === Verification.ERROR,
354
498
  success: verification === Verification.VALID
@@ -364,7 +508,8 @@ Search.propTypes = {
364
508
  submit: PropTypes.objectOf(PropTypes.string.isRequired),
365
509
  item: PropTypes.objectOf(PropTypes.string.isRequired),
366
510
  itemTitle: PropTypes.objectOf(PropTypes.string.isRequired),
367
- notice: PropTypes.objectOf(PropTypes.string.isRequired)
511
+ notice: PropTypes.objectOf(PropTypes.string.isRequired),
512
+ popularItem: PropTypes.objectOf(PropTypes.string.isRequired)
368
513
  }),
369
514
  value: PropTypes.string,
370
515
  label: PropTypes.string,
@@ -387,12 +532,24 @@ Search.propTypes = {
387
532
  input: PropTypes.string,
388
533
  listItemTitle: PropTypes.string,
389
534
  control: PropTypes.string,
390
- icon: PropTypes.string
535
+ icon: PropTypes.string,
536
+ textNotFound: PropTypes.string,
537
+ popularTitle: PropTypes.string,
538
+ popularItem: PropTypes.string
391
539
  }),
540
+ showNotFound: PropTypes.bool,
541
+ textNotFound: PropTypes.string,
542
+ popularItems: PropTypes.arrayOf(PropTypes.shape({
543
+ title: PropTypes.string.isRequired,
544
+ href: PropTypes.string,
545
+ icon: PropTypes.element
546
+ }).isRequired),
547
+ popularTitle: PropTypes.string,
392
548
  onChange: PropTypes.func,
393
549
  onSubmit: PropTypes.func,
394
550
  onBlur: PropTypes.func,
395
- onFocus: PropTypes.func
551
+ onFocus: PropTypes.func,
552
+ onPopularItemClick: PropTypes.func
396
553
  };
397
554
  var _default = Search;
398
555
  exports["default"] = _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@megafon/ui-core",
3
- "version": "5.5.0",
3
+ "version": "5.6.0",
4
4
  "files": [
5
5
  "dist",
6
6
  "styles"
@@ -96,5 +96,5 @@
96
96
  "react-popper": "^2.2.3",
97
97
  "swiper": "^6.5.6"
98
98
  },
99
- "gitHead": "34e8243c981c8304b0d857a04dd0e25b1a7dc762"
99
+ "gitHead": "bbacc7ca8173754967c131090ce860c050b2ce36"
100
100
  }