@snack-uikit/fields 0.53.2-preview-0be0d183.0 → 0.54.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +2 -2
  3. package/dist/cjs/components/FieldSelect/FieldSelectMultiple.d.ts +2 -2
  4. package/dist/cjs/components/FieldSelect/FieldSelectMultiple.js +9 -11
  5. package/dist/cjs/components/FieldSelect/FieldSelectSingle.js +9 -10
  6. package/dist/cjs/components/FieldSelect/{hooks.d.ts → hooks/common.d.ts} +1 -6
  7. package/dist/cjs/components/FieldSelect/{hooks.js → hooks/common.js} +4 -18
  8. package/dist/cjs/components/FieldSelect/hooks/customOption.d.ts +26 -0
  9. package/dist/cjs/components/FieldSelect/hooks/customOption.js +69 -0
  10. package/dist/cjs/components/FieldSelect/hooks/index.d.ts +2 -0
  11. package/dist/cjs/components/FieldSelect/hooks/index.js +26 -0
  12. package/dist/cjs/components/FieldSelect/index.d.ts +1 -1
  13. package/dist/cjs/components/FieldSelect/types.d.ts +3 -3
  14. package/dist/cjs/components/FieldSelect/utils/customOption.d.ts +4 -0
  15. package/dist/cjs/components/FieldSelect/utils/index.d.ts +2 -1
  16. package/dist/cjs/components/FieldSelect/utils/index.js +2 -1
  17. package/dist/esm/components/FieldSelect/FieldSelectMultiple.d.ts +2 -2
  18. package/dist/esm/components/FieldSelect/FieldSelectMultiple.js +6 -11
  19. package/dist/esm/components/FieldSelect/FieldSelectSingle.js +7 -11
  20. package/dist/esm/components/FieldSelect/{hooks.d.ts → hooks/common.d.ts} +1 -6
  21. package/dist/esm/components/FieldSelect/{hooks.js → hooks/common.js} +3 -12
  22. package/dist/esm/components/FieldSelect/hooks/customOption.d.ts +26 -0
  23. package/dist/esm/components/FieldSelect/hooks/customOption.js +38 -0
  24. package/dist/esm/components/FieldSelect/hooks/index.d.ts +2 -0
  25. package/dist/esm/components/FieldSelect/hooks/index.js +2 -0
  26. package/dist/esm/components/FieldSelect/index.d.ts +1 -1
  27. package/dist/esm/components/FieldSelect/types.d.ts +3 -3
  28. package/dist/esm/components/FieldSelect/utils/customOption.d.ts +4 -0
  29. package/dist/esm/components/FieldSelect/utils/index.d.ts +2 -1
  30. package/dist/esm/components/FieldSelect/utils/index.js +2 -1
  31. package/package.json +2 -2
  32. package/src/components/FieldSelect/FieldSelectMultiple.tsx +7 -22
  33. package/src/components/FieldSelect/FieldSelectSingle.tsx +8 -22
  34. package/src/components/FieldSelect/{hooks.ts → hooks/common.ts} +4 -23
  35. package/src/components/FieldSelect/hooks/customOption.ts +90 -0
  36. package/src/components/FieldSelect/hooks/index.ts +2 -0
  37. package/src/components/FieldSelect/index.ts +1 -1
  38. package/src/components/FieldSelect/types.ts +6 -3
  39. package/src/components/FieldSelect/utils/customOption.ts +23 -0
  40. package/src/components/FieldSelect/utils/index.ts +2 -1
  41. package/dist/cjs/components/FieldSelect/utils/getCustomOptionTriggerByCode.d.ts +0 -4
  42. package/dist/esm/components/FieldSelect/utils/getCustomOptionTriggerByCode.d.ts +0 -4
  43. package/src/components/FieldSelect/utils/getCustomOptionTriggerByCode.ts +0 -23
  44. /package/dist/cjs/components/FieldSelect/utils/{getCustomOptionTriggerByCode.js → customOption.js} +0 -0
  45. /package/dist/esm/components/FieldSelect/utils/{getCustomOptionTriggerByCode.js → customOption.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # 0.54.0 (2026-04-21)
7
+
8
+
9
+ ### Features
10
+
11
+ * **IAM-6423:** support triggers for custom field option ([1865a13](https://github.com/cloud-ru-tech/snack-uikit/commit/1865a13f6e270de961295a96ad7c8d80c7cb147d))
12
+
13
+
14
+
15
+
16
+
6
17
  ## 0.53.1 (2026-04-02)
7
18
 
8
19
 
package/README.md CHANGED
@@ -741,12 +741,12 @@ FieldStepper в основном предназначен для работы с
741
741
  | noDataState | `EmptyStateProps` | - | Экран при отстутствии данных |
742
742
  | noResultsState | `EmptyStateProps` | - | Экран при отстутствии результатов поиска или фильтров |
743
743
  | errorDataState | `EmptyStateProps` | - | Экран при ошибке запроса |
744
- | addCustomOptionTriggers | `FieldSelectSingleAddCustomOptionTrigger[] \| FieldSelectAddCustomOptionTrigger[]` | - | Триггеры фиксации произвольного значения из строки поиска в режиме `single`. Если передан, имеет приоритет над устаревшим `addOptionByEnter`. Триггеры фиксации произвольного значения из строки поиска в режиме `multiple`. Если передан, имеет приоритет над устаревшим `addOptionByEnter`. |
744
+ | addCustomOptionTriggers | `FieldSelectSingleAddCustomOptionTrigger[] \| FieldSelectMultipleAddCustomOptionTrigger[]` | - | Триггеры фиксации произвольного значения из строки поиска в режиме `single`. Если передан, имеет приоритет над устаревшим `addOptionByEnter`. Триггеры фиксации произвольного значения из строки поиска в режиме `multiple`. Если передан, имеет приоритет над устаревшим `addOptionByEnter`. |
745
745
  | selection | "single" \| "multiple" | - | |
746
746
  | ref | `LegacyRef<HTMLInputElement>` | - | Allows getting a ref to the component instance. Once the component unmounts, React will set `ref.current` to `null` (or call the ref with `null` if you passed a callback ref). @see {@link https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom React Docs} |
747
747
  | key | `Key` | - | |
748
748
  | removeByBackspace | `boolean` | - | |
749
- ## FieldSelectAddCustomOptionTrigger
749
+ ## FieldSelectMultipleAddCustomOptionTrigger
750
750
  События, по которым произвольное значение из строки поиска фиксируется в значении поля
751
751
  ### Props
752
752
  | name | type | default value | description |
@@ -1,4 +1,4 @@
1
- import { FieldSelectAddCustomOptionTrigger, SelectedOptionFormatter } from './types';
1
+ import { SelectedOptionFormatter } from './types';
2
2
  export declare const FieldSelectMultiple: import("react").ForwardRefExoticComponent<import("./types").InputProps & import("./types").WrapperProps & {
3
3
  options: import("./types").OptionProps[];
4
4
  loading?: boolean;
@@ -29,5 +29,5 @@ export declare const FieldSelectMultiple: import("react").ForwardRefExoticCompon
29
29
  onOpenChange?(open: boolean): void;
30
30
  selectedOptionFormatter?: SelectedOptionFormatter;
31
31
  } & Pick<import("@snack-uikit/list").DroplistProps, "onScroll" | "scrollRef" | "scrollContainerRef" | "untouchableScrollbars" | "dataError" | "dataFiltered" | "scrollToSelectedItem" | "virtualized" | "noDataState" | "noResultsState" | "errorDataState">, "showCopyButton" | "onCopyButtonClick"> & {
32
- addCustomOptionTriggers?: FieldSelectAddCustomOptionTrigger[];
32
+ addCustomOptionTriggers?: import("./types").FieldSelectMultipleAddCustomOptionTrigger[];
33
33
  } & import("react").RefAttributes<HTMLInputElement>>;
@@ -103,19 +103,17 @@ exports.FieldSelectMultiple = (0, react_1.forwardRef)((props, ref) => {
103
103
  selectedOptionFormatter,
104
104
  resetSearchOnOptionSelection
105
105
  }));
106
- const resolvedAddCustomOptionTriggers = (0, hooks_2.useResolvedAddCustomOptionTriggers)({
106
+ const {
107
+ resolvedAddCustomOptionTriggers,
108
+ tryCommitCustomOptionFromInput
109
+ } = (0, hooks_2.useFieldSelectMultipleCustomOption)({
107
110
  addCustomOptionTriggers,
108
- addOptionByEnter
111
+ addOptionByEnter,
112
+ inputValue,
113
+ value,
114
+ setValue,
115
+ updateInputValue
109
116
  });
110
- const tryCommitCustomOptionFromInput = (0, react_1.useCallback)(trigger => {
111
- if (!resolvedAddCustomOptionTriggers.includes(trigger) || inputValue === '') {
112
- return;
113
- }
114
- if (!(value !== null && value !== void 0 ? value : []).includes(inputValue)) {
115
- setValue(prev => (prev !== null && prev !== void 0 ? prev : []).concat(inputValue));
116
- updateInputValue();
117
- }
118
- }, [resolvedAddCustomOptionTriggers, inputValue, value, setValue, updateInputValue]);
119
117
  const prefixSettings = (0, hooks_1.usePrefix)({
120
118
  prefix,
121
119
  disabled
@@ -100,10 +100,6 @@ exports.FieldSelectSingle = (0, react_1.forwardRef)((props, ref) => {
100
100
  resetSearchOnOptionSelection,
101
101
  selectedOptionFormatter
102
102
  }));
103
- const resolvedAddCustomOptionTriggers = (0, hooks_2.useResolvedAddCustomOptionTriggers)({
104
- addCustomOptionTriggers: addCustomOptionTriggersProp,
105
- addOptionByEnter
106
- });
107
103
  const prevSelectedItem = (0, react_1.useRef)(selectedItem);
108
104
  const prefixSettings = (0, hooks_1.usePrefix)({
109
105
  prefix,
@@ -178,12 +174,15 @@ exports.FieldSelectSingle = (0, react_1.forwardRef)((props, ref) => {
178
174
  setValue(newValue);
179
175
  }
180
176
  }, [setOpen, setValue]);
181
- const tryCommitCustomOptionFromInput = (0, react_1.useCallback)(trigger => {
182
- if (!resolvedAddCustomOptionTriggers.includes(trigger) || inputValue === '') {
183
- return;
184
- }
185
- handleSelectionChange(inputValue);
186
- }, [resolvedAddCustomOptionTriggers, inputValue, handleSelectionChange]);
177
+ const {
178
+ resolvedAddCustomOptionTriggers,
179
+ tryCommitCustomOptionFromInput
180
+ } = (0, hooks_2.useFieldSelectSingleCustomOption)({
181
+ addCustomOptionTriggers: addCustomOptionTriggersProp,
182
+ addOptionByEnter,
183
+ inputValue,
184
+ handleSelectionChange
185
+ });
187
186
  const handleBlur = e => {
188
187
  var _a;
189
188
  if (!open && !buttonsRefs.filter(Boolean).includes(e.relatedTarget)) {
@@ -1,7 +1,7 @@
1
1
  import { KeyboardEvent, KeyboardEventHandler, MouseEvent, RefObject } from 'react';
2
2
  import { Handler } from 'uncontrollable';
3
3
  import { ItemProps } from '@snack-uikit/list';
4
- import { FieldSelectAddCustomOptionTrigger, ItemWithId, SearchState, SelectedOptionFormatter } from './types';
4
+ import { ItemWithId, SearchState, SelectedOptionFormatter } from '../types';
5
5
  type UseHandleOnKeyDownProps = {
6
6
  inputKeyDownNavigationHandler: KeyboardEventHandler<HTMLInputElement>;
7
7
  onInputKeyDownProp: KeyboardEventHandler<HTMLInputElement> | undefined;
@@ -38,9 +38,4 @@ export declare function useHandleDeleteItem(setValue: Handler): (item?: ItemWith
38
38
  * Четкий и нечеткий поиск среди айтемов по полям 'content.option', 'content.caption', 'content.description', 'label'
39
39
  */
40
40
  export declare function useSearch(items: ItemProps[], enableFuzzySearch?: boolean): (search: string) => import("@snack-uikit/list/dist/esm/components/Items").Item[];
41
- type UseResolvedAddCustomOptionTriggersProps = {
42
- addCustomOptionTriggers?: FieldSelectAddCustomOptionTrigger[];
43
- addOptionByEnter: boolean;
44
- };
45
- export declare function useResolvedAddCustomOptionTriggers({ addCustomOptionTriggers, addOptionByEnter, }: UseResolvedAddCustomOptionTriggersProps): FieldSelectAddCustomOptionTrigger[];
46
41
  export {};
@@ -13,15 +13,13 @@ exports.useButtons = useButtons;
13
13
  exports.useSearchInput = useSearchInput;
14
14
  exports.useHandleDeleteItem = useHandleDeleteItem;
15
15
  exports.useSearch = useSearch;
16
- exports.useResolvedAddCustomOptionTriggers = useResolvedAddCustomOptionTriggers;
17
16
  const fuzzy_search_1 = __importDefault(require("fuzzy-search"));
18
17
  const react_1 = require("react");
19
18
  const input_private_1 = require("@snack-uikit/input-private");
20
19
  const list_1 = require("@snack-uikit/list");
21
- const hooks_1 = require("../../hooks");
22
- const legacy_1 = require("./legacy");
23
- const utils_1 = require("./utils");
24
- const filterItemsByFlattenIds_1 = require("./utils/filterItemsByFlattenIds");
20
+ const hooks_1 = require("../../../hooks");
21
+ const legacy_1 = require("../legacy");
22
+ const utils_1 = require("../utils");
25
23
  function useHandleOnKeyDown(_ref) {
26
24
  let {
27
25
  setOpen,
@@ -174,19 +172,7 @@ function useSearch(items) {
174
172
  }, [enableFuzzySearch, flattenItems]);
175
173
  const filterFunction = (0, react_1.useCallback)(search => {
176
174
  const filteredIds = filterFlattenFunction(search).map(item => item.id);
177
- return (0, filterItemsByFlattenIds_1.filterItemsByFlattenIds)(items, filteredIds);
175
+ return (0, utils_1.filterItemsByFlattenIds)(items, filteredIds);
178
176
  }, [filterFlattenFunction, items]);
179
177
  return (0, react_1.useCallback)(search => search.length >= DEFAULT_MIN_SEARCH_INPUT_LENGTH ? filterFunction(search) : items, [filterFunction, items]);
180
- }
181
- function useResolvedAddCustomOptionTriggers(_ref4) {
182
- let {
183
- addCustomOptionTriggers,
184
- addOptionByEnter
185
- } = _ref4;
186
- return (0, react_1.useMemo)(() => {
187
- if (addCustomOptionTriggers !== undefined) {
188
- return addCustomOptionTriggers;
189
- }
190
- return addOptionByEnter ? ['enter'] : [];
191
- }, [addCustomOptionTriggers, addOptionByEnter]);
192
178
  }
@@ -0,0 +1,26 @@
1
+ import { Handler } from 'uncontrollable';
2
+ import { SelectionSingleValueType } from '@snack-uikit/list';
3
+ import { FieldSelectMultipleAddCustomOptionTrigger } from '../types';
4
+ type UseResolvedAddCustomOptionTriggersParams = {
5
+ addCustomOptionTriggers?: FieldSelectMultipleAddCustomOptionTrigger[];
6
+ addOptionByEnter: boolean;
7
+ };
8
+ type UseFieldSelectMultipleCustomOptionParams = UseResolvedAddCustomOptionTriggersParams & {
9
+ inputValue: string;
10
+ value: SelectionSingleValueType[] | undefined;
11
+ setValue: Handler;
12
+ updateInputValue: () => void;
13
+ };
14
+ export declare function useFieldSelectMultipleCustomOption({ addCustomOptionTriggers, addOptionByEnter, inputValue, value, setValue, updateInputValue, }: UseFieldSelectMultipleCustomOptionParams): {
15
+ resolvedAddCustomOptionTriggers: FieldSelectMultipleAddCustomOptionTrigger[];
16
+ tryCommitCustomOptionFromInput: (trigger: FieldSelectMultipleAddCustomOptionTrigger) => void;
17
+ };
18
+ type UseFieldSelectSingleCustomOptionParams = UseResolvedAddCustomOptionTriggersParams & {
19
+ inputValue: string;
20
+ handleSelectionChange: (value: SelectionSingleValueType) => void;
21
+ };
22
+ export declare function useFieldSelectSingleCustomOption({ addCustomOptionTriggers, addOptionByEnter, inputValue, handleSelectionChange, }: UseFieldSelectSingleCustomOptionParams): {
23
+ resolvedAddCustomOptionTriggers: FieldSelectMultipleAddCustomOptionTrigger[];
24
+ tryCommitCustomOptionFromInput: (trigger: FieldSelectMultipleAddCustomOptionTrigger) => void;
25
+ };
26
+ export {};
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useFieldSelectMultipleCustomOption = useFieldSelectMultipleCustomOption;
7
+ exports.useFieldSelectSingleCustomOption = useFieldSelectSingleCustomOption;
8
+ const react_1 = require("react");
9
+ function useResolvedAddCustomOptionTriggers(_ref) {
10
+ let {
11
+ addCustomOptionTriggers,
12
+ addOptionByEnter
13
+ } = _ref;
14
+ return (0, react_1.useMemo)(() => {
15
+ if (addCustomOptionTriggers !== undefined) {
16
+ return addCustomOptionTriggers;
17
+ }
18
+ return addOptionByEnter ? ['enter'] : [];
19
+ }, [addCustomOptionTriggers, addOptionByEnter]);
20
+ }
21
+ function useFieldSelectMultipleCustomOption(_ref2) {
22
+ let {
23
+ addCustomOptionTriggers,
24
+ addOptionByEnter,
25
+ inputValue,
26
+ value,
27
+ setValue,
28
+ updateInputValue
29
+ } = _ref2;
30
+ const resolvedAddCustomOptionTriggers = useResolvedAddCustomOptionTriggers({
31
+ addCustomOptionTriggers,
32
+ addOptionByEnter
33
+ });
34
+ const tryCommitCustomOptionFromInput = (0, react_1.useCallback)(trigger => {
35
+ if (!resolvedAddCustomOptionTriggers.includes(trigger) || inputValue === '') {
36
+ return;
37
+ }
38
+ if (!(value !== null && value !== void 0 ? value : []).includes(inputValue)) {
39
+ setValue(prev => (prev !== null && prev !== void 0 ? prev : []).concat(inputValue));
40
+ updateInputValue();
41
+ }
42
+ }, [resolvedAddCustomOptionTriggers, inputValue, value, setValue, updateInputValue]);
43
+ return {
44
+ resolvedAddCustomOptionTriggers,
45
+ tryCommitCustomOptionFromInput
46
+ };
47
+ }
48
+ function useFieldSelectSingleCustomOption(_ref3) {
49
+ let {
50
+ addCustomOptionTriggers,
51
+ addOptionByEnter,
52
+ inputValue,
53
+ handleSelectionChange
54
+ } = _ref3;
55
+ const resolvedAddCustomOptionTriggers = useResolvedAddCustomOptionTriggers({
56
+ addCustomOptionTriggers,
57
+ addOptionByEnter
58
+ });
59
+ const tryCommitCustomOptionFromInput = (0, react_1.useCallback)(trigger => {
60
+ if (!resolvedAddCustomOptionTriggers.includes(trigger) || inputValue === '') {
61
+ return;
62
+ }
63
+ handleSelectionChange(inputValue);
64
+ }, [resolvedAddCustomOptionTriggers, inputValue, handleSelectionChange]);
65
+ return {
66
+ resolvedAddCustomOptionTriggers,
67
+ tryCommitCustomOptionFromInput
68
+ };
69
+ }
@@ -0,0 +1,2 @@
1
+ export * from './common';
2
+ export * from './customOption';
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+
3
+ var __createBinding = void 0 && (void 0).__createBinding || (Object.create ? function (o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = {
8
+ enumerable: true,
9
+ get: function () {
10
+ return m[k];
11
+ }
12
+ };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ } : function (o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ });
19
+ var __exportStar = void 0 && (void 0).__exportStar || function (m, exports) {
20
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
21
+ };
22
+ Object.defineProperty(exports, "__esModule", {
23
+ value: true
24
+ });
25
+ __exportStar(require("./common"), exports);
26
+ __exportStar(require("./customOption"), exports);
@@ -1,2 +1,2 @@
1
1
  export { FieldSelect } from './FieldSelect';
2
- export type { FieldSelectAddCustomOptionTrigger, FieldSelectSingleAddCustomOptionTrigger, FieldSelectSingleProps, FieldSelectMultipleProps, FieldSelectProps, OptionProps, BaseOptionProps, AccordionOptionProps, NestListOptionProps, GroupOptionProps, SelectedOptionFormatter, } from './types';
2
+ export type { FieldSelectMultipleAddCustomOptionTrigger, FieldSelectSingleAddCustomOptionTrigger, FieldSelectSingleProps, FieldSelectMultipleProps, FieldSelectProps, OptionProps, BaseOptionProps, AccordionOptionProps, NestListOptionProps, GroupOptionProps, SelectedOptionFormatter, } from './types';
@@ -32,8 +32,8 @@ export type SearchState = {
32
32
  onChange?(value: string): void;
33
33
  };
34
34
  /** События, по которым произвольное значение из строки поиска фиксируется в значении поля */
35
- export type FieldSelectAddCustomOptionTrigger = 'enter' | 'blur' | 'space' | 'comma';
36
- export type FieldSelectSingleAddCustomOptionTrigger = Extract<FieldSelectAddCustomOptionTrigger, 'enter' | 'blur'>;
35
+ export type FieldSelectMultipleAddCustomOptionTrigger = 'enter' | 'blur' | 'space' | 'comma';
36
+ export type FieldSelectSingleAddCustomOptionTrigger = Extract<FieldSelectMultipleAddCustomOptionTrigger, 'enter' | 'blur'>;
37
37
  export type FieldSelectPrivateProps = InputProps & WrapperProps & {
38
38
  options: OptionProps[];
39
39
  loading?: boolean;
@@ -95,7 +95,7 @@ export type FieldSelectMultipleProps = FieldSelectPrivateProps & {
95
95
  * Триггеры фиксации произвольного значения из строки поиска в режиме `multiple`.
96
96
  * Если передан, имеет приоритет над устаревшим `addOptionByEnter`.
97
97
  */
98
- addCustomOptionTriggers?: FieldSelectAddCustomOptionTrigger[];
98
+ addCustomOptionTriggers?: FieldSelectMultipleAddCustomOptionTrigger[];
99
99
  };
100
100
  export type FieldSelectProps = (FieldSelectSingleProps & {
101
101
  selection?: 'single';
@@ -0,0 +1,4 @@
1
+ import { KeyboardEvent } from 'react';
2
+ import { FieldSelectMultipleAddCustomOptionTrigger } from '../types';
3
+ export declare const getCustomOptionTriggerByCode: (code: KeyboardEvent<HTMLInputElement>["code"]) => FieldSelectMultipleAddCustomOptionTrigger | undefined;
4
+ export declare const shouldHandleCustomOptionTrigger: (trigger: FieldSelectMultipleAddCustomOptionTrigger | undefined, availableTriggers: FieldSelectMultipleAddCustomOptionTrigger[]) => trigger is FieldSelectMultipleAddCustomOptionTrigger;
@@ -5,4 +5,5 @@ export * from './updateItems';
5
5
  export * from './getArrowIcon';
6
6
  export * from './getValueByPath';
7
7
  export * from './checkisSearchUnavailable';
8
- export * from './getCustomOptionTriggerByCode';
8
+ export * from './customOption';
9
+ export * from './filterItemsByFlattenIds';
@@ -29,4 +29,5 @@ __exportStar(require("./updateItems"), exports);
29
29
  __exportStar(require("./getArrowIcon"), exports);
30
30
  __exportStar(require("./getValueByPath"), exports);
31
31
  __exportStar(require("./checkisSearchUnavailable"), exports);
32
- __exportStar(require("./getCustomOptionTriggerByCode"), exports);
32
+ __exportStar(require("./customOption"), exports);
33
+ __exportStar(require("./filterItemsByFlattenIds"), exports);
@@ -1,4 +1,4 @@
1
- import { FieldSelectAddCustomOptionTrigger, SelectedOptionFormatter } from './types';
1
+ import { SelectedOptionFormatter } from './types';
2
2
  export declare const FieldSelectMultiple: import("react").ForwardRefExoticComponent<import("./types").InputProps & import("./types").WrapperProps & {
3
3
  options: import("./types").OptionProps[];
4
4
  loading?: boolean;
@@ -29,5 +29,5 @@ export declare const FieldSelectMultiple: import("react").ForwardRefExoticCompon
29
29
  onOpenChange?(open: boolean): void;
30
30
  selectedOptionFormatter?: SelectedOptionFormatter;
31
31
  } & Pick<import("@snack-uikit/list").DroplistProps, "onScroll" | "scrollRef" | "scrollContainerRef" | "untouchableScrollbars" | "dataError" | "dataFiltered" | "scrollToSelectedItem" | "virtualized" | "noDataState" | "noResultsState" | "errorDataState">, "showCopyButton" | "onCopyButtonClick"> & {
32
- addCustomOptionTriggers?: FieldSelectAddCustomOptionTrigger[];
32
+ addCustomOptionTriggers?: import("./types").FieldSelectMultipleAddCustomOptionTrigger[];
33
33
  } & import("react").RefAttributes<HTMLInputElement>>;
@@ -22,7 +22,7 @@ import { usePostfix, usePrefix, useValueControl } from '../../hooks';
22
22
  import { getValidationState } from '../../utils/getValidationState';
23
23
  import { FieldDecorator } from '../FieldDecorator';
24
24
  import { extractFieldDecoratorProps } from '../FieldDecorator/utils';
25
- import { useButtons, useHandleDeleteItem, useHandleOnKeyDown, useResolvedAddCustomOptionTriggers, useSearch, useSearchInput, } from './hooks';
25
+ import { useButtons, useFieldSelectMultipleCustomOption, useHandleDeleteItem, useHandleOnKeyDown, useSearch, useSearchInput, } from './hooks';
26
26
  import styles from './styles.module.css';
27
27
  import { checkisSearchUnavailable, extractListProps, getArrowIcon, getCustomOptionTriggerByCode, shouldHandleCustomOptionTrigger, updateMultipleItems, } from './utils';
28
28
  const BASE_MIN_WIDTH = 4;
@@ -45,19 +45,14 @@ export const FieldSelectMultiple = forwardRef((props, ref) => {
45
45
  const [{ selectedItems, items = [] }, setItems] = useState(() => updateMultipleItems({ options, value, currentItems: [], selectedItems: undefined }));
46
46
  const { inputValue, setInputValue, prevInputValue, updateInputValue } = useSearchInput(Object.assign(Object.assign({}, search), { defaultValue: '', selectedOptionFormatter,
47
47
  resetSearchOnOptionSelection }));
48
- const resolvedAddCustomOptionTriggers = useResolvedAddCustomOptionTriggers({
48
+ const { resolvedAddCustomOptionTriggers, tryCommitCustomOptionFromInput } = useFieldSelectMultipleCustomOption({
49
49
  addCustomOptionTriggers,
50
50
  addOptionByEnter,
51
+ inputValue,
52
+ value,
53
+ setValue,
54
+ updateInputValue,
51
55
  });
52
- const tryCommitCustomOptionFromInput = useCallback((trigger) => {
53
- if (!resolvedAddCustomOptionTriggers.includes(trigger) || inputValue === '') {
54
- return;
55
- }
56
- if (!(value !== null && value !== void 0 ? value : []).includes(inputValue)) {
57
- setValue((prev) => (prev !== null && prev !== void 0 ? prev : []).concat(inputValue));
58
- updateInputValue();
59
- }
60
- }, [resolvedAddCustomOptionTriggers, inputValue, value, setValue, updateInputValue]);
61
56
  const prefixSettings = usePrefix({ prefix, disabled });
62
57
  const postfixSettings = usePostfix({ postfix, disabled });
63
58
  useLayoutEffect(() => {
@@ -21,7 +21,7 @@ import { usePostfix, usePrefix, useValueControl } from '../../hooks';
21
21
  import { getValidationState } from '../../utils/getValidationState';
22
22
  import { FieldDecorator } from '../FieldDecorator';
23
23
  import { extractFieldDecoratorProps } from '../FieldDecorator/utils';
24
- import { useButtons, useHandleOnKeyDown, useResolvedAddCustomOptionTriggers, useSearch, useSearchInput } from './hooks';
24
+ import { useButtons, useFieldSelectSingleCustomOption, useHandleOnKeyDown, useSearch, useSearchInput } from './hooks';
25
25
  import styles from './styles.module.css';
26
26
  import { checkisSearchUnavailable, extractListProps, getArrowIcon, shouldHandleCustomOptionTrigger, updateItems, } from './utils';
27
27
  const defaultSelectedOptionFormatter = item =>
@@ -40,10 +40,6 @@ export const FieldSelectSingle = forwardRef((props, ref) => {
40
40
  const [{ selectedItem, items = [] }, setItems] = useState(() => updateItems({ options, value, currentItems: [], selectedItem: undefined }));
41
41
  const { inputValue, setInputValue, prevInputValue, updateInputValue } = useSearchInput(Object.assign(Object.assign({}, search), { defaultValue: selectedOptionFormatter(selectedItem), resetSearchOnOptionSelection,
42
42
  selectedOptionFormatter }));
43
- const resolvedAddCustomOptionTriggers = useResolvedAddCustomOptionTriggers({
44
- addCustomOptionTriggers: addCustomOptionTriggersProp,
45
- addOptionByEnter,
46
- });
47
43
  const prevSelectedItem = useRef(selectedItem);
48
44
  const prefixSettings = usePrefix({ prefix, disabled });
49
45
  const postfixSettings = usePostfix({ postfix, disabled });
@@ -94,12 +90,12 @@ export const FieldSelectSingle = forwardRef((props, ref) => {
94
90
  setValue(newValue);
95
91
  }
96
92
  }, [setOpen, setValue]);
97
- const tryCommitCustomOptionFromInput = useCallback((trigger) => {
98
- if (!resolvedAddCustomOptionTriggers.includes(trigger) || inputValue === '') {
99
- return;
100
- }
101
- handleSelectionChange(inputValue);
102
- }, [resolvedAddCustomOptionTriggers, inputValue, handleSelectionChange]);
93
+ const { resolvedAddCustomOptionTriggers, tryCommitCustomOptionFromInput } = useFieldSelectSingleCustomOption({
94
+ addCustomOptionTriggers: addCustomOptionTriggersProp,
95
+ addOptionByEnter,
96
+ inputValue,
97
+ handleSelectionChange,
98
+ });
103
99
  const handleBlur = (e) => {
104
100
  var _a;
105
101
  if (!open && !buttonsRefs.filter(Boolean).includes(e.relatedTarget)) {
@@ -1,7 +1,7 @@
1
1
  import { KeyboardEvent, KeyboardEventHandler, MouseEvent, RefObject } from 'react';
2
2
  import { Handler } from 'uncontrollable';
3
3
  import { ItemProps } from '@snack-uikit/list';
4
- import { FieldSelectAddCustomOptionTrigger, ItemWithId, SearchState, SelectedOptionFormatter } from './types';
4
+ import { ItemWithId, SearchState, SelectedOptionFormatter } from '../types';
5
5
  type UseHandleOnKeyDownProps = {
6
6
  inputKeyDownNavigationHandler: KeyboardEventHandler<HTMLInputElement>;
7
7
  onInputKeyDownProp: KeyboardEventHandler<HTMLInputElement> | undefined;
@@ -38,9 +38,4 @@ export declare function useHandleDeleteItem(setValue: Handler): (item?: ItemWith
38
38
  * Четкий и нечеткий поиск среди айтемов по полям 'content.option', 'content.caption', 'content.description', 'label'
39
39
  */
40
40
  export declare function useSearch(items: ItemProps[], enableFuzzySearch?: boolean): (search: string) => import("@snack-uikit/list/dist/esm/components/Items").Item[];
41
- type UseResolvedAddCustomOptionTriggersProps = {
42
- addCustomOptionTriggers?: FieldSelectAddCustomOptionTrigger[];
43
- addOptionByEnter: boolean;
44
- };
45
- export declare function useResolvedAddCustomOptionTriggers({ addCustomOptionTriggers, addOptionByEnter, }: UseResolvedAddCustomOptionTriggersProps): FieldSelectAddCustomOptionTrigger[];
46
41
  export {};
@@ -2,10 +2,9 @@ import FuzzySearch from 'fuzzy-search';
2
2
  import { useCallback, useMemo, useRef } from 'react';
3
3
  import { useButtonNavigation, useClearButton } from '@snack-uikit/input-private';
4
4
  import { isAccordionItemProps, isNextListItemProps, kindFlattenItems, } from '@snack-uikit/list';
5
- import { useCopyButton, useValueControl } from '../../hooks';
6
- import { extractChildIds } from './legacy';
7
- import { getValueByPath, isBaseOptionProps } from './utils';
8
- import { filterItemsByFlattenIds } from './utils/filterItemsByFlattenIds';
5
+ import { useCopyButton, useValueControl } from '../../../hooks';
6
+ import { extractChildIds } from '../legacy';
7
+ import { filterItemsByFlattenIds, getValueByPath, isBaseOptionProps } from '../utils';
9
8
  export function useHandleOnKeyDown({ setOpen, inputKeyDownNavigationHandler, onInputKeyDownProp, }) {
10
9
  return useCallback((onKeyDown) => (e) => {
11
10
  if (e.code === 'Space') {
@@ -117,11 +116,3 @@ export function useSearch(items, enableFuzzySearch = true) {
117
116
  }, [filterFlattenFunction, items]);
118
117
  return useCallback((search) => (search.length >= DEFAULT_MIN_SEARCH_INPUT_LENGTH ? filterFunction(search) : items), [filterFunction, items]);
119
118
  }
120
- export function useResolvedAddCustomOptionTriggers({ addCustomOptionTriggers, addOptionByEnter, }) {
121
- return useMemo(() => {
122
- if (addCustomOptionTriggers !== undefined) {
123
- return addCustomOptionTriggers;
124
- }
125
- return addOptionByEnter ? ['enter'] : [];
126
- }, [addCustomOptionTriggers, addOptionByEnter]);
127
- }
@@ -0,0 +1,26 @@
1
+ import { Handler } from 'uncontrollable';
2
+ import { SelectionSingleValueType } from '@snack-uikit/list';
3
+ import { FieldSelectMultipleAddCustomOptionTrigger } from '../types';
4
+ type UseResolvedAddCustomOptionTriggersParams = {
5
+ addCustomOptionTriggers?: FieldSelectMultipleAddCustomOptionTrigger[];
6
+ addOptionByEnter: boolean;
7
+ };
8
+ type UseFieldSelectMultipleCustomOptionParams = UseResolvedAddCustomOptionTriggersParams & {
9
+ inputValue: string;
10
+ value: SelectionSingleValueType[] | undefined;
11
+ setValue: Handler;
12
+ updateInputValue: () => void;
13
+ };
14
+ export declare function useFieldSelectMultipleCustomOption({ addCustomOptionTriggers, addOptionByEnter, inputValue, value, setValue, updateInputValue, }: UseFieldSelectMultipleCustomOptionParams): {
15
+ resolvedAddCustomOptionTriggers: FieldSelectMultipleAddCustomOptionTrigger[];
16
+ tryCommitCustomOptionFromInput: (trigger: FieldSelectMultipleAddCustomOptionTrigger) => void;
17
+ };
18
+ type UseFieldSelectSingleCustomOptionParams = UseResolvedAddCustomOptionTriggersParams & {
19
+ inputValue: string;
20
+ handleSelectionChange: (value: SelectionSingleValueType) => void;
21
+ };
22
+ export declare function useFieldSelectSingleCustomOption({ addCustomOptionTriggers, addOptionByEnter, inputValue, handleSelectionChange, }: UseFieldSelectSingleCustomOptionParams): {
23
+ resolvedAddCustomOptionTriggers: FieldSelectMultipleAddCustomOptionTrigger[];
24
+ tryCommitCustomOptionFromInput: (trigger: FieldSelectMultipleAddCustomOptionTrigger) => void;
25
+ };
26
+ export {};
@@ -0,0 +1,38 @@
1
+ import { useCallback, useMemo } from 'react';
2
+ function useResolvedAddCustomOptionTriggers({ addCustomOptionTriggers, addOptionByEnter, }) {
3
+ return useMemo(() => {
4
+ if (addCustomOptionTriggers !== undefined) {
5
+ return addCustomOptionTriggers;
6
+ }
7
+ return addOptionByEnter ? ['enter'] : [];
8
+ }, [addCustomOptionTriggers, addOptionByEnter]);
9
+ }
10
+ export function useFieldSelectMultipleCustomOption({ addCustomOptionTriggers, addOptionByEnter, inputValue, value, setValue, updateInputValue, }) {
11
+ const resolvedAddCustomOptionTriggers = useResolvedAddCustomOptionTriggers({
12
+ addCustomOptionTriggers,
13
+ addOptionByEnter,
14
+ });
15
+ const tryCommitCustomOptionFromInput = useCallback((trigger) => {
16
+ if (!resolvedAddCustomOptionTriggers.includes(trigger) || inputValue === '') {
17
+ return;
18
+ }
19
+ if (!(value !== null && value !== void 0 ? value : []).includes(inputValue)) {
20
+ setValue((prev) => (prev !== null && prev !== void 0 ? prev : []).concat(inputValue));
21
+ updateInputValue();
22
+ }
23
+ }, [resolvedAddCustomOptionTriggers, inputValue, value, setValue, updateInputValue]);
24
+ return { resolvedAddCustomOptionTriggers, tryCommitCustomOptionFromInput };
25
+ }
26
+ export function useFieldSelectSingleCustomOption({ addCustomOptionTriggers, addOptionByEnter, inputValue, handleSelectionChange, }) {
27
+ const resolvedAddCustomOptionTriggers = useResolvedAddCustomOptionTriggers({
28
+ addCustomOptionTriggers,
29
+ addOptionByEnter,
30
+ });
31
+ const tryCommitCustomOptionFromInput = useCallback((trigger) => {
32
+ if (!resolvedAddCustomOptionTriggers.includes(trigger) || inputValue === '') {
33
+ return;
34
+ }
35
+ handleSelectionChange(inputValue);
36
+ }, [resolvedAddCustomOptionTriggers, inputValue, handleSelectionChange]);
37
+ return { resolvedAddCustomOptionTriggers, tryCommitCustomOptionFromInput };
38
+ }
@@ -0,0 +1,2 @@
1
+ export * from './common';
2
+ export * from './customOption';
@@ -0,0 +1,2 @@
1
+ export * from './common';
2
+ export * from './customOption';
@@ -1,2 +1,2 @@
1
1
  export { FieldSelect } from './FieldSelect';
2
- export type { FieldSelectAddCustomOptionTrigger, FieldSelectSingleAddCustomOptionTrigger, FieldSelectSingleProps, FieldSelectMultipleProps, FieldSelectProps, OptionProps, BaseOptionProps, AccordionOptionProps, NestListOptionProps, GroupOptionProps, SelectedOptionFormatter, } from './types';
2
+ export type { FieldSelectMultipleAddCustomOptionTrigger, FieldSelectSingleAddCustomOptionTrigger, FieldSelectSingleProps, FieldSelectMultipleProps, FieldSelectProps, OptionProps, BaseOptionProps, AccordionOptionProps, NestListOptionProps, GroupOptionProps, SelectedOptionFormatter, } from './types';
@@ -32,8 +32,8 @@ export type SearchState = {
32
32
  onChange?(value: string): void;
33
33
  };
34
34
  /** События, по которым произвольное значение из строки поиска фиксируется в значении поля */
35
- export type FieldSelectAddCustomOptionTrigger = 'enter' | 'blur' | 'space' | 'comma';
36
- export type FieldSelectSingleAddCustomOptionTrigger = Extract<FieldSelectAddCustomOptionTrigger, 'enter' | 'blur'>;
35
+ export type FieldSelectMultipleAddCustomOptionTrigger = 'enter' | 'blur' | 'space' | 'comma';
36
+ export type FieldSelectSingleAddCustomOptionTrigger = Extract<FieldSelectMultipleAddCustomOptionTrigger, 'enter' | 'blur'>;
37
37
  export type FieldSelectPrivateProps = InputProps & WrapperProps & {
38
38
  options: OptionProps[];
39
39
  loading?: boolean;
@@ -95,7 +95,7 @@ export type FieldSelectMultipleProps = FieldSelectPrivateProps & {
95
95
  * Триггеры фиксации произвольного значения из строки поиска в режиме `multiple`.
96
96
  * Если передан, имеет приоритет над устаревшим `addOptionByEnter`.
97
97
  */
98
- addCustomOptionTriggers?: FieldSelectAddCustomOptionTrigger[];
98
+ addCustomOptionTriggers?: FieldSelectMultipleAddCustomOptionTrigger[];
99
99
  };
100
100
  export type FieldSelectProps = (FieldSelectSingleProps & {
101
101
  selection?: 'single';
@@ -0,0 +1,4 @@
1
+ import { KeyboardEvent } from 'react';
2
+ import { FieldSelectMultipleAddCustomOptionTrigger } from '../types';
3
+ export declare const getCustomOptionTriggerByCode: (code: KeyboardEvent<HTMLInputElement>["code"]) => FieldSelectMultipleAddCustomOptionTrigger | undefined;
4
+ export declare const shouldHandleCustomOptionTrigger: (trigger: FieldSelectMultipleAddCustomOptionTrigger | undefined, availableTriggers: FieldSelectMultipleAddCustomOptionTrigger[]) => trigger is FieldSelectMultipleAddCustomOptionTrigger;
@@ -5,4 +5,5 @@ export * from './updateItems';
5
5
  export * from './getArrowIcon';
6
6
  export * from './getValueByPath';
7
7
  export * from './checkisSearchUnavailable';
8
- export * from './getCustomOptionTriggerByCode';
8
+ export * from './customOption';
9
+ export * from './filterItemsByFlattenIds';
@@ -5,4 +5,5 @@ export * from './updateItems';
5
5
  export * from './getArrowIcon';
6
6
  export * from './getValueByPath';
7
7
  export * from './checkisSearchUnavailable';
8
- export * from './getCustomOptionTriggerByCode';
8
+ export * from './customOption';
9
+ export * from './filterItemsByFlattenIds';
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public"
5
5
  },
6
6
  "title": "Fields",
7
- "version": "0.53.2-preview-0be0d183.0",
7
+ "version": "0.54.0",
8
8
  "sideEffects": [
9
9
  "*.css",
10
10
  "*.woff",
@@ -66,5 +66,5 @@
66
66
  "peerDependencies": {
67
67
  "@snack-uikit/locale": "*"
68
68
  },
69
- "gitHead": "5eb2aad3219d953d4e2ea540da79a84bb03ee4c1"
69
+ "gitHead": "91f67a065fb1a5b9b57c9c03374c752e3acf3a0f"
70
70
  }
@@ -23,19 +23,14 @@ import { FieldDecorator } from '../FieldDecorator';
23
23
  import { extractFieldDecoratorProps } from '../FieldDecorator/utils';
24
24
  import {
25
25
  useButtons,
26
+ useFieldSelectMultipleCustomOption,
26
27
  useHandleDeleteItem,
27
28
  useHandleOnKeyDown,
28
- useResolvedAddCustomOptionTriggers,
29
29
  useSearch,
30
30
  useSearchInput,
31
31
  } from './hooks';
32
32
  import styles from './styles.module.scss';
33
- import {
34
- FieldSelectAddCustomOptionTrigger,
35
- FieldSelectMultipleProps,
36
- ItemWithId,
37
- SelectedOptionFormatter,
38
- } from './types';
33
+ import { FieldSelectMultipleProps, ItemWithId, SelectedOptionFormatter } from './types';
39
34
  import {
40
35
  checkisSearchUnavailable,
41
36
  extractListProps,
@@ -109,25 +104,15 @@ export const FieldSelectMultiple = forwardRef<HTMLInputElement, FieldSelectMulti
109
104
  resetSearchOnOptionSelection,
110
105
  });
111
106
 
112
- const resolvedAddCustomOptionTriggers = useResolvedAddCustomOptionTriggers({
107
+ const { resolvedAddCustomOptionTriggers, tryCommitCustomOptionFromInput } = useFieldSelectMultipleCustomOption({
113
108
  addCustomOptionTriggers,
114
109
  addOptionByEnter,
110
+ inputValue,
111
+ value,
112
+ setValue,
113
+ updateInputValue,
115
114
  });
116
115
 
117
- const tryCommitCustomOptionFromInput = useCallback(
118
- (trigger: FieldSelectAddCustomOptionTrigger) => {
119
- if (!resolvedAddCustomOptionTriggers.includes(trigger) || inputValue === '') {
120
- return;
121
- }
122
-
123
- if (!(value ?? []).includes(inputValue)) {
124
- setValue((prev: SelectionSingleValueType[]) => (prev ?? []).concat(inputValue));
125
- updateInputValue();
126
- }
127
- },
128
- [resolvedAddCustomOptionTriggers, inputValue, value, setValue, updateInputValue],
129
- );
130
-
131
116
  const prefixSettings = usePrefix({ prefix, disabled });
132
117
  const postfixSettings = usePostfix({ postfix, disabled });
133
118
 
@@ -20,14 +20,9 @@ import { usePostfix, usePrefix, useValueControl } from '../../hooks';
20
20
  import { getValidationState } from '../../utils/getValidationState';
21
21
  import { FieldDecorator } from '../FieldDecorator';
22
22
  import { extractFieldDecoratorProps } from '../FieldDecorator/utils';
23
- import { useButtons, useHandleOnKeyDown, useResolvedAddCustomOptionTriggers, useSearch, useSearchInput } from './hooks';
23
+ import { useButtons, useFieldSelectSingleCustomOption, useHandleOnKeyDown, useSearch, useSearchInput } from './hooks';
24
24
  import styles from './styles.module.scss';
25
- import {
26
- FieldSelectAddCustomOptionTrigger,
27
- FieldSelectSingleProps,
28
- ItemWithId,
29
- SelectedOptionFormatter,
30
- } from './types';
25
+ import { FieldSelectSingleProps, ItemWithId, SelectedOptionFormatter } from './types';
31
26
  import {
32
27
  checkisSearchUnavailable,
33
28
  extractListProps,
@@ -96,11 +91,6 @@ export const FieldSelectSingle = forwardRef<HTMLInputElement, FieldSelectSingleP
96
91
  selectedOptionFormatter,
97
92
  });
98
93
 
99
- const resolvedAddCustomOptionTriggers = useResolvedAddCustomOptionTriggers({
100
- addCustomOptionTriggers: addCustomOptionTriggersProp,
101
- addOptionByEnter,
102
- });
103
-
104
94
  const prevSelectedItem = useRef<ItemWithId | undefined>(selectedItem);
105
95
 
106
96
  const prefixSettings = usePrefix({ prefix, disabled });
@@ -166,16 +156,12 @@ export const FieldSelectSingle = forwardRef<HTMLInputElement, FieldSelectSingleP
166
156
  [setOpen, setValue],
167
157
  );
168
158
 
169
- const tryCommitCustomOptionFromInput = useCallback(
170
- (trigger: FieldSelectAddCustomOptionTrigger) => {
171
- if (!resolvedAddCustomOptionTriggers.includes(trigger) || inputValue === '') {
172
- return;
173
- }
174
-
175
- handleSelectionChange(inputValue);
176
- },
177
- [resolvedAddCustomOptionTriggers, inputValue, handleSelectionChange],
178
- );
159
+ const { resolvedAddCustomOptionTriggers, tryCommitCustomOptionFromInput } = useFieldSelectSingleCustomOption({
160
+ addCustomOptionTriggers: addCustomOptionTriggersProp,
161
+ addOptionByEnter,
162
+ inputValue,
163
+ handleSelectionChange,
164
+ });
179
165
 
180
166
  const handleBlur = (e: FocusEvent<HTMLInputElement>) => {
181
167
  if (!open && !buttonsRefs.filter(Boolean).includes(e.relatedTarget)) {
@@ -11,11 +11,10 @@ import {
11
11
  SelectionSingleValueType,
12
12
  } from '@snack-uikit/list';
13
13
 
14
- import { useCopyButton, useValueControl } from '../../hooks';
15
- import { extractChildIds } from './legacy';
16
- import { FieldSelectAddCustomOptionTrigger, ItemWithId, SearchState, SelectedOptionFormatter } from './types';
17
- import { getValueByPath, isBaseOptionProps } from './utils';
18
- import { filterItemsByFlattenIds } from './utils/filterItemsByFlattenIds';
14
+ import { useCopyButton, useValueControl } from '../../../hooks';
15
+ import { extractChildIds } from '../legacy';
16
+ import { ItemWithId, SearchState, SelectedOptionFormatter } from '../types';
17
+ import { filterItemsByFlattenIds, getValueByPath, isBaseOptionProps } from '../utils';
19
18
 
20
19
  type UseHandleOnKeyDownProps = {
21
20
  inputKeyDownNavigationHandler: KeyboardEventHandler<HTMLInputElement>;
@@ -218,21 +217,3 @@ export function useSearch(items: ItemProps[], enableFuzzySearch: boolean = true)
218
217
  [filterFunction, items],
219
218
  );
220
219
  }
221
-
222
- type UseResolvedAddCustomOptionTriggersProps = {
223
- addCustomOptionTriggers?: FieldSelectAddCustomOptionTrigger[];
224
- addOptionByEnter: boolean;
225
- };
226
-
227
- export function useResolvedAddCustomOptionTriggers({
228
- addCustomOptionTriggers,
229
- addOptionByEnter,
230
- }: UseResolvedAddCustomOptionTriggersProps): FieldSelectAddCustomOptionTrigger[] {
231
- return useMemo(() => {
232
- if (addCustomOptionTriggers !== undefined) {
233
- return addCustomOptionTriggers;
234
- }
235
-
236
- return addOptionByEnter ? ['enter'] : [];
237
- }, [addCustomOptionTriggers, addOptionByEnter]);
238
- }
@@ -0,0 +1,90 @@
1
+ import { useCallback, useMemo } from 'react';
2
+ import { Handler } from 'uncontrollable';
3
+
4
+ import { SelectionSingleValueType } from '@snack-uikit/list';
5
+
6
+ import { FieldSelectMultipleAddCustomOptionTrigger } from '../types';
7
+
8
+ type UseResolvedAddCustomOptionTriggersParams = {
9
+ addCustomOptionTriggers?: FieldSelectMultipleAddCustomOptionTrigger[];
10
+ addOptionByEnter: boolean;
11
+ };
12
+
13
+ function useResolvedAddCustomOptionTriggers({
14
+ addCustomOptionTriggers,
15
+ addOptionByEnter,
16
+ }: UseResolvedAddCustomOptionTriggersParams): FieldSelectMultipleAddCustomOptionTrigger[] {
17
+ return useMemo(() => {
18
+ if (addCustomOptionTriggers !== undefined) {
19
+ return addCustomOptionTriggers;
20
+ }
21
+
22
+ return addOptionByEnter ? ['enter'] : [];
23
+ }, [addCustomOptionTriggers, addOptionByEnter]);
24
+ }
25
+
26
+ type UseFieldSelectMultipleCustomOptionParams = UseResolvedAddCustomOptionTriggersParams & {
27
+ inputValue: string;
28
+ value: SelectionSingleValueType[] | undefined;
29
+ setValue: Handler;
30
+ updateInputValue: () => void;
31
+ };
32
+ export function useFieldSelectMultipleCustomOption({
33
+ addCustomOptionTriggers,
34
+ addOptionByEnter,
35
+ inputValue,
36
+ value,
37
+ setValue,
38
+ updateInputValue,
39
+ }: UseFieldSelectMultipleCustomOptionParams) {
40
+ const resolvedAddCustomOptionTriggers = useResolvedAddCustomOptionTriggers({
41
+ addCustomOptionTriggers,
42
+ addOptionByEnter,
43
+ });
44
+
45
+ const tryCommitCustomOptionFromInput = useCallback(
46
+ (trigger: FieldSelectMultipleAddCustomOptionTrigger) => {
47
+ if (!resolvedAddCustomOptionTriggers.includes(trigger) || inputValue === '') {
48
+ return;
49
+ }
50
+
51
+ if (!(value ?? []).includes(inputValue)) {
52
+ setValue((prev: SelectionSingleValueType[]) => (prev ?? []).concat(inputValue));
53
+ updateInputValue();
54
+ }
55
+ },
56
+ [resolvedAddCustomOptionTriggers, inputValue, value, setValue, updateInputValue],
57
+ );
58
+
59
+ return { resolvedAddCustomOptionTriggers, tryCommitCustomOptionFromInput };
60
+ }
61
+
62
+ type UseFieldSelectSingleCustomOptionParams = UseResolvedAddCustomOptionTriggersParams & {
63
+ inputValue: string;
64
+ handleSelectionChange: (value: SelectionSingleValueType) => void;
65
+ };
66
+
67
+ export function useFieldSelectSingleCustomOption({
68
+ addCustomOptionTriggers,
69
+ addOptionByEnter,
70
+ inputValue,
71
+ handleSelectionChange,
72
+ }: UseFieldSelectSingleCustomOptionParams) {
73
+ const resolvedAddCustomOptionTriggers = useResolvedAddCustomOptionTriggers({
74
+ addCustomOptionTriggers,
75
+ addOptionByEnter,
76
+ });
77
+
78
+ const tryCommitCustomOptionFromInput = useCallback(
79
+ (trigger: FieldSelectMultipleAddCustomOptionTrigger) => {
80
+ if (!resolvedAddCustomOptionTriggers.includes(trigger) || inputValue === '') {
81
+ return;
82
+ }
83
+
84
+ handleSelectionChange(inputValue);
85
+ },
86
+ [resolvedAddCustomOptionTriggers, inputValue, handleSelectionChange],
87
+ );
88
+
89
+ return { resolvedAddCustomOptionTriggers, tryCommitCustomOptionFromInput };
90
+ }
@@ -0,0 +1,2 @@
1
+ export * from './common';
2
+ export * from './customOption';
@@ -1,7 +1,7 @@
1
1
  export { FieldSelect } from './FieldSelect';
2
2
 
3
3
  export type {
4
- FieldSelectAddCustomOptionTrigger,
4
+ FieldSelectMultipleAddCustomOptionTrigger,
5
5
  FieldSelectSingleAddCustomOptionTrigger,
6
6
  FieldSelectSingleProps,
7
7
  FieldSelectMultipleProps,
@@ -71,8 +71,11 @@ export type SearchState = {
71
71
  };
72
72
 
73
73
  /** События, по которым произвольное значение из строки поиска фиксируется в значении поля */
74
- export type FieldSelectAddCustomOptionTrigger = 'enter' | 'blur' | 'space' | 'comma';
75
- export type FieldSelectSingleAddCustomOptionTrigger = Extract<FieldSelectAddCustomOptionTrigger, 'enter' | 'blur'>;
74
+ export type FieldSelectMultipleAddCustomOptionTrigger = 'enter' | 'blur' | 'space' | 'comma';
75
+ export type FieldSelectSingleAddCustomOptionTrigger = Extract<
76
+ FieldSelectMultipleAddCustomOptionTrigger,
77
+ 'enter' | 'blur'
78
+ >;
76
79
 
77
80
  export type FieldSelectPrivateProps = InputProps &
78
81
  WrapperProps & {
@@ -170,7 +173,7 @@ export type FieldSelectMultipleProps = FieldSelectPrivateProps & {
170
173
  * Триггеры фиксации произвольного значения из строки поиска в режиме `multiple`.
171
174
  * Если передан, имеет приоритет над устаревшим `addOptionByEnter`.
172
175
  */
173
- addCustomOptionTriggers?: FieldSelectAddCustomOptionTrigger[];
176
+ addCustomOptionTriggers?: FieldSelectMultipleAddCustomOptionTrigger[];
174
177
  };
175
178
 
176
179
  export type FieldSelectProps =
@@ -0,0 +1,23 @@
1
+ import { KeyboardEvent } from 'react';
2
+
3
+ import { FieldSelectMultipleAddCustomOptionTrigger } from '../types';
4
+
5
+ export const getCustomOptionTriggerByCode = (
6
+ code: KeyboardEvent<HTMLInputElement>['code'],
7
+ ): FieldSelectMultipleAddCustomOptionTrigger | undefined => {
8
+ switch (code) {
9
+ case 'Enter':
10
+ return 'enter';
11
+ case 'Space':
12
+ return 'space';
13
+ case 'Comma':
14
+ return 'comma';
15
+ default:
16
+ return undefined;
17
+ }
18
+ };
19
+
20
+ export const shouldHandleCustomOptionTrigger = (
21
+ trigger: FieldSelectMultipleAddCustomOptionTrigger | undefined,
22
+ availableTriggers: FieldSelectMultipleAddCustomOptionTrigger[],
23
+ ): trigger is FieldSelectMultipleAddCustomOptionTrigger => (trigger ? availableTriggers.includes(trigger) : false);
@@ -5,4 +5,5 @@ export * from './updateItems';
5
5
  export * from './getArrowIcon';
6
6
  export * from './getValueByPath';
7
7
  export * from './checkisSearchUnavailable';
8
- export * from './getCustomOptionTriggerByCode';
8
+ export * from './customOption';
9
+ export * from './filterItemsByFlattenIds';
@@ -1,4 +0,0 @@
1
- import { KeyboardEvent } from 'react';
2
- import { FieldSelectAddCustomOptionTrigger } from '../types';
3
- export declare const getCustomOptionTriggerByCode: (code: KeyboardEvent<HTMLInputElement>["code"]) => FieldSelectAddCustomOptionTrigger | undefined;
4
- export declare const shouldHandleCustomOptionTrigger: (trigger: FieldSelectAddCustomOptionTrigger | undefined, availableTriggers: FieldSelectAddCustomOptionTrigger[]) => trigger is FieldSelectAddCustomOptionTrigger;
@@ -1,4 +0,0 @@
1
- import { KeyboardEvent } from 'react';
2
- import { FieldSelectAddCustomOptionTrigger } from '../types';
3
- export declare const getCustomOptionTriggerByCode: (code: KeyboardEvent<HTMLInputElement>["code"]) => FieldSelectAddCustomOptionTrigger | undefined;
4
- export declare const shouldHandleCustomOptionTrigger: (trigger: FieldSelectAddCustomOptionTrigger | undefined, availableTriggers: FieldSelectAddCustomOptionTrigger[]) => trigger is FieldSelectAddCustomOptionTrigger;
@@ -1,23 +0,0 @@
1
- import { KeyboardEvent } from 'react';
2
-
3
- import { FieldSelectAddCustomOptionTrigger } from '../types';
4
-
5
- export const getCustomOptionTriggerByCode = (
6
- code: KeyboardEvent<HTMLInputElement>['code'],
7
- ): FieldSelectAddCustomOptionTrigger | undefined => {
8
- switch (code) {
9
- case 'Enter':
10
- return 'enter';
11
- case 'Space':
12
- return 'space';
13
- case 'Comma':
14
- return 'comma';
15
- default:
16
- return undefined;
17
- }
18
- };
19
-
20
- export const shouldHandleCustomOptionTrigger = (
21
- trigger: FieldSelectAddCustomOptionTrigger | undefined,
22
- availableTriggers: FieldSelectAddCustomOptionTrigger[],
23
- ): trigger is FieldSelectAddCustomOptionTrigger => (trigger ? availableTriggers.includes(trigger) : false);