@snack-uikit/fields 0.34.0 → 0.35.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,28 @@
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
+ ## 0.35.1 (2024-11-20)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **PDS-959:** do not change item structure in search mode ([1abca3d](https://github.com/cloud-ru-tech/snack-uikit/commit/1abca3d7ca7cef91665195ee51a06cc9af14461e))
12
+
13
+
14
+
15
+
16
+
17
+ # 0.35.0 (2024-11-19)
18
+
19
+
20
+ ### Features
21
+
22
+ * **FF-5887:** change prefix/postfix for FieldStepper ([b7aca2d](https://github.com/cloud-ru-tech/snack-uikit/commit/b7aca2da5ef613747599ff1cafaaa3de747be717))
23
+
24
+
25
+
26
+
27
+
6
28
  # 0.34.0 (2024-11-18)
7
29
 
8
30
 
@@ -37,5 +37,5 @@ export declare function useHandleDeleteItem(setValue: Handler): (item?: ItemWith
37
37
  /**
38
38
  * Четкий и нечеткий поиск среди айтемов по полям 'content.option', 'content.caption', 'content.description', 'label'
39
39
  */
40
- export declare function useSearch(items: ItemProps[], enableFuzzySearch?: boolean): (search: string) => import("@snack-uikit/list/dist/esm/components/Items").Item[] | import("@snack-uikit/list/dist/esm/components/Items").FlattenItem[];
40
+ export declare function useSearch(items: ItemProps[], enableFuzzySearch?: boolean): (search: string) => import("@snack-uikit/list/dist/esm/components/Items").Item[];
41
41
  export {};
@@ -20,6 +20,7 @@ const list_1 = require("@snack-uikit/list");
20
20
  const hooks_1 = require("../../hooks");
21
21
  const legacy_1 = require("./legacy");
22
22
  const utils_1 = require("./utils");
23
+ const filterItemsByFlattenIds_1 = require("./utils/filterItemsByFlattenIds");
23
24
  function useHandleOnKeyDown(_ref) {
24
25
  let {
25
26
  setOpen,
@@ -158,7 +159,7 @@ function useSearch(items) {
158
159
  });
159
160
  return Object.values(flattenItems);
160
161
  }, [items]);
161
- const filterFunction = (0, react_1.useCallback)(search => {
162
+ const filterFlattenFunction = (0, react_1.useCallback)(search => {
162
163
  if (!enableFuzzySearch) {
163
164
  return flattenItems.filter(item => [...COMMON_FIELDS_TO_SEARCH, 'label'].some(key => {
164
165
  const value = (0, utils_1.getValueByPath)(item, key);
@@ -168,5 +169,9 @@ function useSearch(items) {
168
169
  const searcher = new fuzzy_search_1.default(flattenItems, COMMON_FIELDS_TO_SEARCH, {});
169
170
  return searcher.search(search);
170
171
  }, [enableFuzzySearch, flattenItems]);
172
+ const filterFunction = (0, react_1.useCallback)(search => {
173
+ const filteredIds = filterFlattenFunction(search).map(item => item.id);
174
+ return (0, filterItemsByFlattenIds_1.filterItemsByFlattenIds)(items, filteredIds);
175
+ }, [filterFlattenFunction, items]);
171
176
  return (0, react_1.useCallback)(search => search.length >= DEFAULT_MIN_SEARCH_INPUT_LENGTH ? filterFunction(search) : items, [filterFunction, items]);
172
177
  }
@@ -0,0 +1,2 @@
1
+ import { ItemId, ItemProps } from '@snack-uikit/list';
2
+ export declare function filterItemsByFlattenIds(items: ItemProps[], ids: ItemId[]): import("@snack-uikit/list/dist/esm/components/Items").Item[];
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.filterItemsByFlattenIds = filterItemsByFlattenIds;
7
+ const isItemWithId = item => item.id !== undefined;
8
+ const isGroupItem = item => item.type === 'group';
9
+ function filterItemsByFlattenIds(items, ids) {
10
+ const filteredItems = [];
11
+ items.forEach(item => {
12
+ if (isItemWithId(item) && item.id && ids.includes(item.id)) {
13
+ filteredItems.push(item);
14
+ return;
15
+ }
16
+ if (isGroupItem(item)) {
17
+ const filteredSubItems = filterItemsByFlattenIds(item.items, ids);
18
+ filteredItems.push(Object.assign(Object.assign({}, item), {
19
+ items: filteredSubItems
20
+ }));
21
+ return;
22
+ }
23
+ });
24
+ return filteredItems;
25
+ }
@@ -18,6 +18,7 @@ Object.defineProperty(exports, "__esModule", {
18
18
  });
19
19
  exports.FieldStepper = void 0;
20
20
  const jsx_runtime_1 = require("react/jsx-runtime");
21
+ const classnames_1 = __importDefault(require("classnames"));
21
22
  const merge_refs_1 = __importDefault(require("merge-refs"));
22
23
  const react_1 = require("react");
23
24
  const button_1 = require("@snack-uikit/button");
@@ -42,6 +43,11 @@ const getDefaultValue = (min, max) => {
42
43
  }
43
44
  return 0;
44
45
  };
46
+ const SymbolWidth = {
47
+ s: 8,
48
+ m: 9,
49
+ l: 10
50
+ };
45
51
  exports.FieldStepper = (0, react_1.forwardRef)((_a, ref) => {
46
52
  var {
47
53
  id,
@@ -83,8 +89,6 @@ exports.FieldStepper = (0, react_1.forwardRef)((_a, ref) => {
83
89
  const [tooltip, setTooltip] = (0, react_1.useState)('');
84
90
  const timerRef = (0, react_1.useRef)();
85
91
  const inputRef = (0, react_1.useRef)(null);
86
- const minusButtonRef = (0, react_1.useRef)(null);
87
- const plusButtonRef = (0, react_1.useRef)(null);
88
92
  const isMinusButtonDisabled = typeof min === 'number' && value <= min || readonly || disabled;
89
93
  const isPlusButtonDisabled = typeof max === 'number' && value >= max || readonly || disabled;
90
94
  const prefixSettings = (0, hooks_1.usePrefix)({
@@ -130,17 +134,10 @@ exports.FieldStepper = (0, react_1.forwardRef)((_a, ref) => {
130
134
  onFocus === null || onFocus === void 0 ? void 0 : onFocus(event);
131
135
  };
132
136
  const handleInputChange = (value, event) => setValue(Number(value), event);
133
- const handleInputKeyDown = event => {
134
- var _a, _b;
137
+ const handleInputKeyDown = () => {
135
138
  if (readonly || disabled) {
136
139
  return;
137
140
  }
138
- if (event.key === 'ArrowRight' && !isPlusButtonDisabled) {
139
- (_a = plusButtonRef.current) === null || _a === void 0 ? void 0 : _a.focus();
140
- }
141
- if (event.key === 'ArrowLeft' && !isMinusButtonDisabled) {
142
- (_b = minusButtonRef.current) === null || _b === void 0 ? void 0 : _b.focus();
143
- }
144
141
  };
145
142
  const handleMinusButtonClick = e => {
146
143
  e.preventDefault();
@@ -152,25 +149,15 @@ exports.FieldStepper = (0, react_1.forwardRef)((_a, ref) => {
152
149
  e.stopPropagation();
153
150
  setValue(Math.max(Math.min(max, value + step), min));
154
151
  };
155
- const handleMinusButtonKeyDown = event => {
156
- var _a;
152
+ const handleMinusButtonKeyDown = () => {
157
153
  if (readonly || disabled) {
158
154
  return;
159
155
  }
160
- if (event.key === 'ArrowRight') {
161
- event.preventDefault();
162
- (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
163
- }
164
156
  };
165
- const handlePlusButtonKeyDown = event => {
166
- var _a;
157
+ const handlePlusButtonKeyDown = () => {
167
158
  if (readonly || disabled) {
168
159
  return;
169
160
  }
170
- if (event.key === 'ArrowLeft') {
171
- event.preventDefault();
172
- (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
173
- }
174
161
  };
175
162
  return (0, jsx_runtime_1.jsx)(FieldDecorator_1.FieldDecorator, Object.assign({
176
163
  className: className,
@@ -192,6 +179,7 @@ exports.FieldStepper = (0, react_1.forwardRef)((_a, ref) => {
192
179
  tip: tooltip,
193
180
  open: allowMoreThanLimits ? false : tooltipOpen,
194
181
  "data-test-id": 'field-stepper__limit-tooltip',
182
+ triggerClassName: styles_module_scss_1.default.trigger,
195
183
  children: (0, jsx_runtime_1.jsx)(helperComponents_1.FieldContainerPrivate, {
196
184
  size: size,
197
185
  validationState: fieldValidationState,
@@ -199,54 +187,68 @@ exports.FieldStepper = (0, react_1.forwardRef)((_a, ref) => {
199
187
  readonly: readonly,
200
188
  variant: constants_1.CONTAINER_VARIANT.SingleLine,
201
189
  inputRef: inputRef,
202
- prefix: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, {
203
- children: [(0, jsx_runtime_1.jsx)(button_1.ButtonFunction, {
204
- tabIndex: -1,
205
- ref: minusButtonRef,
206
- size: 'xs',
207
- className: styles_module_scss_1.default.button,
208
- icon: (0, jsx_runtime_1.jsx)(icons_1.MinusSVG, {}),
209
- onClick: handleMinusButtonClick,
210
- onKeyDown: handleMinusButtonKeyDown,
211
- disabled: isMinusButtonDisabled,
212
- "data-test-id": 'field-stepper__minus-button'
213
- }), prefixSettings.show && prefixSettings.render({
214
- key: prefixSettings.id
215
- })]
190
+ prefix: (0, jsx_runtime_1.jsx)(button_1.ButtonFunction, {
191
+ tabIndex: -1,
192
+ size: 'xs',
193
+ className: styles_module_scss_1.default.button,
194
+ icon: (0, jsx_runtime_1.jsx)(icons_1.MinusSVG, {}),
195
+ onClick: handleMinusButtonClick,
196
+ onKeyDown: handleMinusButtonKeyDown,
197
+ disabled: isMinusButtonDisabled,
198
+ "data-test-id": 'field-stepper__minus-button'
216
199
  }),
217
- postfix: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, {
218
- children: [postfixSettings.show && postfixSettings.render({
219
- key: postfixSettings.id
220
- }), (0, jsx_runtime_1.jsx)(button_1.ButtonFunction, {
221
- ref: plusButtonRef,
222
- tabIndex: -1,
223
- size: 'xs',
224
- className: styles_module_scss_1.default.button,
225
- icon: (0, jsx_runtime_1.jsx)(icons_1.PlusSVG, {}),
226
- onClick: handlePlusButtonClick,
227
- onKeyDown: handlePlusButtonKeyDown,
228
- disabled: isPlusButtonDisabled,
229
- "data-test-id": 'field-stepper__plus-button'
230
- })]
200
+ postfix: (0, jsx_runtime_1.jsx)(button_1.ButtonFunction, {
201
+ tabIndex: -1,
202
+ size: 'xs',
203
+ className: styles_module_scss_1.default.button,
204
+ icon: (0, jsx_runtime_1.jsx)(icons_1.PlusSVG, {}),
205
+ onClick: handlePlusButtonClick,
206
+ onKeyDown: handlePlusButtonKeyDown,
207
+ disabled: isPlusButtonDisabled,
208
+ "data-test-id": 'field-stepper__plus-button'
231
209
  }),
232
- children: (0, jsx_runtime_1.jsx)(input_private_1.InputPrivate, {
233
- ref: (0, merge_refs_1.default)(ref, inputRef),
234
- className: styles_module_scss_1.default.stepper,
235
- "data-size": size,
236
- value: String(value),
237
- tabIndex: 0,
238
- onKeyDown: handleInputKeyDown,
239
- onChange: handleInputChange,
240
- onBlur: handleInputBlur,
241
- onFocus: handleInputFocus,
242
- disabled: disabled,
243
- readonly: readonly,
244
- type: 'number',
245
- id: id,
246
- name: name,
247
- min: min,
248
- max: max,
249
- "data-test-id": 'field-stepper__input'
210
+ children: (0, jsx_runtime_1.jsxs)("div", {
211
+ className: styles_module_scss_1.default.wrap,
212
+ children: [(0, jsx_runtime_1.jsx)("div", {
213
+ className: (0, classnames_1.default)({
214
+ [styles_module_scss_1.default.prefixWrapper]: prefixSettings.show
215
+ }),
216
+ children: prefixSettings.show && prefixSettings.render({
217
+ key: prefixSettings.id
218
+ })
219
+ }), (0, jsx_runtime_1.jsx)("div", {
220
+ style: {
221
+ width: String(value).length * SymbolWidth[size],
222
+ maxWidth: '100%'
223
+ },
224
+ children: (0, jsx_runtime_1.jsx)(input_private_1.InputPrivate, {
225
+ ref: (0, merge_refs_1.default)(ref, inputRef),
226
+ className: styles_module_scss_1.default.stepper,
227
+ "data-size": size,
228
+ value: String(value),
229
+ tabIndex: 0,
230
+ onKeyDown: handleInputKeyDown,
231
+ onChange: handleInputChange,
232
+ onBlur: handleInputBlur,
233
+ onFocus: handleInputFocus,
234
+ disabled: disabled,
235
+ readonly: readonly,
236
+ type: 'number',
237
+ id: id,
238
+ step: step,
239
+ name: name,
240
+ min: min,
241
+ max: max,
242
+ "data-test-id": 'field-stepper__input'
243
+ })
244
+ }), (0, jsx_runtime_1.jsx)("div", {
245
+ className: (0, classnames_1.default)({
246
+ [styles_module_scss_1.default.postfixWrapper]: postfixSettings.show
247
+ }),
248
+ children: postfixSettings.show && postfixSettings.render({
249
+ key: postfixSettings.id
250
+ })
251
+ })]
250
252
  })
251
253
  })
252
254
  })
@@ -7,4 +7,23 @@
7
7
  }
8
8
  .button[data-disabled] *{
9
9
  cursor:not-allowed;
10
+ }
11
+
12
+ .trigger{
13
+ min-width:auto;
14
+ }
15
+
16
+ .wrap{
17
+ overflow:hidden;
18
+ display:flex;
19
+ justify-content:center;
20
+ max-width:100%;
21
+ }
22
+
23
+ .prefixWrapper{
24
+ margin-right:var(--space-fields-stepper-content-container, 4px);
25
+ }
26
+
27
+ .postfixWrapper{
28
+ margin-left:var(--space-fields-stepper-content-container, 4px);
10
29
  }
@@ -37,5 +37,5 @@ export declare function useHandleDeleteItem(setValue: Handler): (item?: ItemWith
37
37
  /**
38
38
  * Четкий и нечеткий поиск среди айтемов по полям 'content.option', 'content.caption', 'content.description', 'label'
39
39
  */
40
- export declare function useSearch(items: ItemProps[], enableFuzzySearch?: boolean): (search: string) => import("@snack-uikit/list/dist/esm/components/Items").Item[] | import("@snack-uikit/list/dist/esm/components/Items").FlattenItem[];
40
+ export declare function useSearch(items: ItemProps[], enableFuzzySearch?: boolean): (search: string) => import("@snack-uikit/list/dist/esm/components/Items").Item[];
41
41
  export {};
@@ -5,6 +5,7 @@ import { isAccordionItemProps, isNextListItemProps, kindFlattenItems, } from '@s
5
5
  import { useCopyButton, useValueControl } from '../../hooks';
6
6
  import { extractChildIds } from './legacy';
7
7
  import { getValueByPath, isBaseOptionProps } from './utils';
8
+ import { filterItemsByFlattenIds } from './utils/filterItemsByFlattenIds';
8
9
  export function useHandleOnKeyDown({ setOpen, inputKeyDownNavigationHandler, onInputKeyDownProp, }) {
9
10
  return useCallback((onKeyDown) => (e) => {
10
11
  if (e.code === 'Space') {
@@ -100,7 +101,7 @@ export function useSearch(items, enableFuzzySearch = true) {
100
101
  const { flattenItems } = kindFlattenItems({ items });
101
102
  return Object.values(flattenItems);
102
103
  }, [items]);
103
- const filterFunction = useCallback((search) => {
104
+ const filterFlattenFunction = useCallback((search) => {
104
105
  if (!enableFuzzySearch) {
105
106
  return flattenItems.filter(item => [...COMMON_FIELDS_TO_SEARCH, 'label'].some(key => {
106
107
  const value = getValueByPath(item, key);
@@ -110,5 +111,9 @@ export function useSearch(items, enableFuzzySearch = true) {
110
111
  const searcher = new FuzzySearch(flattenItems, COMMON_FIELDS_TO_SEARCH, {});
111
112
  return searcher.search(search);
112
113
  }, [enableFuzzySearch, flattenItems]);
114
+ const filterFunction = useCallback((search) => {
115
+ const filteredIds = filterFlattenFunction(search).map(item => item.id);
116
+ return filterItemsByFlattenIds(items, filteredIds);
117
+ }, [filterFlattenFunction, items]);
113
118
  return useCallback((search) => (search.length >= DEFAULT_MIN_SEARCH_INPUT_LENGTH ? filterFunction(search) : items), [filterFunction, items]);
114
119
  }
@@ -0,0 +1,2 @@
1
+ import { ItemId, ItemProps } from '@snack-uikit/list';
2
+ export declare function filterItemsByFlattenIds(items: ItemProps[], ids: ItemId[]): import("@snack-uikit/list/dist/esm/components/Items").Item[];
@@ -0,0 +1,17 @@
1
+ const isItemWithId = (item) => item.id !== undefined;
2
+ const isGroupItem = (item) => item.type === 'group';
3
+ export function filterItemsByFlattenIds(items, ids) {
4
+ const filteredItems = [];
5
+ items.forEach(item => {
6
+ if (isItemWithId(item) && item.id && ids.includes(item.id)) {
7
+ filteredItems.push(item);
8
+ return;
9
+ }
10
+ if (isGroupItem(item)) {
11
+ const filteredSubItems = filterItemsByFlattenIds(item.items, ids);
12
+ filteredItems.push(Object.assign(Object.assign({}, item), { items: filteredSubItems }));
13
+ return;
14
+ }
15
+ });
16
+ return filteredItems;
17
+ }
@@ -9,7 +9,8 @@ var __rest = (this && this.__rest) || function (s, e) {
9
9
  }
10
10
  return t;
11
11
  };
12
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
12
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
+ import cn from 'classnames';
13
14
  import mergeRefs from 'merge-refs';
14
15
  import { forwardRef, useEffect, useRef, useState, } from 'react';
15
16
  import { ButtonFunction } from '@snack-uikit/button';
@@ -34,6 +35,11 @@ const getDefaultValue = (min, max) => {
34
35
  }
35
36
  return 0;
36
37
  };
38
+ const SymbolWidth = {
39
+ s: 8,
40
+ m: 9,
41
+ l: 10,
42
+ };
37
43
  export const FieldStepper = forwardRef((_a, ref) => {
38
44
  var { id, name, value: valueProp, min = Number.NEGATIVE_INFINITY, max = Number.POSITIVE_INFINITY, step = 1, disabled = false, readonly = false, allowMoreThanLimits = true, onChange: onChangeProp, onFocus, onBlur, className, label, labelTooltip, labelTooltipPlacement, required = false, caption, hint, showHintIcon, size = SIZE.S, validationState = VALIDATION_STATE.Default, error, prefix, postfix } = _a, rest = __rest(_a, ["id", "name", "value", "min", "max", "step", "disabled", "readonly", "allowMoreThanLimits", "onChange", "onFocus", "onBlur", "className", "label", "labelTooltip", "labelTooltipPlacement", "required", "caption", "hint", "showHintIcon", "size", "validationState", "error", "prefix", "postfix"]);
39
45
  const { t } = useLocale('Fields');
@@ -46,8 +52,6 @@ export const FieldStepper = forwardRef((_a, ref) => {
46
52
  const [tooltip, setTooltip] = useState('');
47
53
  const timerRef = useRef();
48
54
  const inputRef = useRef(null);
49
- const minusButtonRef = useRef(null);
50
- const plusButtonRef = useRef(null);
51
55
  const isMinusButtonDisabled = (typeof min === 'number' && value <= min) || readonly || disabled;
52
56
  const isPlusButtonDisabled = (typeof max === 'number' && value >= max) || readonly || disabled;
53
57
  const prefixSettings = usePrefix({ prefix, disabled });
@@ -84,17 +88,10 @@ export const FieldStepper = forwardRef((_a, ref) => {
84
88
  onFocus === null || onFocus === void 0 ? void 0 : onFocus(event);
85
89
  };
86
90
  const handleInputChange = (value, event) => setValue(Number(value), event);
87
- const handleInputKeyDown = event => {
88
- var _a, _b;
91
+ const handleInputKeyDown = () => {
89
92
  if (readonly || disabled) {
90
93
  return;
91
94
  }
92
- if (event.key === 'ArrowRight' && !isPlusButtonDisabled) {
93
- (_a = plusButtonRef.current) === null || _a === void 0 ? void 0 : _a.focus();
94
- }
95
- if (event.key === 'ArrowLeft' && !isMinusButtonDisabled) {
96
- (_b = minusButtonRef.current) === null || _b === void 0 ? void 0 : _b.focus();
97
- }
98
95
  };
99
96
  const handleMinusButtonClick = (e) => {
100
97
  e.preventDefault();
@@ -106,25 +103,18 @@ export const FieldStepper = forwardRef((_a, ref) => {
106
103
  e.stopPropagation();
107
104
  setValue(Math.max(Math.min(max, value + step), min));
108
105
  };
109
- const handleMinusButtonKeyDown = event => {
110
- var _a;
106
+ const handleMinusButtonKeyDown = () => {
111
107
  if (readonly || disabled) {
112
108
  return;
113
109
  }
114
- if (event.key === 'ArrowRight') {
115
- event.preventDefault();
116
- (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
117
- }
118
110
  };
119
- const handlePlusButtonKeyDown = event => {
120
- var _a;
111
+ const handlePlusButtonKeyDown = () => {
121
112
  if (readonly || disabled) {
122
113
  return;
123
114
  }
124
- if (event.key === 'ArrowLeft') {
125
- event.preventDefault();
126
- (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
127
- }
128
115
  };
129
- return (_jsx(FieldDecorator, Object.assign({ className: className, label: label, labelTooltip: labelTooltip, labelTooltipPlacement: labelTooltipPlacement, labelFor: id, required: required, caption: caption, hint: hint, disabled: disabled, readonly: readonly, showHintIcon: showHintIcon, size: size, validationState: fieldValidationState, error: error }, extractSupportProps(rest), { children: _jsx(Tooltip, { tip: tooltip, open: allowMoreThanLimits ? false : tooltipOpen, "data-test-id": 'field-stepper__limit-tooltip', children: _jsx(FieldContainerPrivate, { size: size, validationState: fieldValidationState, disabled: disabled, readonly: readonly, variant: CONTAINER_VARIANT.SingleLine, inputRef: inputRef, prefix: _jsxs(_Fragment, { children: [_jsx(ButtonFunction, { tabIndex: -1, ref: minusButtonRef, size: 'xs', className: styles.button, icon: _jsx(MinusSVG, {}), onClick: handleMinusButtonClick, onKeyDown: handleMinusButtonKeyDown, disabled: isMinusButtonDisabled, "data-test-id": 'field-stepper__minus-button' }), prefixSettings.show && prefixSettings.render({ key: prefixSettings.id })] }), postfix: _jsxs(_Fragment, { children: [postfixSettings.show && postfixSettings.render({ key: postfixSettings.id }), _jsx(ButtonFunction, { ref: plusButtonRef, tabIndex: -1, size: 'xs', className: styles.button, icon: _jsx(PlusSVG, {}), onClick: handlePlusButtonClick, onKeyDown: handlePlusButtonKeyDown, disabled: isPlusButtonDisabled, "data-test-id": 'field-stepper__plus-button' })] }), children: _jsx(InputPrivate, { ref: mergeRefs(ref, inputRef), className: styles.stepper, "data-size": size, value: String(value), tabIndex: 0, onKeyDown: handleInputKeyDown, onChange: handleInputChange, onBlur: handleInputBlur, onFocus: handleInputFocus, disabled: disabled, readonly: readonly, type: 'number', id: id, name: name, min: min, max: max, "data-test-id": 'field-stepper__input' }) }) }) })));
116
+ return (_jsx(FieldDecorator, Object.assign({ className: className, label: label, labelTooltip: labelTooltip, labelTooltipPlacement: labelTooltipPlacement, labelFor: id, required: required, caption: caption, hint: hint, disabled: disabled, readonly: readonly, showHintIcon: showHintIcon, size: size, validationState: fieldValidationState, error: error }, extractSupportProps(rest), { children: _jsx(Tooltip, { tip: tooltip, open: allowMoreThanLimits ? false : tooltipOpen, "data-test-id": 'field-stepper__limit-tooltip', triggerClassName: styles.trigger, children: _jsx(FieldContainerPrivate, { size: size, validationState: fieldValidationState, disabled: disabled, readonly: readonly, variant: CONTAINER_VARIANT.SingleLine, inputRef: inputRef, prefix: _jsx(ButtonFunction, { tabIndex: -1, size: 'xs', className: styles.button, icon: _jsx(MinusSVG, {}), onClick: handleMinusButtonClick, onKeyDown: handleMinusButtonKeyDown, disabled: isMinusButtonDisabled, "data-test-id": 'field-stepper__minus-button' }), postfix: _jsx(ButtonFunction, { tabIndex: -1, size: 'xs', className: styles.button, icon: _jsx(PlusSVG, {}), onClick: handlePlusButtonClick, onKeyDown: handlePlusButtonKeyDown, disabled: isPlusButtonDisabled, "data-test-id": 'field-stepper__plus-button' }), children: _jsxs("div", { className: styles.wrap, children: [_jsx("div", { className: cn({ [styles.prefixWrapper]: prefixSettings.show }), children: prefixSettings.show && prefixSettings.render({ key: prefixSettings.id }) }), _jsx("div", { style: {
117
+ width: String(value).length * SymbolWidth[size],
118
+ maxWidth: '100%',
119
+ }, children: _jsx(InputPrivate, { ref: mergeRefs(ref, inputRef), className: styles.stepper, "data-size": size, value: String(value), tabIndex: 0, onKeyDown: handleInputKeyDown, onChange: handleInputChange, onBlur: handleInputBlur, onFocus: handleInputFocus, disabled: disabled, readonly: readonly, type: 'number', id: id, step: step, name: name, min: min, max: max, "data-test-id": 'field-stepper__input' }) }), _jsx("div", { className: cn({ [styles.postfixWrapper]: postfixSettings.show }), children: postfixSettings.show && postfixSettings.render({ key: postfixSettings.id }) })] }) }) }) })));
130
120
  });
@@ -7,4 +7,23 @@
7
7
  }
8
8
  .button[data-disabled] *{
9
9
  cursor:not-allowed;
10
+ }
11
+
12
+ .trigger{
13
+ min-width:auto;
14
+ }
15
+
16
+ .wrap{
17
+ overflow:hidden;
18
+ display:flex;
19
+ justify-content:center;
20
+ max-width:100%;
21
+ }
22
+
23
+ .prefixWrapper{
24
+ margin-right:var(--space-fields-stepper-content-container, 4px);
25
+ }
26
+
27
+ .postfixWrapper{
28
+ margin-left:var(--space-fields-stepper-content-container, 4px);
10
29
  }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public"
5
5
  },
6
6
  "title": "Fields",
7
- "version": "0.34.0",
7
+ "version": "0.35.1",
8
8
  "sideEffects": [
9
9
  "*.css",
10
10
  "*.woff",
@@ -37,13 +37,13 @@
37
37
  "scripts": {},
38
38
  "dependencies": {
39
39
  "@snack-uikit/button": "0.19.3",
40
- "@snack-uikit/calendar": "0.11.6",
41
- "@snack-uikit/color-picker": "0.3.3",
40
+ "@snack-uikit/calendar": "0.11.7",
41
+ "@snack-uikit/color-picker": "0.3.4",
42
42
  "@snack-uikit/divider": "3.2.1",
43
43
  "@snack-uikit/dropdown": "0.4.1",
44
44
  "@snack-uikit/icons": "0.24.0",
45
- "@snack-uikit/input-private": "4.2.2",
46
- "@snack-uikit/list": "0.21.6",
45
+ "@snack-uikit/input-private": "4.3.0",
46
+ "@snack-uikit/list": "0.21.7",
47
47
  "@snack-uikit/scroll": "0.9.1",
48
48
  "@snack-uikit/skeleton": "0.5.1",
49
49
  "@snack-uikit/slider": "0.3.2",
@@ -65,5 +65,5 @@
65
65
  "peerDependencies": {
66
66
  "@snack-uikit/locale": "*"
67
67
  },
68
- "gitHead": "29125ae9f570868bae8ad22fef795a50a150758e"
68
+ "gitHead": "8a406cbe32e37af611f8abee40c622e314a46090"
69
69
  }
@@ -15,6 +15,7 @@ import { useCopyButton, useValueControl } from '../../hooks';
15
15
  import { extractChildIds } from './legacy';
16
16
  import { ItemWithId, SearchState, SelectedOptionFormatter } from './types';
17
17
  import { getValueByPath, isBaseOptionProps } from './utils';
18
+ import { filterItemsByFlattenIds } from './utils/filterItemsByFlattenIds';
18
19
 
19
20
  type UseHandleOnKeyDownProps = {
20
21
  inputKeyDownNavigationHandler: KeyboardEventHandler<HTMLInputElement>;
@@ -187,13 +188,12 @@ export function useSearch(items: ItemProps[], enableFuzzySearch: boolean = true)
187
188
  return Object.values(flattenItems);
188
189
  }, [items]);
189
190
 
190
- const filterFunction = useCallback(
191
+ const filterFlattenFunction = useCallback(
191
192
  (search: string) => {
192
193
  if (!enableFuzzySearch) {
193
194
  return flattenItems.filter(item =>
194
195
  [...COMMON_FIELDS_TO_SEARCH, 'label'].some(key => {
195
196
  const value = getValueByPath(item, key);
196
-
197
197
  return value.toLowerCase().includes(search.toLowerCase());
198
198
  }),
199
199
  );
@@ -205,6 +205,14 @@ export function useSearch(items: ItemProps[], enableFuzzySearch: boolean = true)
205
205
  [enableFuzzySearch, flattenItems],
206
206
  );
207
207
 
208
+ const filterFunction = useCallback(
209
+ (search: string) => {
210
+ const filteredIds = filterFlattenFunction(search).map(item => item.id);
211
+ return filterItemsByFlattenIds(items, filteredIds);
212
+ },
213
+ [filterFlattenFunction, items],
214
+ );
215
+
208
216
  return useCallback(
209
217
  (search: string) => (search.length >= DEFAULT_MIN_SEARCH_INPUT_LENGTH ? filterFunction(search) : items),
210
218
  [filterFunction, items],
@@ -0,0 +1,23 @@
1
+ import { GroupItemProps, ItemId, ItemProps } from '@snack-uikit/list';
2
+
3
+ import { ItemWithId } from '../types';
4
+
5
+ const isItemWithId = (item: ItemProps): item is ItemWithId => (item as ItemWithId).id !== undefined;
6
+
7
+ const isGroupItem = (item: ItemProps): item is GroupItemProps => (item as GroupItemProps).type === 'group';
8
+
9
+ export function filterItemsByFlattenIds(items: ItemProps[], ids: ItemId[]) {
10
+ const filteredItems: ItemProps[] = [];
11
+ items.forEach(item => {
12
+ if (isItemWithId(item) && item.id && ids.includes(item.id)) {
13
+ filteredItems.push(item);
14
+ return;
15
+ }
16
+ if (isGroupItem(item)) {
17
+ const filteredSubItems = filterItemsByFlattenIds(item.items, ids);
18
+ filteredItems.push({ ...item, items: filteredSubItems });
19
+ return;
20
+ }
21
+ });
22
+ return filteredItems;
23
+ }
@@ -1,3 +1,4 @@
1
+ import cn from 'classnames';
1
2
  import mergeRefs from 'merge-refs';
2
3
  import {
3
4
  ChangeEvent,
@@ -76,6 +77,12 @@ const getDefaultValue = (min: number, max: number) => {
76
77
  return 0;
77
78
  };
78
79
 
80
+ const SymbolWidth = {
81
+ s: 8,
82
+ m: 9,
83
+ l: 10,
84
+ };
85
+
79
86
  export const FieldStepper = forwardRef<HTMLInputElement, FieldStepperProps>(
80
87
  (
81
88
  {
@@ -118,8 +125,7 @@ export const FieldStepper = forwardRef<HTMLInputElement, FieldStepperProps>(
118
125
  const [tooltip, setTooltip] = useState('');
119
126
  const timerRef = useRef<ReturnType<typeof setTimeout>>();
120
127
  const inputRef = useRef<HTMLInputElement>(null);
121
- const minusButtonRef = useRef<HTMLButtonElement>(null);
122
- const plusButtonRef = useRef<HTMLButtonElement>(null);
128
+
123
129
  const isMinusButtonDisabled = (typeof min === 'number' && value <= min) || readonly || disabled;
124
130
  const isPlusButtonDisabled = (typeof max === 'number' && value >= max) || readonly || disabled;
125
131
 
@@ -171,18 +177,10 @@ export const FieldStepper = forwardRef<HTMLInputElement, FieldStepperProps>(
171
177
 
172
178
  const handleInputChange = (value: string, event: ChangeEvent<HTMLInputElement>) => setValue(Number(value), event);
173
179
 
174
- const handleInputKeyDown: KeyboardEventHandler<HTMLInputElement> = event => {
180
+ const handleInputKeyDown: KeyboardEventHandler<HTMLInputElement> = () => {
175
181
  if (readonly || disabled) {
176
182
  return;
177
183
  }
178
-
179
- if (event.key === 'ArrowRight' && !isPlusButtonDisabled) {
180
- plusButtonRef.current?.focus();
181
- }
182
-
183
- if (event.key === 'ArrowLeft' && !isMinusButtonDisabled) {
184
- minusButtonRef.current?.focus();
185
- }
186
184
  };
187
185
 
188
186
  const handleMinusButtonClick = (e: MouseEvent) => {
@@ -197,26 +195,16 @@ export const FieldStepper = forwardRef<HTMLInputElement, FieldStepperProps>(
197
195
  setValue(Math.max(Math.min(max, value + step), min));
198
196
  };
199
197
 
200
- const handleMinusButtonKeyDown: KeyboardEventHandler<HTMLInputElement> = event => {
198
+ const handleMinusButtonKeyDown: KeyboardEventHandler<HTMLInputElement> = () => {
201
199
  if (readonly || disabled) {
202
200
  return;
203
201
  }
204
-
205
- if (event.key === 'ArrowRight') {
206
- event.preventDefault();
207
- inputRef.current?.focus();
208
- }
209
202
  };
210
203
 
211
- const handlePlusButtonKeyDown: KeyboardEventHandler<HTMLInputElement> = event => {
204
+ const handlePlusButtonKeyDown: KeyboardEventHandler<HTMLInputElement> = () => {
212
205
  if (readonly || disabled) {
213
206
  return;
214
207
  }
215
-
216
- if (event.key === 'ArrowLeft') {
217
- event.preventDefault();
218
- inputRef.current?.focus();
219
- }
220
208
  };
221
209
 
222
210
  return (
@@ -241,6 +229,7 @@ export const FieldStepper = forwardRef<HTMLInputElement, FieldStepperProps>(
241
229
  tip={tooltip}
242
230
  open={allowMoreThanLimits ? false : tooltipOpen}
243
231
  data-test-id='field-stepper__limit-tooltip'
232
+ triggerClassName={styles.trigger}
244
233
  >
245
234
  <FieldContainerPrivate
246
235
  size={size}
@@ -250,58 +239,66 @@ export const FieldStepper = forwardRef<HTMLInputElement, FieldStepperProps>(
250
239
  variant={CONTAINER_VARIANT.SingleLine}
251
240
  inputRef={inputRef}
252
241
  prefix={
253
- <>
254
- <ButtonFunction
255
- tabIndex={-1}
256
- ref={minusButtonRef}
257
- size='xs'
258
- className={styles.button}
259
- icon={<MinusSVG />}
260
- onClick={handleMinusButtonClick}
261
- onKeyDown={handleMinusButtonKeyDown}
262
- disabled={isMinusButtonDisabled}
263
- data-test-id='field-stepper__minus-button'
264
- />
265
- {prefixSettings.show && prefixSettings.render({ key: prefixSettings.id })}
266
- </>
242
+ <ButtonFunction
243
+ tabIndex={-1}
244
+ size='xs'
245
+ className={styles.button}
246
+ icon={<MinusSVG />}
247
+ onClick={handleMinusButtonClick}
248
+ onKeyDown={handleMinusButtonKeyDown}
249
+ disabled={isMinusButtonDisabled}
250
+ data-test-id='field-stepper__minus-button'
251
+ />
267
252
  }
268
253
  postfix={
269
- <>
270
- {postfixSettings.show && postfixSettings.render({ key: postfixSettings.id })}
271
-
272
- <ButtonFunction
273
- ref={plusButtonRef}
274
- tabIndex={-1}
275
- size='xs'
276
- className={styles.button}
277
- icon={<PlusSVG />}
278
- onClick={handlePlusButtonClick}
279
- onKeyDown={handlePlusButtonKeyDown}
280
- disabled={isPlusButtonDisabled}
281
- data-test-id='field-stepper__plus-button'
282
- />
283
- </>
254
+ <ButtonFunction
255
+ tabIndex={-1}
256
+ size='xs'
257
+ className={styles.button}
258
+ icon={<PlusSVG />}
259
+ onClick={handlePlusButtonClick}
260
+ onKeyDown={handlePlusButtonKeyDown}
261
+ disabled={isPlusButtonDisabled}
262
+ data-test-id='field-stepper__plus-button'
263
+ />
284
264
  }
285
265
  >
286
- <InputPrivate
287
- ref={mergeRefs(ref, inputRef)}
288
- className={styles.stepper}
289
- data-size={size}
290
- value={String(value)}
291
- tabIndex={0}
292
- onKeyDown={handleInputKeyDown}
293
- onChange={handleInputChange}
294
- onBlur={handleInputBlur}
295
- onFocus={handleInputFocus}
296
- disabled={disabled}
297
- readonly={readonly}
298
- type='number'
299
- id={id}
300
- name={name}
301
- min={min}
302
- max={max}
303
- data-test-id='field-stepper__input'
304
- />
266
+ <div className={styles.wrap}>
267
+ <div className={cn({ [styles.prefixWrapper]: prefixSettings.show })}>
268
+ {prefixSettings.show && prefixSettings.render({ key: prefixSettings.id })}
269
+ </div>
270
+
271
+ <div
272
+ style={{
273
+ width: String(value).length * SymbolWidth[size],
274
+ maxWidth: '100%',
275
+ }}
276
+ >
277
+ <InputPrivate
278
+ ref={mergeRefs(ref, inputRef)}
279
+ className={styles.stepper}
280
+ data-size={size}
281
+ value={String(value)}
282
+ tabIndex={0}
283
+ onKeyDown={handleInputKeyDown}
284
+ onChange={handleInputChange}
285
+ onBlur={handleInputBlur}
286
+ onFocus={handleInputFocus}
287
+ disabled={disabled}
288
+ readonly={readonly}
289
+ type='number'
290
+ id={id}
291
+ step={step}
292
+ name={name}
293
+ min={min}
294
+ max={max}
295
+ data-test-id='field-stepper__input'
296
+ />
297
+ </div>
298
+ <div className={cn({ [styles.postfixWrapper]: postfixSettings.show })}>
299
+ {postfixSettings.show && postfixSettings.render({ key: postfixSettings.id })}
300
+ </div>
301
+ </div>
305
302
  </FieldContainerPrivate>
306
303
  </Tooltip>
307
304
  </FieldDecorator>
@@ -1,3 +1,5 @@
1
+ @use '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-fields' as ft-fields;
2
+
1
3
  .stepper {
2
4
  text-align: center;
3
5
  }
@@ -10,4 +12,23 @@
10
12
  &[data-disabled] * {
11
13
  cursor: not-allowed;
12
14
  }
13
- }
15
+ }
16
+
17
+ .trigger {
18
+ min-width: auto;
19
+ }
20
+
21
+ .wrap {
22
+ overflow: hidden;
23
+ display: flex;
24
+ justify-content: center;
25
+ max-width: 100%;
26
+ }
27
+
28
+ .prefixWrapper {
29
+ margin-right: ft-fields.$space-fields-stepper-content-container;
30
+ }
31
+
32
+ .postfixWrapper {
33
+ margin-left: ft-fields.$space-fields-stepper-content-container;
34
+ }