@megafon/ui-core 6.5.0 → 6.6.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.
@@ -130,7 +130,7 @@
130
130
  color: var(--content);
131
131
  font-size: 100%;
132
132
  font-family: inherit;
133
- line-height: 25px;
133
+ line-height: 24px;
134
134
  background-color: transparent;
135
135
  outline: none;
136
136
  -webkit-box-shadow: none;
@@ -140,6 +140,7 @@
140
140
  -webkit-appearance: none;
141
141
  -moz-appearance: none;
142
142
  appearance: none;
143
+ resize: none;
143
144
  caret-color: var(--brandGreen);
144
145
  }
145
146
  @media screen and (max-width: 767px) {
@@ -153,8 +154,10 @@
153
154
  display: block;
154
155
  overflow: auto;
155
156
  color: var(--spbSky3);
156
- -webkit-transition-duration: 0.2s, 0.01s;
157
- transition-duration: 0.2s, 0.01s;
157
+ -webkit-transition: scale 0.5s, top 0.01s, -webkit-transform 0.5s;
158
+ transition: scale 0.5s, top 0.01s, -webkit-transform 0.5s;
159
+ transition: transform 0.5s, scale 0.5s, top 0.01s;
160
+ transition: transform 0.5s, scale 0.5s, top 0.01s, -webkit-transform 0.5s;
158
161
  pointer-events: none;
159
162
  font-family: inherit;
160
163
  font-size: 15px;
@@ -338,6 +341,7 @@
338
341
  border-radius: 12px;
339
342
  background-color: var(--base);
340
343
  }
344
+ .mfui-v6-search_type_textfield .mfui-v6-search__control_focused,
341
345
  .mfui-v6-search_type_textfield .mfui-v6-search__control:hover {
342
346
  border-color: var(--content);
343
347
  }
@@ -348,15 +352,54 @@
348
352
  .mfui-v6-search_type_textfield .mfui-v6-search__field:focus + .mfui-v6-search__label {
349
353
  -webkit-transform: scale(0.8) translate(-12.5%, -75%);
350
354
  transform: scale(0.8) translate(-12.5%, -75%);
355
+ -webkit-transition: scale 0.2s, top 0.01s, -webkit-transform 0.2s;
356
+ transition: scale 0.2s, top 0.01s, -webkit-transform 0.2s;
357
+ transition: transform 0.2s, scale 0.2s, top 0.01s;
358
+ transition: transform 0.2s, scale 0.2s, top 0.01s, -webkit-transform 0.2s;
351
359
  }
352
360
  .mfui-v6-search_type_textfield .mfui-v6-search__field_labeled {
353
- padding-top: 22px;
361
+ padding-top: 25px;
362
+ padding-bottom: 9px;
354
363
  }
355
364
  .mfui-v6-search_type_textfield .mfui-v6-search__submit-icon {
356
365
  width: 32px;
357
366
  height: 32px;
358
367
  fill: var(--content);
359
368
  }
369
+ .mfui-v6-search_textarea .mfui-v6-search__control {
370
+ height: auto;
371
+ min-height: 60px;
372
+ }
373
+ .mfui-v6-search_textarea .mfui-v6-search__field {
374
+ -webkit-box-sizing: content-box;
375
+ box-sizing: content-box;
376
+ max-height: 204px;
377
+ padding-top: 17px;
378
+ padding-bottom: 9px;
379
+ scrollbar-width: thin;
380
+ scrollbar-color: var(--spbSky2) transparent;
381
+ }
382
+ .mfui-v6-search_textarea .mfui-v6-search__field_labeled {
383
+ padding-top: 25px;
384
+ padding-bottom: 9px;
385
+ }
386
+ .mfui-v6-search_textarea .mfui-v6-search__icons {
387
+ height: 58px;
388
+ margin-left: 8px;
389
+ }
390
+ .mfui-v6-search_textarea .mfui-v6-search__field_resized {
391
+ -webkit-transition: height 0.1s;
392
+ transition: height 0.1s;
393
+ will-change: height;
394
+ }
395
+ .mfui-v6-search__textarea-wrapper {
396
+ position: relative;
397
+ -webkit-box-flex: 1;
398
+ -ms-flex-positive: 1;
399
+ flex-grow: 1;
400
+ width: 100%;
401
+ overflow: hidden;
402
+ }
360
403
  .mfui-v6-search_type_textfield.mfui-v6-search_disabled .mfui-v6-search__control {
361
404
  border-color: var(--spbSky1);
362
405
  background-color: var(--spbSky1);
@@ -451,3 +494,32 @@
451
494
  .mfui-v6-search_success .mfui-v6-search__control {
452
495
  border-color: var(--brandGreen);
453
496
  }
497
+ .mfui-v6-search__resizer {
498
+ position: absolute;
499
+ right: 0;
500
+ bottom: 0;
501
+ display: -webkit-box;
502
+ display: -ms-flexbox;
503
+ display: flex;
504
+ -webkit-box-align: center;
505
+ -ms-flex-align: center;
506
+ align-items: center;
507
+ -webkit-box-pack: center;
508
+ -ms-flex-pack: center;
509
+ justify-content: center;
510
+ -webkit-box-sizing: content-box;
511
+ box-sizing: content-box;
512
+ width: 12px;
513
+ height: 12px;
514
+ padding: 4px;
515
+ cursor: ns-resize;
516
+ }
517
+ .mfui-v6-search__resizer svg {
518
+ -webkit-transition: stroke 0.3s;
519
+ transition: stroke 0.3s;
520
+ stroke: var(--spbSky2);
521
+ }
522
+ .mfui-v6-search__resizer:hover svg,
523
+ .mfui-v6-search__resizer:active svg {
524
+ stroke: var(--spbSky3);
525
+ }
@@ -1,5 +1,10 @@
1
1
  import React from 'react';
2
2
  import './Search.less';
3
+ export declare const MinTextareaHeight: {
4
+ readonly ONE_ROW: 24;
5
+ readonly THREE_ROWS: number;
6
+ };
7
+ declare type MinTextareaHeightType = typeof MinTextareaHeight[keyof typeof MinTextareaHeight];
3
8
  export declare const Verification: {
4
9
  readonly VALID: "valid";
5
10
  readonly ERROR: "error";
@@ -40,11 +45,14 @@ export interface ISearchProps {
40
45
  notice?: Record<string, string>;
41
46
  popularItem?: Record<string, string>;
42
47
  clearButton?: Record<string, string>;
48
+ resizer?: Record<string, string>;
43
49
  };
44
50
  /** Значение */
45
51
  value?: string;
46
52
  /** Вид отображения */
47
53
  type?: 'textfield' | 'compact';
54
+ /** Включить режим textarea */
55
+ textarea?: boolean;
48
56
  /** Заголовок поля */
49
57
  label?: string;
50
58
  /** HTML идентификатор поля поиска */
@@ -78,6 +86,7 @@ export interface ISearchProps {
78
86
  textNotFound?: string;
79
87
  popularTitle?: string;
80
88
  popularItem?: string;
89
+ resizer?: string;
81
90
  };
82
91
  /** Отображение загрузки */
83
92
  showLoader?: boolean;
@@ -91,14 +100,18 @@ export interface ISearchProps {
91
100
  popularTitle?: string;
92
101
  /** Ссылка на элемент списка с результатами */
93
102
  listRef?: React.Ref<HTMLDivElement>;
103
+ /** Минимальная высота textarea, px */
104
+ minTextareaHeight?: MinTextareaHeightType;
105
+ /** Показать хендлер для ресайза текстового поля */
106
+ showResizer?: boolean;
94
107
  /** Обработчик изменения поля */
95
108
  onChange?: (value: string) => void;
96
109
  /** Обработчик нажатия на enter */
97
110
  onSubmit?: (value: string) => void;
98
111
  /** Обработчик выхода из фокуса */
99
- onBlur?: (e: React.FocusEvent<HTMLInputElement>) => void;
112
+ onBlur?: (e: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
100
113
  /** Обработчик входа в фокус */
101
- onFocus?: (e: React.FocusEvent<HTMLInputElement>) => void;
114
+ onFocus?: (e: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
102
115
  /** Обработчик нажатия на популярный элемент */
103
116
  onPopularItemClick?: (popularItemValue?: string) => void;
104
117
  }
@@ -1,6 +1,7 @@
1
1
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
2
  import _extends from "@babel/runtime/helpers/extends";
3
3
  import "core-js/modules/es.array.slice.js";
4
+ import "core-js/modules/es.parse-float.js";
4
5
  import "core-js/modules/es.regexp.exec.js";
5
6
  import "core-js/modules/es.string.replace.js";
6
7
  import "core-js/modules/es.string.split.js";
@@ -11,10 +12,12 @@ import "core-js/modules/es.object.values.js";
11
12
  import React from 'react';
12
13
  import { cnCreate, filterDataAttrs } from '@megafon/ui-helpers';
13
14
  import debounce from 'lodash.debounce';
15
+ import throttle from 'lodash.throttle';
14
16
  import * as PropTypes from 'prop-types';
15
17
  import Caption from "../Caption/Caption";
16
18
  import Header from "../Header/Header";
17
19
  import Preloader from "../Preloader/Preloader";
20
+ import throttleTime from "../../constants/throttleTime";
18
21
  import "./Search.css";
19
22
 
20
23
  var SearchIcon16 = function SearchIcon16(props) {
@@ -42,8 +45,26 @@ var ClearIcon = function ClearIcon(props) {
42
45
  }));
43
46
  };
44
47
 
48
+ var ResizeIcon = function ResizeIcon(props) {
49
+ return /*#__PURE__*/React.createElement("svg", _extends({
50
+ width: 12,
51
+ height: 12
52
+ }, props), /*#__PURE__*/React.createElement("path", {
53
+ d: "M1 11L11 1M7 11l4-4"
54
+ }));
55
+ };
56
+
57
+ var DEFAULT_ROW_COUNT = 3;
58
+ var MAX_ROW_COUNT = 6;
59
+ var TEXTAREA_ROW_HEIGHT = 24;
60
+ var TEXTAREA_MAX_HEIGHT = TEXTAREA_ROW_HEIGHT * MAX_ROW_COUNT;
61
+ var DEFAULT_LABEL_TOP_POSITION = 16;
45
62
  var SEARCH_QUERY_REGEX = /[^A-Z-a-zА-ЯЁа-яё0-9]/g;
46
63
  var MAX_POPULAR_ITEMS_LENGTH = 5;
64
+ export var MinTextareaHeight = {
65
+ ONE_ROW: TEXTAREA_ROW_HEIGHT,
66
+ THREE_ROWS: TEXTAREA_ROW_HEIGHT * DEFAULT_ROW_COUNT
67
+ };
47
68
  export var Verification = {
48
69
  VALID: 'valid',
49
70
  ERROR: 'error'
@@ -75,6 +96,7 @@ var Search = function Search(_ref) {
75
96
  classes = _ref.classes,
76
97
  _ref$type = _ref.type,
77
98
  type = _ref$type === void 0 ? 'textfield' : _ref$type,
99
+ textarea = _ref.textarea,
78
100
  showLoader = _ref.showLoader,
79
101
  showNotFound = _ref.showNotFound,
80
102
  textNotFound = _ref.textNotFound,
@@ -82,6 +104,9 @@ var Search = function Search(_ref) {
82
104
  popularItems = _ref$popularItems === void 0 ? [] : _ref$popularItems,
83
105
  popularTitle = _ref.popularTitle,
84
106
  listRef = _ref.listRef,
107
+ _ref$minTextareaHeigh = _ref.minTextareaHeight,
108
+ minTextareaHeight = _ref$minTextareaHeigh === void 0 ? MinTextareaHeight.ONE_ROW : _ref$minTextareaHeigh,
109
+ showResizer = _ref.showResizer,
85
110
  onChange = _ref.onChange,
86
111
  onSubmit = _ref.onSubmit,
87
112
  onBlur = _ref.onBlur,
@@ -108,19 +133,68 @@ var Search = function Search(_ref) {
108
133
  popularActiveIndex = _React$useState8[0],
109
134
  setPopularActiveIndex = _React$useState8[1];
110
135
 
136
+ var _React$useState9 = React.useState(TEXTAREA_ROW_HEIGHT),
137
+ _React$useState10 = _slicedToArray(_React$useState9, 2),
138
+ textareaHeight = _React$useState10[0],
139
+ setTextareaHeight = _React$useState10[1];
140
+
141
+ var _React$useState11 = React.useState(false),
142
+ _React$useState12 = _slicedToArray(_React$useState11, 2),
143
+ isTextareaResizing = _React$useState12[0],
144
+ setIsTextareaResizing = _React$useState12[1];
145
+
146
+ var _React$useState13 = React.useState(false),
147
+ _React$useState14 = _slicedToArray(_React$useState13, 2),
148
+ isTextareaResized = _React$useState14[0],
149
+ setIsTextareaResized = _React$useState14[1];
150
+
111
151
  var debouncedOnChange = React.useRef(debounce(function (inputValue) {
112
152
  return onChange && onChange(inputValue);
113
153
  }, changeDelay));
114
154
  var highlightedItem = React.useRef(null);
115
- var fieldNode = React.useRef(null);
155
+ var containerRef = React.useRef(null);
156
+ var fieldRef = React.useRef(null);
157
+ var labelRef = React.useRef(null);
158
+ var resizerRef = React.useRef(null);
116
159
  var correctPopularItems = React.useMemo(function () {
117
160
  return popularItems.slice(0, MAX_POPULAR_ITEMS_LENGTH);
118
161
  }, [popularItems]);
119
162
  var isCompact = type === 'compact';
163
+ var isTextarea = textarea && !isCompact;
120
164
  var showClearIcon = isCompact && !!searchQuery;
121
165
  var showTextFieldSubmit = !hideIcon && !isCompact && !showLoader;
122
166
  var showAdditionalElement = !hideIcon || showLoader || showClearIcon;
123
167
  var showPopularItems = !!correctPopularItems.length;
168
+
169
+ var getFieldRef = function getFieldRef(node) {
170
+ if (!node) {
171
+ return;
172
+ }
173
+
174
+ fieldRef.current = node;
175
+ };
176
+
177
+ var textareaResize = React.useCallback(function () {
178
+ if (!isTextarea || !fieldRef.current || !containerRef.current || isTextareaResized) {
179
+ return;
180
+ }
181
+
182
+ fieldRef.current.style.height = "".concat(minTextareaHeight, "px");
183
+ var scrollHeight = fieldRef.current.scrollHeight;
184
+
185
+ var _window$getComputedSt = window.getComputedStyle(fieldRef.current),
186
+ paddingTop = _window$getComputedSt.paddingTop,
187
+ paddingBottom = _window$getComputedSt.paddingBottom;
188
+
189
+ var innerHeight = scrollHeight - parseFloat(paddingTop) - parseFloat(paddingBottom);
190
+
191
+ if (innerHeight >= TEXTAREA_MAX_HEIGHT) {
192
+ fieldRef.current.style.height = "".concat(TEXTAREA_MAX_HEIGHT, "px");
193
+ return;
194
+ }
195
+
196
+ fieldRef.current.style.height = "".concat(innerHeight, "px");
197
+ }, [isTextareaResized, minTextareaHeight, isTextarea]);
124
198
  var handleChange = React.useCallback(function (e) {
125
199
  var _e$target$value = e.target.value,
126
200
  inputValue = _e$target$value === void 0 ? '' : _e$target$value;
@@ -134,7 +208,9 @@ var Search = function Search(_ref) {
134
208
  // @ts-ignore
135
209
  debouncedOnChange.current(inputValue);
136
210
  }
137
- }, [changeDelay, onChange]);
211
+
212
+ textareaResize();
213
+ }, [changeDelay, onChange, textareaResize]);
138
214
  var handleHoverItem = React.useCallback(function (index) {
139
215
  return function () {
140
216
  setActiveIndex(index);
@@ -185,7 +261,7 @@ var Search = function Search(_ref) {
185
261
  var _a;
186
262
 
187
263
  var chosenValue = popularItems[popularActiveIndex].title;
188
- (_a = fieldNode === null || fieldNode === void 0 ? void 0 : fieldNode.current) === null || _a === void 0 ? void 0 : _a.blur();
264
+ (_a = fieldRef === null || fieldRef === void 0 ? void 0 : fieldRef.current) === null || _a === void 0 ? void 0 : _a.blur();
189
265
  onPopularItemClick === null || onPopularItemClick === void 0 ? void 0 : onPopularItemClick(chosenValue);
190
266
  }, [onPopularItemClick, popularActiveIndex, popularItems]);
191
267
  var handleKeyDownPopularItems = React.useCallback(function (e) {
@@ -210,6 +286,7 @@ var Search = function Search(_ref) {
210
286
  {
211
287
  var currentHref = (_a = popularItems[popularActiveIndex]) === null || _a === void 0 ? void 0 : _a.href;
212
288
  handlePopularItemMouseUp();
289
+ e.preventDefault();
213
290
 
214
291
  if (currentHref) {
215
292
  window.location.href = currentHref;
@@ -246,7 +323,12 @@ var Search = function Search(_ref) {
246
323
  break;
247
324
 
248
325
  case e.key === 'Enter' && activeIndex === -1:
326
+ if (isTextarea && e.shiftKey) {
327
+ return false;
328
+ }
329
+
249
330
  handleSearchSubmit();
331
+ e.preventDefault();
250
332
  break;
251
333
 
252
334
  default:
@@ -254,7 +336,7 @@ var Search = function Search(_ref) {
254
336
  }
255
337
 
256
338
  return false;
257
- }, [activeIndex, handleItemSubmit, handleSearchSubmit, items.length]);
339
+ }, [activeIndex, handleItemSubmit, handleSearchSubmit, items.length, isTextarea]);
258
340
  var handleKeyDown = React.useCallback(function (e) {
259
341
  if (showNotFound && showPopularItems) {
260
342
  return handleKeyDownPopularItems(e);
@@ -272,9 +354,68 @@ var Search = function Search(_ref) {
272
354
  !!debouncedOnChange.current && debouncedOnChange.current.cancel();
273
355
  onChange === null || onChange === void 0 ? void 0 : onChange('');
274
356
  setSearchQuery('');
275
- (_a = fieldNode === null || fieldNode === void 0 ? void 0 : fieldNode.current) === null || _a === void 0 ? void 0 : _a.focus();
357
+ (_a = fieldRef === null || fieldRef === void 0 ? void 0 : fieldRef.current) === null || _a === void 0 ? void 0 : _a.focus();
276
358
  };
277
359
 
360
+ var handleTextareaScroll = function handleTextareaScroll() {
361
+ if (!(fieldRef === null || fieldRef === void 0 ? void 0 : fieldRef.current) || !(labelRef === null || labelRef === void 0 ? void 0 : labelRef.current)) {
362
+ return;
363
+ }
364
+
365
+ var scrollTop = fieldRef.current.scrollTop;
366
+
367
+ if (!scrollTop) {
368
+ labelRef.current.style.top = '';
369
+ return;
370
+ }
371
+
372
+ labelRef.current.style.top = "".concat(DEFAULT_LABEL_TOP_POSITION - scrollTop, "px");
373
+ };
374
+
375
+ React.useEffect(function () {
376
+ textareaResize();
377
+ }, [textareaResize]);
378
+ React.useEffect(function () {
379
+ if (!resizerRef.current || !isTextarea || !showResizer) {
380
+ return;
381
+ }
382
+
383
+ var originalHeight;
384
+ var originalCoordinateY;
385
+ var handleResize = throttle(function (moveEvent) {
386
+ var currentCoordinateY = moveEvent.clientY || moveEvent.touches[0].clientY;
387
+ var resizeHeight = originalHeight + (currentCoordinateY - originalCoordinateY);
388
+ var updatedHeight = resizeHeight < minTextareaHeight ? minTextareaHeight : resizeHeight;
389
+ setTextareaHeight(updatedHeight);
390
+ setIsTextareaResized(true);
391
+ }, throttleTime.resizeTextarea);
392
+
393
+ var handleResizeCancel = function handleResizeCancel() {
394
+ setIsTextareaResizing(false);
395
+ window.removeEventListener('mousemove', handleResize);
396
+ window.removeEventListener('touchmove', handleResize);
397
+ window.removeEventListener('mouseup', handleResizeCancel);
398
+ window.removeEventListener('touchend', handleResizeCancel);
399
+ };
400
+
401
+ var handleStartResize = function handleStartResize(event) {
402
+ if (!fieldRef.current) {
403
+ return;
404
+ }
405
+
406
+ setIsTextareaResizing(true);
407
+ event.preventDefault();
408
+ originalHeight = parseFloat(getComputedStyle(fieldRef.current).getPropertyValue('height').replace('px', ''));
409
+ originalCoordinateY = event.clientY || event.touches[0].clientY;
410
+ window.addEventListener('mousemove', handleResize);
411
+ window.addEventListener('touchmove', handleResize);
412
+ window.addEventListener('mouseup', handleResizeCancel);
413
+ window.addEventListener('touchend', handleResizeCancel);
414
+ };
415
+
416
+ resizerRef.current.addEventListener('mousedown', handleStartResize);
417
+ resizerRef.current.addEventListener('touchstart', handleStartResize);
418
+ }, [isTextarea, minTextareaHeight, showResizer]);
278
419
  React.useEffect(function () {
279
420
  return setSearchQuery(value);
280
421
  }, [value]);
@@ -407,17 +548,14 @@ var Search = function Search(_ref) {
407
548
  }, popularTitle || 'Популярные действия'), renderPopularItems())));
408
549
  };
409
550
 
410
- return /*#__PURE__*/React.createElement("div", _extends({}, filterDataAttrs(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.root), {
411
- className: cn({
412
- open: isFocused,
413
- disabled: disabled,
414
- type: type,
415
- error: verification === Verification.ERROR,
416
- success: verification === Verification.VALID
417
- }, [className])
418
- }), /*#__PURE__*/React.createElement("div", _extends({}, filterDataAttrs(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.control), {
419
- className: cn('control', [classes === null || classes === void 0 ? void 0 : classes.control])
420
- }), isCompact && renderSubmitButton(), /*#__PURE__*/React.createElement("input", _extends({}, filterDataAttrs(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.searchField), {
551
+ var renderLabel = /*#__PURE__*/React.createElement("label", {
552
+ className: cn('label', [classes === null || classes === void 0 ? void 0 : classes.label]),
553
+ htmlFor: searchId,
554
+ ref: labelRef
555
+ }, label, required && /*#__PURE__*/React.createElement("span", {
556
+ className: cn('require-mark')
557
+ }, "*"));
558
+ var renderInput = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("input", _extends({}, filterDataAttrs(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.searchField), {
421
559
  id: searchId,
422
560
  className: cn('field', {
423
561
  filled: !!searchQuery,
@@ -433,13 +571,48 @@ var Search = function Search(_ref) {
433
571
  disabled: disabled,
434
572
  type: "text",
435
573
  autoComplete: "off",
436
- ref: fieldNode
437
- })), label && /*#__PURE__*/React.createElement("label", {
438
- className: cn('label', [classes === null || classes === void 0 ? void 0 : classes.label]),
439
- htmlFor: searchId
440
- }, label, required && /*#__PURE__*/React.createElement("span", {
441
- className: cn('require-mark')
442
- }, "*")), showAdditionalElement && /*#__PURE__*/React.createElement("div", {
574
+ ref: getFieldRef
575
+ })), label && renderLabel);
576
+ var renderTextarea = /*#__PURE__*/React.createElement(React.Fragment, null, showResizer && /*#__PURE__*/React.createElement("div", _extends({
577
+ className: cn('resizer', [classes === null || classes === void 0 ? void 0 : classes.resizer]),
578
+ ref: resizerRef
579
+ }, filterDataAttrs(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.resizer)), /*#__PURE__*/React.createElement(ResizeIcon, null)), /*#__PURE__*/React.createElement("div", {
580
+ className: cn('textarea-wrapper')
581
+ }, /*#__PURE__*/React.createElement("textarea", _extends({}, filterDataAttrs(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.searchField), {
582
+ id: searchId,
583
+ className: cn('field', {
584
+ filled: !!searchQuery,
585
+ labeled: !!label
586
+ }, [classes === null || classes === void 0 ? void 0 : classes.input]),
587
+ style: {
588
+ height: "".concat(textareaHeight, "px")
589
+ },
590
+ placeholder: placeholder,
591
+ value: searchQuery,
592
+ onChange: handleChange,
593
+ onFocus: handleFocus,
594
+ onBlur: handleBlur,
595
+ onKeyDown: handleKeyDown,
596
+ onClick: handleClick,
597
+ onScroll: handleTextareaScroll,
598
+ autoComplete: "off",
599
+ ref: getFieldRef
600
+ })), label && renderLabel));
601
+ return /*#__PURE__*/React.createElement("div", _extends({}, filterDataAttrs(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.root), {
602
+ className: cn({
603
+ open: isFocused,
604
+ disabled: disabled,
605
+ type: type,
606
+ textarea: isTextarea,
607
+ error: verification === Verification.ERROR,
608
+ success: verification === Verification.VALID
609
+ }, [className])
610
+ }), /*#__PURE__*/React.createElement("div", _extends({}, filterDataAttrs(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.control), {
611
+ className: cn('control', {
612
+ focused: isTextareaResizing
613
+ }, [classes === null || classes === void 0 ? void 0 : classes.control]),
614
+ ref: containerRef
615
+ }), isCompact && renderSubmitButton(), isTextarea ? renderTextarea : renderInput, showAdditionalElement && /*#__PURE__*/React.createElement("div", {
443
616
  className: cn('icons')
444
617
  }, showTextFieldSubmit && renderSubmitButton(), showLoader && /*#__PURE__*/React.createElement(Preloader, {
445
618
  delay: false,
@@ -462,9 +635,12 @@ Search.propTypes = {
462
635
  item: PropTypes.objectOf(PropTypes.string.isRequired),
463
636
  itemTitle: PropTypes.objectOf(PropTypes.string.isRequired),
464
637
  notice: PropTypes.objectOf(PropTypes.string.isRequired),
465
- popularItem: PropTypes.objectOf(PropTypes.string.isRequired)
638
+ popularItem: PropTypes.objectOf(PropTypes.string.isRequired),
639
+ resizer: PropTypes.objectOf(PropTypes.string.isRequired)
466
640
  }),
467
641
  value: PropTypes.string,
642
+ type: PropTypes.oneOf(['textfield', 'compact']),
643
+ textarea: PropTypes.bool,
468
644
  label: PropTypes.string,
469
645
  searchId: PropTypes.string,
470
646
  placeholder: PropTypes.string,
@@ -489,7 +665,8 @@ Search.propTypes = {
489
665
  icon: PropTypes.string,
490
666
  textNotFound: PropTypes.string,
491
667
  popularTitle: PropTypes.string,
492
- popularItem: PropTypes.string
668
+ popularItem: PropTypes.string,
669
+ resizer: PropTypes.string
493
670
  }),
494
671
  showNotFound: PropTypes.bool,
495
672
  textNotFound: PropTypes.string,
@@ -502,6 +679,8 @@ Search.propTypes = {
502
679
  listRef: PropTypes.oneOfType([PropTypes.func, PropTypes.oneOfType([PropTypes.shape({
503
680
  current: PropTypes.elementType
504
681
  }), PropTypes.any])]),
682
+ minTextareaHeight: PropTypes.oneOf([MinTextareaHeight.ONE_ROW, MinTextareaHeight.THREE_ROWS]),
683
+ showResizer: PropTypes.bool,
505
684
  onChange: PropTypes.func,
506
685
  onSubmit: PropTypes.func,
507
686
  onBlur: PropTypes.func,
@@ -0,0 +1,4 @@
1
+ <svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M1 11L11 1" stroke-opacity="0.2" stroke-width="2" stroke-linecap="round"/>
3
+ <path d="M7 11L11 7" stroke-opacity="0.2" stroke-width="2" stroke-linecap="round"/>
4
+ </svg>
@@ -5,6 +5,7 @@ import "core-js/modules/es.parse-float.js";
5
5
  import "core-js/modules/es.regexp.exec.js";
6
6
  import "core-js/modules/es.string.replace.js";
7
7
  import "core-js/modules/es.object.get-own-property-descriptor.js";
8
+ import "core-js/modules/web.timers.js";
8
9
  import "core-js/modules/es.array.concat.js";
9
10
  import "core-js/modules/es.object.values.js";
10
11
 
@@ -63,6 +64,10 @@ var ResizeIcon = function ResizeIcon(props) {
63
64
  }));
64
65
  };
65
66
 
67
+ var isElement = function isElement(target) {
68
+ return target instanceof HTMLElement || target instanceof SVGElement || target instanceof SVGPathElement;
69
+ };
70
+
66
71
  var TEXTAREA_MAX_HEIGHT = 144;
67
72
  var DEFAULT_LABEL_TOP_POSITION = 16;
68
73
  var DEFAULT_ROW_COUNT = 3;
@@ -172,6 +177,11 @@ var TextField = function TextField(_ref) {
172
177
  isTextareaResized = _useState14[0],
173
178
  setIsTextareaResized = _useState14[1];
174
179
 
180
+ var _useState15 = useState(null),
181
+ _useState16 = _slicedToArray(_useState15, 2),
182
+ clickedElement = _useState16[0],
183
+ setClickedElement = _useState16[1];
184
+
175
185
  var fieldNode = useRef();
176
186
  var labelRef = useRef(null);
177
187
  var resizerRef = useRef(null);
@@ -275,6 +285,15 @@ var TextField = function TextField(_ref) {
275
285
  onChange === null || onChange === void 0 ? void 0 : onChange(e);
276
286
  };
277
287
 
288
+ var handleMouseDown = function handleMouseDown(e) {
289
+ if (isElement(e.target)) {
290
+ setClickedElement(e.target);
291
+ return;
292
+ }
293
+
294
+ setClickedElement(null);
295
+ };
296
+
278
297
  var handleNoticeTransitionEnd = useCallback(function () {
279
298
  !noticeText && setCurrentNoticeText(noticeText);
280
299
  }, [noticeText]);
@@ -311,7 +330,9 @@ var TextField = function TextField(_ref) {
311
330
  });
312
331
  (_a = nativeInputValue === null || nativeInputValue === void 0 ? void 0 : nativeInputValue.set) === null || _a === void 0 ? void 0 : _a.call(field, '');
313
332
  field === null || field === void 0 ? void 0 : field.dispatchEvent(inputEvent);
314
- field === null || field === void 0 ? void 0 : field.focus();
333
+ setTimeout(function () {
334
+ return field === null || field === void 0 ? void 0 : field.focus();
335
+ });
315
336
  }
316
337
 
317
338
  onIconClick === null || onIconClick === void 0 ? void 0 : onIconClick(e);
@@ -320,8 +341,16 @@ var TextField = function TextField(_ref) {
320
341
  onFocus === null || onFocus === void 0 ? void 0 : onFocus(e);
321
342
  }, [onFocus]);
322
343
  var handleBlur = useCallback(function (e) {
344
+ var _a;
345
+
346
+ setClickedElement(null);
347
+
348
+ if ((_a = clickedElement === null || clickedElement === void 0 ? void 0 : clickedElement.closest('div')) === null || _a === void 0 ? void 0 : _a.classList.contains(cn('icon-box'))) {
349
+ return;
350
+ }
351
+
323
352
  onBlur === null || onBlur === void 0 ? void 0 : onBlur(e);
324
- }, [onBlur]);
353
+ }, [onBlur, clickedElement]);
325
354
  var handleBeforeMaskChange = useCallback(function (newState, oldState, inputedValue) {
326
355
  return onBeforeMaskChange && onBeforeMaskChange(inputedValue, newState, oldState);
327
356
  }, [onBeforeMaskChange]);
@@ -475,7 +504,8 @@ var TextField = function TextField(_ref) {
475
504
  }), /*#__PURE__*/React.createElement("div", {
476
505
  className: cn('field-wrapper', {
477
506
  textarea: textarea && textareaType
478
- })
507
+ }),
508
+ onMouseDown: handleMouseDown
479
509
  }, renderField(), textareaType === TextareaTypes.FLEXIBLE && !hideResizeButton && /*#__PURE__*/React.createElement("div", _extends({
480
510
  className: cn('resizer'),
481
511
  ref: resizerRef
@@ -130,7 +130,7 @@
130
130
  color: var(--content);
131
131
  font-size: 100%;
132
132
  font-family: inherit;
133
- line-height: 25px;
133
+ line-height: 24px;
134
134
  background-color: transparent;
135
135
  outline: none;
136
136
  -webkit-box-shadow: none;
@@ -140,6 +140,7 @@
140
140
  -webkit-appearance: none;
141
141
  -moz-appearance: none;
142
142
  appearance: none;
143
+ resize: none;
143
144
  caret-color: var(--brandGreen);
144
145
  }
145
146
  @media screen and (max-width: 767px) {
@@ -153,8 +154,10 @@
153
154
  display: block;
154
155
  overflow: auto;
155
156
  color: var(--spbSky3);
156
- -webkit-transition-duration: 0.2s, 0.01s;
157
- transition-duration: 0.2s, 0.01s;
157
+ -webkit-transition: scale 0.5s, top 0.01s, -webkit-transform 0.5s;
158
+ transition: scale 0.5s, top 0.01s, -webkit-transform 0.5s;
159
+ transition: transform 0.5s, scale 0.5s, top 0.01s;
160
+ transition: transform 0.5s, scale 0.5s, top 0.01s, -webkit-transform 0.5s;
158
161
  pointer-events: none;
159
162
  font-family: inherit;
160
163
  font-size: 15px;
@@ -338,6 +341,7 @@
338
341
  border-radius: 12px;
339
342
  background-color: var(--base);
340
343
  }
344
+ .mfui-v6-search_type_textfield .mfui-v6-search__control_focused,
341
345
  .mfui-v6-search_type_textfield .mfui-v6-search__control:hover {
342
346
  border-color: var(--content);
343
347
  }
@@ -348,15 +352,54 @@
348
352
  .mfui-v6-search_type_textfield .mfui-v6-search__field:focus + .mfui-v6-search__label {
349
353
  -webkit-transform: scale(0.8) translate(-12.5%, -75%);
350
354
  transform: scale(0.8) translate(-12.5%, -75%);
355
+ -webkit-transition: scale 0.2s, top 0.01s, -webkit-transform 0.2s;
356
+ transition: scale 0.2s, top 0.01s, -webkit-transform 0.2s;
357
+ transition: transform 0.2s, scale 0.2s, top 0.01s;
358
+ transition: transform 0.2s, scale 0.2s, top 0.01s, -webkit-transform 0.2s;
351
359
  }
352
360
  .mfui-v6-search_type_textfield .mfui-v6-search__field_labeled {
353
- padding-top: 22px;
361
+ padding-top: 25px;
362
+ padding-bottom: 9px;
354
363
  }
355
364
  .mfui-v6-search_type_textfield .mfui-v6-search__submit-icon {
356
365
  width: 32px;
357
366
  height: 32px;
358
367
  fill: var(--content);
359
368
  }
369
+ .mfui-v6-search_textarea .mfui-v6-search__control {
370
+ height: auto;
371
+ min-height: 60px;
372
+ }
373
+ .mfui-v6-search_textarea .mfui-v6-search__field {
374
+ -webkit-box-sizing: content-box;
375
+ box-sizing: content-box;
376
+ max-height: 204px;
377
+ padding-top: 17px;
378
+ padding-bottom: 9px;
379
+ scrollbar-width: thin;
380
+ scrollbar-color: var(--spbSky2) transparent;
381
+ }
382
+ .mfui-v6-search_textarea .mfui-v6-search__field_labeled {
383
+ padding-top: 25px;
384
+ padding-bottom: 9px;
385
+ }
386
+ .mfui-v6-search_textarea .mfui-v6-search__icons {
387
+ height: 58px;
388
+ margin-left: 8px;
389
+ }
390
+ .mfui-v6-search_textarea .mfui-v6-search__field_resized {
391
+ -webkit-transition: height 0.1s;
392
+ transition: height 0.1s;
393
+ will-change: height;
394
+ }
395
+ .mfui-v6-search__textarea-wrapper {
396
+ position: relative;
397
+ -webkit-box-flex: 1;
398
+ -ms-flex-positive: 1;
399
+ flex-grow: 1;
400
+ width: 100%;
401
+ overflow: hidden;
402
+ }
360
403
  .mfui-v6-search_type_textfield.mfui-v6-search_disabled .mfui-v6-search__control {
361
404
  border-color: var(--spbSky1);
362
405
  background-color: var(--spbSky1);
@@ -451,3 +494,32 @@
451
494
  .mfui-v6-search_success .mfui-v6-search__control {
452
495
  border-color: var(--brandGreen);
453
496
  }
497
+ .mfui-v6-search__resizer {
498
+ position: absolute;
499
+ right: 0;
500
+ bottom: 0;
501
+ display: -webkit-box;
502
+ display: -ms-flexbox;
503
+ display: flex;
504
+ -webkit-box-align: center;
505
+ -ms-flex-align: center;
506
+ align-items: center;
507
+ -webkit-box-pack: center;
508
+ -ms-flex-pack: center;
509
+ justify-content: center;
510
+ -webkit-box-sizing: content-box;
511
+ box-sizing: content-box;
512
+ width: 12px;
513
+ height: 12px;
514
+ padding: 4px;
515
+ cursor: ns-resize;
516
+ }
517
+ .mfui-v6-search__resizer svg {
518
+ -webkit-transition: stroke 0.3s;
519
+ transition: stroke 0.3s;
520
+ stroke: var(--spbSky2);
521
+ }
522
+ .mfui-v6-search__resizer:hover svg,
523
+ .mfui-v6-search__resizer:active svg {
524
+ stroke: var(--spbSky3);
525
+ }
@@ -1,5 +1,10 @@
1
1
  import React from 'react';
2
2
  import './Search.less';
3
+ export declare const MinTextareaHeight: {
4
+ readonly ONE_ROW: 24;
5
+ readonly THREE_ROWS: number;
6
+ };
7
+ declare type MinTextareaHeightType = typeof MinTextareaHeight[keyof typeof MinTextareaHeight];
3
8
  export declare const Verification: {
4
9
  readonly VALID: "valid";
5
10
  readonly ERROR: "error";
@@ -40,11 +45,14 @@ export interface ISearchProps {
40
45
  notice?: Record<string, string>;
41
46
  popularItem?: Record<string, string>;
42
47
  clearButton?: Record<string, string>;
48
+ resizer?: Record<string, string>;
43
49
  };
44
50
  /** Значение */
45
51
  value?: string;
46
52
  /** Вид отображения */
47
53
  type?: 'textfield' | 'compact';
54
+ /** Включить режим textarea */
55
+ textarea?: boolean;
48
56
  /** Заголовок поля */
49
57
  label?: string;
50
58
  /** HTML идентификатор поля поиска */
@@ -78,6 +86,7 @@ export interface ISearchProps {
78
86
  textNotFound?: string;
79
87
  popularTitle?: string;
80
88
  popularItem?: string;
89
+ resizer?: string;
81
90
  };
82
91
  /** Отображение загрузки */
83
92
  showLoader?: boolean;
@@ -91,14 +100,18 @@ export interface ISearchProps {
91
100
  popularTitle?: string;
92
101
  /** Ссылка на элемент списка с результатами */
93
102
  listRef?: React.Ref<HTMLDivElement>;
103
+ /** Минимальная высота textarea, px */
104
+ minTextareaHeight?: MinTextareaHeightType;
105
+ /** Показать хендлер для ресайза текстового поля */
106
+ showResizer?: boolean;
94
107
  /** Обработчик изменения поля */
95
108
  onChange?: (value: string) => void;
96
109
  /** Обработчик нажатия на enter */
97
110
  onSubmit?: (value: string) => void;
98
111
  /** Обработчик выхода из фокуса */
99
- onBlur?: (e: React.FocusEvent<HTMLInputElement>) => void;
112
+ onBlur?: (e: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
100
113
  /** Обработчик входа в фокус */
101
- onFocus?: (e: React.FocusEvent<HTMLInputElement>) => void;
114
+ onFocus?: (e: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
102
115
  /** Обработчик нажатия на популярный элемент */
103
116
  onPopularItemClick?: (popularItemValue?: string) => void;
104
117
  }
@@ -5,7 +5,7 @@ function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "functi
5
5
  Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports["default"] = exports.SearchItemsPaddings = exports.Verification = void 0;
8
+ exports["default"] = exports.SearchItemsPaddings = exports.Verification = exports.MinTextareaHeight = void 0;
9
9
 
10
10
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
11
11
 
@@ -13,6 +13,8 @@ var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")
13
13
 
14
14
  require("core-js/modules/es.array.slice.js");
15
15
 
16
+ require("core-js/modules/es.parse-float.js");
17
+
16
18
  require("core-js/modules/es.regexp.exec.js");
17
19
 
18
20
  require("core-js/modules/es.string.replace.js");
@@ -33,6 +35,8 @@ var _uiHelpers = require("@megafon/ui-helpers");
33
35
 
34
36
  var _lodash = _interopRequireDefault(require("lodash.debounce"));
35
37
 
38
+ var _lodash2 = _interopRequireDefault(require("lodash.throttle"));
39
+
36
40
  var PropTypes = _interopRequireWildcard(require("prop-types"));
37
41
 
38
42
  var _Caption = _interopRequireDefault(require("../Caption/Caption"));
@@ -41,6 +45,8 @@ var _Header = _interopRequireDefault(require("../Header/Header"));
41
45
 
42
46
  var _Preloader = _interopRequireDefault(require("../Preloader/Preloader"));
43
47
 
48
+ var _throttleTime = _interopRequireDefault(require("../../constants/throttleTime"));
49
+
44
50
  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); }
45
51
 
46
52
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
@@ -72,8 +78,27 @@ var ClearIcon = function ClearIcon(props) {
72
78
  }));
73
79
  };
74
80
 
81
+ var ResizeIcon = function ResizeIcon(props) {
82
+ return /*#__PURE__*/_react["default"].createElement("svg", (0, _extends2["default"])({
83
+ width: 12,
84
+ height: 12
85
+ }, props), /*#__PURE__*/_react["default"].createElement("path", {
86
+ d: "M1 11L11 1M7 11l4-4"
87
+ }));
88
+ };
89
+
90
+ var DEFAULT_ROW_COUNT = 3;
91
+ var MAX_ROW_COUNT = 6;
92
+ var TEXTAREA_ROW_HEIGHT = 24;
93
+ var TEXTAREA_MAX_HEIGHT = TEXTAREA_ROW_HEIGHT * MAX_ROW_COUNT;
94
+ var DEFAULT_LABEL_TOP_POSITION = 16;
75
95
  var SEARCH_QUERY_REGEX = /[^A-Z-a-zА-ЯЁа-яё0-9]/g;
76
96
  var MAX_POPULAR_ITEMS_LENGTH = 5;
97
+ var MinTextareaHeight = {
98
+ ONE_ROW: TEXTAREA_ROW_HEIGHT,
99
+ THREE_ROWS: TEXTAREA_ROW_HEIGHT * DEFAULT_ROW_COUNT
100
+ };
101
+ exports.MinTextareaHeight = MinTextareaHeight;
77
102
  var Verification = {
78
103
  VALID: 'valid',
79
104
  ERROR: 'error'
@@ -107,6 +132,7 @@ var Search = function Search(_ref) {
107
132
  classes = _ref.classes,
108
133
  _ref$type = _ref.type,
109
134
  type = _ref$type === void 0 ? 'textfield' : _ref$type,
135
+ textarea = _ref.textarea,
110
136
  showLoader = _ref.showLoader,
111
137
  showNotFound = _ref.showNotFound,
112
138
  textNotFound = _ref.textNotFound,
@@ -114,6 +140,9 @@ var Search = function Search(_ref) {
114
140
  popularItems = _ref$popularItems === void 0 ? [] : _ref$popularItems,
115
141
  popularTitle = _ref.popularTitle,
116
142
  listRef = _ref.listRef,
143
+ _ref$minTextareaHeigh = _ref.minTextareaHeight,
144
+ minTextareaHeight = _ref$minTextareaHeigh === void 0 ? MinTextareaHeight.ONE_ROW : _ref$minTextareaHeigh,
145
+ showResizer = _ref.showResizer,
117
146
  onChange = _ref.onChange,
118
147
  onSubmit = _ref.onSubmit,
119
148
  onBlur = _ref.onBlur,
@@ -140,24 +169,76 @@ var Search = function Search(_ref) {
140
169
  popularActiveIndex = _React$useState8[0],
141
170
  setPopularActiveIndex = _React$useState8[1];
142
171
 
172
+ var _React$useState9 = _react["default"].useState(TEXTAREA_ROW_HEIGHT),
173
+ _React$useState10 = (0, _slicedToArray2["default"])(_React$useState9, 2),
174
+ textareaHeight = _React$useState10[0],
175
+ setTextareaHeight = _React$useState10[1];
176
+
177
+ var _React$useState11 = _react["default"].useState(false),
178
+ _React$useState12 = (0, _slicedToArray2["default"])(_React$useState11, 2),
179
+ isTextareaResizing = _React$useState12[0],
180
+ setIsTextareaResizing = _React$useState12[1];
181
+
182
+ var _React$useState13 = _react["default"].useState(false),
183
+ _React$useState14 = (0, _slicedToArray2["default"])(_React$useState13, 2),
184
+ isTextareaResized = _React$useState14[0],
185
+ setIsTextareaResized = _React$useState14[1];
186
+
143
187
  var debouncedOnChange = _react["default"].useRef((0, _lodash["default"])(function (inputValue) {
144
188
  return onChange && onChange(inputValue);
145
189
  }, changeDelay));
146
190
 
147
191
  var highlightedItem = _react["default"].useRef(null);
148
192
 
149
- var fieldNode = _react["default"].useRef(null);
193
+ var containerRef = _react["default"].useRef(null);
194
+
195
+ var fieldRef = _react["default"].useRef(null);
196
+
197
+ var labelRef = _react["default"].useRef(null);
198
+
199
+ var resizerRef = _react["default"].useRef(null);
150
200
 
151
201
  var correctPopularItems = _react["default"].useMemo(function () {
152
202
  return popularItems.slice(0, MAX_POPULAR_ITEMS_LENGTH);
153
203
  }, [popularItems]);
154
204
 
155
205
  var isCompact = type === 'compact';
206
+ var isTextarea = textarea && !isCompact;
156
207
  var showClearIcon = isCompact && !!searchQuery;
157
208
  var showTextFieldSubmit = !hideIcon && !isCompact && !showLoader;
158
209
  var showAdditionalElement = !hideIcon || showLoader || showClearIcon;
159
210
  var showPopularItems = !!correctPopularItems.length;
160
211
 
212
+ var getFieldRef = function getFieldRef(node) {
213
+ if (!node) {
214
+ return;
215
+ }
216
+
217
+ fieldRef.current = node;
218
+ };
219
+
220
+ var textareaResize = _react["default"].useCallback(function () {
221
+ if (!isTextarea || !fieldRef.current || !containerRef.current || isTextareaResized) {
222
+ return;
223
+ }
224
+
225
+ fieldRef.current.style.height = "".concat(minTextareaHeight, "px");
226
+ var scrollHeight = fieldRef.current.scrollHeight;
227
+
228
+ var _window$getComputedSt = window.getComputedStyle(fieldRef.current),
229
+ paddingTop = _window$getComputedSt.paddingTop,
230
+ paddingBottom = _window$getComputedSt.paddingBottom;
231
+
232
+ var innerHeight = scrollHeight - parseFloat(paddingTop) - parseFloat(paddingBottom);
233
+
234
+ if (innerHeight >= TEXTAREA_MAX_HEIGHT) {
235
+ fieldRef.current.style.height = "".concat(TEXTAREA_MAX_HEIGHT, "px");
236
+ return;
237
+ }
238
+
239
+ fieldRef.current.style.height = "".concat(innerHeight, "px");
240
+ }, [isTextareaResized, minTextareaHeight, isTextarea]);
241
+
161
242
  var handleChange = _react["default"].useCallback(function (e) {
162
243
  var _e$target$value = e.target.value,
163
244
  inputValue = _e$target$value === void 0 ? '' : _e$target$value;
@@ -171,7 +252,9 @@ var Search = function Search(_ref) {
171
252
  // @ts-ignore
172
253
  debouncedOnChange.current(inputValue);
173
254
  }
174
- }, [changeDelay, onChange]);
255
+
256
+ textareaResize();
257
+ }, [changeDelay, onChange, textareaResize]);
175
258
 
176
259
  var handleHoverItem = _react["default"].useCallback(function (index) {
177
260
  return function () {
@@ -232,7 +315,7 @@ var Search = function Search(_ref) {
232
315
  var _a;
233
316
 
234
317
  var chosenValue = popularItems[popularActiveIndex].title;
235
- (_a = fieldNode === null || fieldNode === void 0 ? void 0 : fieldNode.current) === null || _a === void 0 ? void 0 : _a.blur();
318
+ (_a = fieldRef === null || fieldRef === void 0 ? void 0 : fieldRef.current) === null || _a === void 0 ? void 0 : _a.blur();
236
319
  onPopularItemClick === null || onPopularItemClick === void 0 ? void 0 : onPopularItemClick(chosenValue);
237
320
  }, [onPopularItemClick, popularActiveIndex, popularItems]);
238
321
 
@@ -258,6 +341,7 @@ var Search = function Search(_ref) {
258
341
  {
259
342
  var currentHref = (_a = popularItems[popularActiveIndex]) === null || _a === void 0 ? void 0 : _a.href;
260
343
  handlePopularItemMouseUp();
344
+ e.preventDefault();
261
345
 
262
346
  if (currentHref) {
263
347
  window.location.href = currentHref;
@@ -295,7 +379,12 @@ var Search = function Search(_ref) {
295
379
  break;
296
380
 
297
381
  case e.key === 'Enter' && activeIndex === -1:
382
+ if (isTextarea && e.shiftKey) {
383
+ return false;
384
+ }
385
+
298
386
  handleSearchSubmit();
387
+ e.preventDefault();
299
388
  break;
300
389
 
301
390
  default:
@@ -303,7 +392,7 @@ var Search = function Search(_ref) {
303
392
  }
304
393
 
305
394
  return false;
306
- }, [activeIndex, handleItemSubmit, handleSearchSubmit, items.length]);
395
+ }, [activeIndex, handleItemSubmit, handleSearchSubmit, items.length, isTextarea]);
307
396
 
308
397
  var handleKeyDown = _react["default"].useCallback(function (e) {
309
398
  if (showNotFound && showPopularItems) {
@@ -322,9 +411,70 @@ var Search = function Search(_ref) {
322
411
  !!debouncedOnChange.current && debouncedOnChange.current.cancel();
323
412
  onChange === null || onChange === void 0 ? void 0 : onChange('');
324
413
  setSearchQuery('');
325
- (_a = fieldNode === null || fieldNode === void 0 ? void 0 : fieldNode.current) === null || _a === void 0 ? void 0 : _a.focus();
414
+ (_a = fieldRef === null || fieldRef === void 0 ? void 0 : fieldRef.current) === null || _a === void 0 ? void 0 : _a.focus();
326
415
  };
327
416
 
417
+ var handleTextareaScroll = function handleTextareaScroll() {
418
+ if (!(fieldRef === null || fieldRef === void 0 ? void 0 : fieldRef.current) || !(labelRef === null || labelRef === void 0 ? void 0 : labelRef.current)) {
419
+ return;
420
+ }
421
+
422
+ var scrollTop = fieldRef.current.scrollTop;
423
+
424
+ if (!scrollTop) {
425
+ labelRef.current.style.top = '';
426
+ return;
427
+ }
428
+
429
+ labelRef.current.style.top = "".concat(DEFAULT_LABEL_TOP_POSITION - scrollTop, "px");
430
+ };
431
+
432
+ _react["default"].useEffect(function () {
433
+ textareaResize();
434
+ }, [textareaResize]);
435
+
436
+ _react["default"].useEffect(function () {
437
+ if (!resizerRef.current || !isTextarea || !showResizer) {
438
+ return;
439
+ }
440
+
441
+ var originalHeight;
442
+ var originalCoordinateY;
443
+ var handleResize = (0, _lodash2["default"])(function (moveEvent) {
444
+ var currentCoordinateY = moveEvent.clientY || moveEvent.touches[0].clientY;
445
+ var resizeHeight = originalHeight + (currentCoordinateY - originalCoordinateY);
446
+ var updatedHeight = resizeHeight < minTextareaHeight ? minTextareaHeight : resizeHeight;
447
+ setTextareaHeight(updatedHeight);
448
+ setIsTextareaResized(true);
449
+ }, _throttleTime["default"].resizeTextarea);
450
+
451
+ var handleResizeCancel = function handleResizeCancel() {
452
+ setIsTextareaResizing(false);
453
+ window.removeEventListener('mousemove', handleResize);
454
+ window.removeEventListener('touchmove', handleResize);
455
+ window.removeEventListener('mouseup', handleResizeCancel);
456
+ window.removeEventListener('touchend', handleResizeCancel);
457
+ };
458
+
459
+ var handleStartResize = function handleStartResize(event) {
460
+ if (!fieldRef.current) {
461
+ return;
462
+ }
463
+
464
+ setIsTextareaResizing(true);
465
+ event.preventDefault();
466
+ originalHeight = parseFloat(getComputedStyle(fieldRef.current).getPropertyValue('height').replace('px', ''));
467
+ originalCoordinateY = event.clientY || event.touches[0].clientY;
468
+ window.addEventListener('mousemove', handleResize);
469
+ window.addEventListener('touchmove', handleResize);
470
+ window.addEventListener('mouseup', handleResizeCancel);
471
+ window.addEventListener('touchend', handleResizeCancel);
472
+ };
473
+
474
+ resizerRef.current.addEventListener('mousedown', handleStartResize);
475
+ resizerRef.current.addEventListener('touchstart', handleStartResize);
476
+ }, [isTextarea, minTextareaHeight, showResizer]);
477
+
328
478
  _react["default"].useEffect(function () {
329
479
  return setSearchQuery(value);
330
480
  }, [value]);
@@ -460,17 +610,15 @@ var Search = function Search(_ref) {
460
610
  }, popularTitle || 'Популярные действия'), renderPopularItems())));
461
611
  };
462
612
 
463
- return /*#__PURE__*/_react["default"].createElement("div", (0, _extends2["default"])({}, (0, _uiHelpers.filterDataAttrs)(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.root), {
464
- className: cn({
465
- open: isFocused,
466
- disabled: disabled,
467
- type: type,
468
- error: verification === Verification.ERROR,
469
- success: verification === Verification.VALID
470
- }, [className])
471
- }), /*#__PURE__*/_react["default"].createElement("div", (0, _extends2["default"])({}, (0, _uiHelpers.filterDataAttrs)(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.control), {
472
- className: cn('control', [classes === null || classes === void 0 ? void 0 : classes.control])
473
- }), isCompact && renderSubmitButton(), /*#__PURE__*/_react["default"].createElement("input", (0, _extends2["default"])({}, (0, _uiHelpers.filterDataAttrs)(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.searchField), {
613
+ var renderLabel = /*#__PURE__*/_react["default"].createElement("label", {
614
+ className: cn('label', [classes === null || classes === void 0 ? void 0 : classes.label]),
615
+ htmlFor: searchId,
616
+ ref: labelRef
617
+ }, label, required && /*#__PURE__*/_react["default"].createElement("span", {
618
+ className: cn('require-mark')
619
+ }, "*"));
620
+
621
+ var renderInput = /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, /*#__PURE__*/_react["default"].createElement("input", (0, _extends2["default"])({}, (0, _uiHelpers.filterDataAttrs)(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.searchField), {
474
622
  id: searchId,
475
623
  className: cn('field', {
476
624
  filled: !!searchQuery,
@@ -486,13 +634,50 @@ var Search = function Search(_ref) {
486
634
  disabled: disabled,
487
635
  type: "text",
488
636
  autoComplete: "off",
489
- ref: fieldNode
490
- })), label && /*#__PURE__*/_react["default"].createElement("label", {
491
- className: cn('label', [classes === null || classes === void 0 ? void 0 : classes.label]),
492
- htmlFor: searchId
493
- }, label, required && /*#__PURE__*/_react["default"].createElement("span", {
494
- className: cn('require-mark')
495
- }, "*")), showAdditionalElement && /*#__PURE__*/_react["default"].createElement("div", {
637
+ ref: getFieldRef
638
+ })), label && renderLabel);
639
+
640
+ var renderTextarea = /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, showResizer && /*#__PURE__*/_react["default"].createElement("div", (0, _extends2["default"])({
641
+ className: cn('resizer', [classes === null || classes === void 0 ? void 0 : classes.resizer]),
642
+ ref: resizerRef
643
+ }, (0, _uiHelpers.filterDataAttrs)(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.resizer)), /*#__PURE__*/_react["default"].createElement(ResizeIcon, null)), /*#__PURE__*/_react["default"].createElement("div", {
644
+ className: cn('textarea-wrapper')
645
+ }, /*#__PURE__*/_react["default"].createElement("textarea", (0, _extends2["default"])({}, (0, _uiHelpers.filterDataAttrs)(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.searchField), {
646
+ id: searchId,
647
+ className: cn('field', {
648
+ filled: !!searchQuery,
649
+ labeled: !!label
650
+ }, [classes === null || classes === void 0 ? void 0 : classes.input]),
651
+ style: {
652
+ height: "".concat(textareaHeight, "px")
653
+ },
654
+ placeholder: placeholder,
655
+ value: searchQuery,
656
+ onChange: handleChange,
657
+ onFocus: handleFocus,
658
+ onBlur: handleBlur,
659
+ onKeyDown: handleKeyDown,
660
+ onClick: handleClick,
661
+ onScroll: handleTextareaScroll,
662
+ autoComplete: "off",
663
+ ref: getFieldRef
664
+ })), label && renderLabel));
665
+
666
+ return /*#__PURE__*/_react["default"].createElement("div", (0, _extends2["default"])({}, (0, _uiHelpers.filterDataAttrs)(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.root), {
667
+ className: cn({
668
+ open: isFocused,
669
+ disabled: disabled,
670
+ type: type,
671
+ textarea: isTextarea,
672
+ error: verification === Verification.ERROR,
673
+ success: verification === Verification.VALID
674
+ }, [className])
675
+ }), /*#__PURE__*/_react["default"].createElement("div", (0, _extends2["default"])({}, (0, _uiHelpers.filterDataAttrs)(dataAttrs === null || dataAttrs === void 0 ? void 0 : dataAttrs.control), {
676
+ className: cn('control', {
677
+ focused: isTextareaResizing
678
+ }, [classes === null || classes === void 0 ? void 0 : classes.control]),
679
+ ref: containerRef
680
+ }), isCompact && renderSubmitButton(), isTextarea ? renderTextarea : renderInput, showAdditionalElement && /*#__PURE__*/_react["default"].createElement("div", {
496
681
  className: cn('icons')
497
682
  }, showTextFieldSubmit && renderSubmitButton(), showLoader && /*#__PURE__*/_react["default"].createElement(_Preloader["default"], {
498
683
  delay: false,
@@ -515,9 +700,12 @@ Search.propTypes = {
515
700
  item: PropTypes.objectOf(PropTypes.string.isRequired),
516
701
  itemTitle: PropTypes.objectOf(PropTypes.string.isRequired),
517
702
  notice: PropTypes.objectOf(PropTypes.string.isRequired),
518
- popularItem: PropTypes.objectOf(PropTypes.string.isRequired)
703
+ popularItem: PropTypes.objectOf(PropTypes.string.isRequired),
704
+ resizer: PropTypes.objectOf(PropTypes.string.isRequired)
519
705
  }),
520
706
  value: PropTypes.string,
707
+ type: PropTypes.oneOf(['textfield', 'compact']),
708
+ textarea: PropTypes.bool,
521
709
  label: PropTypes.string,
522
710
  searchId: PropTypes.string,
523
711
  placeholder: PropTypes.string,
@@ -542,7 +730,8 @@ Search.propTypes = {
542
730
  icon: PropTypes.string,
543
731
  textNotFound: PropTypes.string,
544
732
  popularTitle: PropTypes.string,
545
- popularItem: PropTypes.string
733
+ popularItem: PropTypes.string,
734
+ resizer: PropTypes.string
546
735
  }),
547
736
  showNotFound: PropTypes.bool,
548
737
  textNotFound: PropTypes.string,
@@ -555,6 +744,8 @@ Search.propTypes = {
555
744
  listRef: PropTypes.oneOfType([PropTypes.func, PropTypes.oneOfType([PropTypes.shape({
556
745
  current: PropTypes.elementType
557
746
  }), PropTypes.any])]),
747
+ minTextareaHeight: PropTypes.oneOf([MinTextareaHeight.ONE_ROW, MinTextareaHeight.THREE_ROWS]),
748
+ showResizer: PropTypes.bool,
558
749
  onChange: PropTypes.func,
559
750
  onSubmit: PropTypes.func,
560
751
  onBlur: PropTypes.func,
@@ -0,0 +1,4 @@
1
+ <svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M1 11L11 1" stroke-opacity="0.2" stroke-width="2" stroke-linecap="round"/>
3
+ <path d="M7 11L11 7" stroke-opacity="0.2" stroke-width="2" stroke-linecap="round"/>
4
+ </svg>
@@ -21,6 +21,8 @@ require("core-js/modules/es.string.replace.js");
21
21
 
22
22
  require("core-js/modules/es.object.get-own-property-descriptor.js");
23
23
 
24
+ require("core-js/modules/web.timers.js");
25
+
24
26
  require("core-js/modules/es.array.concat.js");
25
27
 
26
28
  require("core-js/modules/es.object.values.js");
@@ -90,6 +92,10 @@ var ResizeIcon = function ResizeIcon(props) {
90
92
  }));
91
93
  };
92
94
 
95
+ var isElement = function isElement(target) {
96
+ return target instanceof HTMLElement || target instanceof SVGElement || target instanceof SVGPathElement;
97
+ };
98
+
93
99
  var TEXTAREA_MAX_HEIGHT = 144;
94
100
  var DEFAULT_LABEL_TOP_POSITION = 16;
95
101
  var DEFAULT_ROW_COUNT = 3;
@@ -202,6 +208,11 @@ var TextField = function TextField(_ref) {
202
208
  isTextareaResized = _useState14[0],
203
209
  setIsTextareaResized = _useState14[1];
204
210
 
211
+ var _useState15 = (0, React.useState)(null),
212
+ _useState16 = (0, _slicedToArray2["default"])(_useState15, 2),
213
+ clickedElement = _useState16[0],
214
+ setClickedElement = _useState16[1];
215
+
205
216
  var fieldNode = (0, React.useRef)();
206
217
  var labelRef = (0, React.useRef)(null);
207
218
  var resizerRef = (0, React.useRef)(null);
@@ -305,6 +316,15 @@ var TextField = function TextField(_ref) {
305
316
  onChange === null || onChange === void 0 ? void 0 : onChange(e);
306
317
  };
307
318
 
319
+ var handleMouseDown = function handleMouseDown(e) {
320
+ if (isElement(e.target)) {
321
+ setClickedElement(e.target);
322
+ return;
323
+ }
324
+
325
+ setClickedElement(null);
326
+ };
327
+
308
328
  var handleNoticeTransitionEnd = (0, React.useCallback)(function () {
309
329
  !noticeText && setCurrentNoticeText(noticeText);
310
330
  }, [noticeText]);
@@ -341,7 +361,9 @@ var TextField = function TextField(_ref) {
341
361
  });
342
362
  (_a = nativeInputValue === null || nativeInputValue === void 0 ? void 0 : nativeInputValue.set) === null || _a === void 0 ? void 0 : _a.call(field, '');
343
363
  field === null || field === void 0 ? void 0 : field.dispatchEvent(inputEvent);
344
- field === null || field === void 0 ? void 0 : field.focus();
364
+ setTimeout(function () {
365
+ return field === null || field === void 0 ? void 0 : field.focus();
366
+ });
345
367
  }
346
368
 
347
369
  onIconClick === null || onIconClick === void 0 ? void 0 : onIconClick(e);
@@ -350,8 +372,16 @@ var TextField = function TextField(_ref) {
350
372
  onFocus === null || onFocus === void 0 ? void 0 : onFocus(e);
351
373
  }, [onFocus]);
352
374
  var handleBlur = (0, React.useCallback)(function (e) {
375
+ var _a;
376
+
377
+ setClickedElement(null);
378
+
379
+ if ((_a = clickedElement === null || clickedElement === void 0 ? void 0 : clickedElement.closest('div')) === null || _a === void 0 ? void 0 : _a.classList.contains(cn('icon-box'))) {
380
+ return;
381
+ }
382
+
353
383
  onBlur === null || onBlur === void 0 ? void 0 : onBlur(e);
354
- }, [onBlur]);
384
+ }, [onBlur, clickedElement]);
355
385
  var handleBeforeMaskChange = (0, React.useCallback)(function (newState, oldState, inputedValue) {
356
386
  return onBeforeMaskChange && onBeforeMaskChange(inputedValue, newState, oldState);
357
387
  }, [onBeforeMaskChange]);
@@ -501,7 +531,8 @@ var TextField = function TextField(_ref) {
501
531
  }), /*#__PURE__*/React.createElement("div", {
502
532
  className: cn('field-wrapper', {
503
533
  textarea: textarea && textareaType
504
- })
534
+ }),
535
+ onMouseDown: handleMouseDown
505
536
  }, renderField(), textareaType === TextareaTypes.FLEXIBLE && !hideResizeButton && /*#__PURE__*/React.createElement("div", (0, _extends2["default"])({
506
537
  className: cn('resizer'),
507
538
  ref: resizerRef
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@megafon/ui-core",
3
- "version": "6.5.0",
3
+ "version": "6.6.1",
4
4
  "files": [
5
5
  "dist",
6
6
  "styles"
@@ -99,5 +99,5 @@
99
99
  "react-popper": "^2.2.3",
100
100
  "swiper": "^6.5.6"
101
101
  },
102
- "gitHead": "9804a8e7196f90b0c310e4ee83988276ff7b58bc"
102
+ "gitHead": "c119b673112829f75ebb828789f183e7c061b715"
103
103
  }