@snack-uikit/fields 0.18.3 → 0.19.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 (41) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +0 -204
  3. package/dist/components/FieldDate/FieldDate.js +1 -1
  4. package/dist/components/FieldSelect/FieldSelectMultiple.d.ts +3 -1
  5. package/dist/components/FieldSelect/FieldSelectMultiple.js +9 -2
  6. package/dist/components/FieldSelect/FieldSelectSingle.d.ts +3 -1
  7. package/dist/components/FieldSelect/FieldSelectSingle.js +16 -4
  8. package/dist/components/FieldSelect/hooks.js +8 -2
  9. package/dist/components/FieldSelect/legacy/components/Items/hooks.d.ts +12 -0
  10. package/dist/components/FieldSelect/legacy/components/Items/hooks.js +33 -0
  11. package/dist/components/FieldSelect/legacy/components/index.d.ts +1 -0
  12. package/dist/components/FieldSelect/legacy/components/index.js +1 -0
  13. package/dist/components/FieldSelect/legacy/hooks.d.ts +5 -0
  14. package/dist/components/FieldSelect/legacy/hooks.js +19 -0
  15. package/dist/components/FieldSelect/legacy/index.d.ts +3 -0
  16. package/dist/components/FieldSelect/legacy/index.js +3 -0
  17. package/dist/components/FieldSelect/legacy/utils.d.ts +29 -0
  18. package/dist/components/FieldSelect/legacy/utils.js +107 -0
  19. package/dist/components/FieldSelect/types.d.ts +6 -3
  20. package/dist/components/FieldSelect/utils/extractListProps.d.ts +1 -1
  21. package/dist/components/FieldSelect/utils/extractListProps.js +1 -3
  22. package/dist/components/FieldSelect/utils/options.js +1 -1
  23. package/dist/components/FieldSelect/utils/typeGuards.js +1 -1
  24. package/dist/components/FieldSelect/utils/updateItems.d.ts +6 -6
  25. package/dist/components/FieldSelect/utils/updateItems.js +12 -3
  26. package/dist/helperComponents/FieldContainerPrivate/styles.module.css +1 -1
  27. package/package.json +15 -13
  28. package/src/components/FieldDate/FieldDate.tsx +1 -1
  29. package/src/components/FieldSelect/FieldSelectMultiple.tsx +10 -2
  30. package/src/components/FieldSelect/FieldSelectSingle.tsx +14 -3
  31. package/src/components/FieldSelect/hooks.ts +11 -2
  32. package/src/components/FieldSelect/legacy/components/Items/hooks.tsx +53 -0
  33. package/src/components/FieldSelect/legacy/components/index.ts +1 -0
  34. package/src/components/FieldSelect/legacy/hooks.ts +32 -0
  35. package/src/components/FieldSelect/legacy/index.ts +3 -0
  36. package/src/components/FieldSelect/legacy/utils.ts +166 -0
  37. package/src/components/FieldSelect/types.ts +29 -12
  38. package/src/components/FieldSelect/utils/extractListProps.ts +0 -4
  39. package/src/components/FieldSelect/utils/options.ts +2 -1
  40. package/src/components/FieldSelect/utils/typeGuards.ts +1 -1
  41. package/src/components/FieldSelect/utils/updateItems.ts +14 -4
@@ -0,0 +1,107 @@
1
+ import { isAccordionItemProps, isBaseItemProps, isGroupItemProps, isNextListItemProps, } from '@snack-uikit/list';
2
+ export function withCollapsedItems({ items, openCollapsedItems }) {
3
+ let itemRefs = [];
4
+ let newItems = [];
5
+ let ids = [];
6
+ let expandedIds = [];
7
+ items.forEach(item => {
8
+ var _a;
9
+ if (((isBaseItemProps(item) && !item.inactive) || isNextListItemProps(item) || isAccordionItemProps(item)) &&
10
+ !item.disabled) {
11
+ newItems = newItems.concat([item]);
12
+ ids = ids.concat([(_a = item.id) !== null && _a !== void 0 ? _a : '']);
13
+ if (item.itemRef) {
14
+ itemRefs = itemRefs.concat([item.itemRef]);
15
+ }
16
+ }
17
+ if (isNextListItemProps(item) && item.id && !item.disabled) {
18
+ expandedIds = expandedIds.concat(item.id);
19
+ }
20
+ if (isGroupItemProps(item)) {
21
+ const { itemRefs: nestedItemsRefs, ids: nestedIds } = withCollapsedItems({
22
+ items: item.items,
23
+ openCollapsedItems,
24
+ });
25
+ ids = ids.concat(nestedIds);
26
+ itemRefs = itemRefs.concat(nestedItemsRefs);
27
+ }
28
+ if (isAccordionItemProps(item) && item.id && openCollapsedItems.includes(item.id)) {
29
+ const { itemRefs: nestedItemsRefs, ids: nestedIds, items: nestedItems, expandedIds: nestedExpandedIds, } = withCollapsedItems({
30
+ items: item.items,
31
+ openCollapsedItems,
32
+ });
33
+ ids = ids.concat(nestedIds);
34
+ newItems = newItems.concat(nestedItems);
35
+ itemRefs = itemRefs.concat(nestedItemsRefs);
36
+ expandedIds = expandedIds.concat(nestedExpandedIds);
37
+ }
38
+ });
39
+ return { items, itemRefs, ids, expandedIds };
40
+ }
41
+ /**
42
+ * Функция возвращает массив id дочерних items
43
+ * @function extractItemIds
44
+ */
45
+ export function extractItemIds(items) {
46
+ return items.reduce((prev, item) => {
47
+ if (isGroupItemProps(item)) {
48
+ return prev.concat(extractItemIds(item.items));
49
+ }
50
+ return item.id ? prev.concat([item.id]) : prev;
51
+ }, []);
52
+ }
53
+ export function extractChildIds({ items }) {
54
+ return items
55
+ .filter(item => isAccordionItemProps(item) ||
56
+ isNextListItemProps(item) ||
57
+ isGroupItemProps(item) ||
58
+ (isBaseItemProps(item) && !item.disabled && !item.inactive))
59
+ .reduce((prev, item) => {
60
+ var _a;
61
+ if (isAccordionItemProps(item) || isNextListItemProps(item)) {
62
+ return prev.concat([(_a = item.id) !== null && _a !== void 0 ? _a : '']).concat(extractChildIds({ items: item.items }));
63
+ }
64
+ if (isGroupItemProps(item)) {
65
+ return prev.concat(extractChildIds({ items: item.items }));
66
+ }
67
+ return item.id && !isGroupItemProps(item) ? prev.concat([item.id]) : prev;
68
+ }, []);
69
+ }
70
+ export function extractAllChildIds({ items }) {
71
+ return items
72
+ .filter(item => isAccordionItemProps(item) ||
73
+ isNextListItemProps(item) ||
74
+ isGroupItemProps(item) ||
75
+ (isBaseItemProps(item) && !item.inactive))
76
+ .reduce((prev, item) => {
77
+ var _a;
78
+ if (isAccordionItemProps(item) || isNextListItemProps(item)) {
79
+ return prev.concat([(_a = item.id) !== null && _a !== void 0 ? _a : '']).concat(extractAllChildIds({ items: item.items }));
80
+ }
81
+ if (isGroupItemProps(item)) {
82
+ return prev.concat(extractAllChildIds({ items: item.items }));
83
+ }
84
+ return item.id && !isGroupItemProps(item) ? prev.concat([item.id]) : prev;
85
+ }, []);
86
+ }
87
+ /**
88
+ *  Функция разворачивает массив айтемов в плоский список
89
+ * @function flattenItems
90
+ */
91
+ export function flattenItems(items) {
92
+ const flattenItems = [];
93
+ function flatten(item) {
94
+ if (!isGroupItemProps(item)) {
95
+ flattenItems.push(item);
96
+ }
97
+ if ('items' in item) {
98
+ for (const nestedItem of item.items) {
99
+ flatten(nestedItem);
100
+ }
101
+ }
102
+ }
103
+ for (const item of items) {
104
+ flatten(item);
105
+ }
106
+ return flattenItems;
107
+ }
@@ -1,12 +1,13 @@
1
1
  import { ReactElement } from 'react';
2
2
  import { InputPrivateProps } from '@snack-uikit/input-private';
3
- import { AccordionItemProps, BaseItemProps, DroplistProps, GroupItemProps, NextListItemProps, SelectionMultipleState, SelectionSingleState } from '@snack-uikit/list';
3
+ import { AccordionItemProps, BaseItemProps, DroplistProps, GroupItemProps, ItemContentProps, NextListItemProps, SelectionMultipleState, SelectionSingleState } from '@snack-uikit/list';
4
4
  import { TagProps } from '@snack-uikit/tag';
5
5
  import { WithSupportProps } from '@snack-uikit/utils';
6
6
  import { FieldDecoratorProps } from '../FieldDecorator';
7
+ export type AnyType = any;
7
8
  export type OptionProps = BaseOptionProps | AccordionOptionProps | GroupOptionProps | NestListOptionProps;
8
9
  export type OptionWithoutGroup = BaseOptionProps | AccordionOptionProps | NestListOptionProps;
9
- export type BaseOptionProps = Pick<BaseItemProps, 'beforeContent' | 'afterContent' | 'disabled'> & BaseItemProps['content'] & {
10
+ export type BaseOptionProps = Pick<BaseItemProps, 'beforeContent' | 'afterContent' | 'disabled'> & Pick<ItemContentProps, 'option' | 'caption' | 'description'> & {
10
11
  value: string | number;
11
12
  } & Pick<TagProps, 'appearance'>;
12
13
  export type AccordionOptionProps = Pick<AccordionItemProps, 'type'> & BaseOptionProps & {
@@ -36,6 +37,8 @@ export type FieldSelectPrivateProps = InputProps & WrapperProps & {
36
37
  };
37
38
  type FiledSelectCommonProps = WithSupportProps<{
38
39
  options: OptionProps[];
40
+ pinTop: OptionProps[];
41
+ pinBottom: OptionProps[];
39
42
  searchable?: boolean;
40
43
  /** Отображение кнопки Копировать для поля (актуально только для `readonly = true`) */
41
44
  showCopyButton?: boolean;
@@ -59,7 +62,7 @@ type FiledSelectCommonProps = WithSupportProps<{
59
62
  open?: boolean;
60
63
  onOpenChange?(open: boolean): void;
61
64
  selectedOptionFormatter?: SelectedOptionFormatter;
62
- }> & Pick<DroplistProps, 'dataError' | 'noDataState' | 'noResultsState' | 'errorDataState' | 'pinTop' | 'pinBottom' | 'dataFiltered'>;
65
+ }> & Pick<DroplistProps, 'dataError' | 'noDataState' | 'noResultsState' | 'errorDataState' | 'dataFiltered'>;
63
66
  export type FieldSelectSingleProps = FieldSelectPrivateProps & Omit<SelectionSingleState, 'mode'> & WrapperProps & FiledSelectCommonProps;
64
67
  export type FieldSelectMultipleProps = FieldSelectPrivateProps & {
65
68
  removeByBackspace?: boolean;
@@ -1,3 +1,3 @@
1
1
  import { DroplistProps } from '@snack-uikit/list';
2
2
  import { FieldSelectProps } from '../types';
3
- export declare function extractListProps({ dataError, noDataState, noResultsState, errorDataState, pinTop, pinBottom, dataFiltered, loading, footer, widthStrategy, }: Partial<FieldSelectProps>): Partial<DroplistProps>;
3
+ export declare function extractListProps({ dataError, noDataState, noResultsState, errorDataState, dataFiltered, loading, footer, widthStrategy, }: Partial<FieldSelectProps>): Partial<DroplistProps>;
@@ -1,11 +1,9 @@
1
- export function extractListProps({ dataError, noDataState, noResultsState, errorDataState, pinTop, pinBottom, dataFiltered, loading, footer, widthStrategy, }) {
1
+ export function extractListProps({ dataError, noDataState, noResultsState, errorDataState, dataFiltered, loading, footer, widthStrategy, }) {
2
2
  return {
3
3
  dataError,
4
4
  noDataState,
5
5
  noResultsState,
6
6
  errorDataState,
7
- pinTop,
8
- pinBottom,
9
7
  dataFiltered,
10
8
  loading,
11
9
  footer,
@@ -9,7 +9,7 @@ var __rest = (this && this.__rest) || function (s, e) {
9
9
  }
10
10
  return t;
11
11
  };
12
- import { flattenItems } from '@snack-uikit/list';
12
+ import { flattenItems } from '../legacy';
13
13
  import { isAccordionOptionProps, isGroupOptionProps, isNextListOptionProps } from './typeGuards';
14
14
  export function transformOptionsToItems(options) {
15
15
  return options.map(option => {
@@ -12,7 +12,7 @@ export function isNextListOptionProps(option) {
12
12
  }
13
13
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
14
  export function isGroupOptionProps(option) {
15
- return 'options' in option && option['type'] === undefined;
15
+ return 'options' in option && option['type'] === 'group';
16
16
  }
17
17
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
18
  export function isFieldSelectMultipleProps(props) {
@@ -1,7 +1,7 @@
1
- import { ItemProps, SelectionSingleValueType } from '@snack-uikit/list';
1
+ import { ItemId, ItemProps, SelectionSingleValueType } from '@snack-uikit/list';
2
2
  import { ItemWithId, OptionProps } from '../types';
3
3
  export declare function createPlaceholderItem(value: SelectionSingleValueType): {
4
- id: SelectionSingleValueType;
4
+ id: ItemId;
5
5
  content: {
6
6
  option: string;
7
7
  };
@@ -9,12 +9,12 @@ export declare function createPlaceholderItem(value: SelectionSingleValueType):
9
9
  };
10
10
  export declare function updateItems({ options, value, selectedItem, }: {
11
11
  options: OptionProps[];
12
- value: SelectionSingleValueType;
12
+ value?: ItemId;
13
13
  selectedItem?: ItemWithId;
14
14
  currentItems?: ItemProps[];
15
15
  }): {
16
16
  selectedItem: ItemWithId | undefined;
17
- items: ItemProps[];
17
+ items: import("@snack-uikit/list/dist/components/Items").Item[];
18
18
  };
19
19
  export declare function updateMultipleItems({ options, value, selectedItems, }: {
20
20
  options: OptionProps[];
@@ -23,10 +23,10 @@ export declare function updateMultipleItems({ options, value, selectedItems, }:
23
23
  currentItems?: ItemProps[];
24
24
  }): {
25
25
  selectedItems: undefined;
26
- items: (ItemProps & {
26
+ items: (import("@snack-uikit/list/dist/components/Items").Item & {
27
27
  appearance?: import("@snack-uikit/tag/dist/types").Appearance | undefined;
28
28
  })[];
29
29
  } | {
30
30
  selectedItems: ItemWithId[];
31
- items: ItemProps[];
31
+ items: import("@snack-uikit/list/dist/components/Items").Item[];
32
32
  };
@@ -1,4 +1,4 @@
1
- import { flattenItems } from '@snack-uikit/list';
1
+ import { flattenItems } from '../legacy';
2
2
  import { transformOptionsToItems } from './options';
3
3
  export function createPlaceholderItem(value) {
4
4
  return { id: value, content: { option: String(value) }, placeholder: true };
@@ -39,11 +39,20 @@ export function updateMultipleItems({ options, value, selectedItems, }) {
39
39
  items: originalItems,
40
40
  };
41
41
  }
42
+ const foundedValue = [];
42
43
  let newItems = originalItems;
43
44
  let newSelectedItems = selectedItems;
44
45
  const flattenOriginalItems = flattenItems(originalItems);
45
- const foundItems = flattenOriginalItems.filter(item => value.includes(item.id));
46
- const nonFoundValues = value.filter(value => !flattenOriginalItems.find(item => item.id === value));
46
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
47
+ const foundItems = flattenOriginalItems.filter((item) => {
48
+ if (value.includes(item.id) && !foundedValue.includes(item.id)) {
49
+ foundedValue.push(item.id);
50
+ return true;
51
+ }
52
+ });
53
+ const nonFoundValues = value.filter(
54
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
+ value => !flattenOriginalItems.find((item) => item.id === value));
47
56
  if (nonFoundValues.length) {
48
57
  const nonFoundItems = nonFoundValues.map(value => (selectedItems === null || selectedItems === void 0 ? void 0 : selectedItems.find(selectedItem => selectedItem.id === value)) || createPlaceholderItem(value));
49
58
  newSelectedItems = [...foundItems, ...nonFoundItems];
@@ -7,7 +7,7 @@
7
7
  border-style:solid;
8
8
  }
9
9
  .container[data-validation=default]{
10
- background-color:var(--sys-neutral-background1-level, #fafafc);
10
+ background-color:var(--sys-neutral-background1-level, #fdfdff);
11
11
  border-color:var(--sys-neutral-decor-default, #dfe2ec);
12
12
  }
13
13
  .container[data-validation=default]:hover{
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public"
5
5
  },
6
6
  "title": "Fields",
7
- "version": "0.18.3",
7
+ "version": "0.19.0",
8
8
  "sideEffects": [
9
9
  "*.css",
10
10
  "*.woff",
@@ -32,29 +32,31 @@
32
32
  "license": "Apache-2.0",
33
33
  "scripts": {},
34
34
  "dependencies": {
35
- "@snack-uikit/button": "0.17.0",
36
- "@snack-uikit/calendar": "0.7.6",
37
- "@snack-uikit/droplist": "0.13.15",
35
+ "@snack-uikit/button": "0.17.1",
36
+ "@snack-uikit/calendar": "0.7.7",
37
+ "@snack-uikit/dropdown": "0.2.2",
38
38
  "@snack-uikit/icons": "0.20.1",
39
- "@snack-uikit/input-private": "3.1.1",
40
- "@snack-uikit/list": "0.11.0",
41
- "@snack-uikit/scroll": "0.5.2",
42
- "@snack-uikit/slider": "0.1.7",
43
- "@snack-uikit/tag": "0.9.0",
44
- "@snack-uikit/tooltip": "0.13.1",
45
- "@snack-uikit/truncate-string": "0.4.12",
46
- "@snack-uikit/utils": "3.2.0",
39
+ "@snack-uikit/input-private": "3.1.2",
40
+ "@snack-uikit/list": "0.11.1",
41
+ "@snack-uikit/scroll": "0.5.3",
42
+ "@snack-uikit/slider": "0.1.8",
43
+ "@snack-uikit/tag": "0.9.1",
44
+ "@snack-uikit/tooltip": "0.13.2",
45
+ "@snack-uikit/truncate-string": "0.4.13",
46
+ "@snack-uikit/utils": "3.3.0",
47
47
  "classnames": "2.3.2",
48
48
  "copy-to-clipboard": "3.3.3",
49
+ "fuzzy-search": "3.2.1",
49
50
  "merge-refs": "1.2.2",
50
51
  "react-textarea-autosize": "8.5.3",
51
52
  "uncontrollable": "8.0.4"
52
53
  },
53
54
  "devDependencies": {
55
+ "@types/fuzzy-search": "2.1.5",
54
56
  "@types/merge-refs": "1.0.0"
55
57
  },
56
58
  "peerDependencies": {
57
59
  "@snack-uikit/locale": "*"
58
60
  },
59
- "gitHead": "f6b48b7373bda024626822d7c17b05d6ddbf20b2"
61
+ "gitHead": "bf1e1f3cfabccc1ee288c18722e33009bd001798"
60
62
  }
@@ -13,7 +13,7 @@ import {
13
13
  import { useUncontrolledProp } from 'uncontrollable';
14
14
 
15
15
  import { Calendar, CalendarProps } from '@snack-uikit/calendar';
16
- import { Dropdown } from '@snack-uikit/droplist';
16
+ import { Dropdown } from '@snack-uikit/dropdown';
17
17
  import { CalendarSVG } from '@snack-uikit/icons';
18
18
  import {
19
19
  ICON_SIZE,
@@ -3,7 +3,7 @@ import mergeRefs from 'merge-refs';
3
3
  import { FocusEvent, forwardRef, KeyboardEvent, KeyboardEventHandler, useLayoutEffect, useRef, useState } from 'react';
4
4
 
5
5
  import { InputPrivate } from '@snack-uikit/input-private';
6
- import { BaseItemProps, Droplist, ItemProps, SelectionSingleValueType, useFuzzySearch } from '@snack-uikit/list';
6
+ import { BaseItemProps, Droplist, ItemProps, SelectionSingleValueType } from '@snack-uikit/list';
7
7
  import { Tag } from '@snack-uikit/tag';
8
8
  import { extractSupportProps } from '@snack-uikit/utils';
9
9
 
@@ -12,13 +12,17 @@ import { useValueControl } from '../../hooks';
12
12
  import { FieldDecorator } from '../FieldDecorator';
13
13
  import { extractFieldDecoratorProps } from '../FieldDecorator/utils';
14
14
  import { useButtons, useHandleDeleteItem, useHandleOnKeyDown, useSearchInput } from './hooks';
15
+ import { useFuzzySearch } from './legacy';
15
16
  import styles from './styles.module.scss';
16
17
  import { FieldSelectMultipleProps, ItemWithId, SelectedOptionFormatter } from './types';
17
18
  import { extractListProps, getArrowIcon, updateMultipleItems } from './utils';
18
19
 
19
20
  const BASE_MIN_WIDTH = 4;
20
21
 
21
- const defaultSelectedOptionFormatter: SelectedOptionFormatter = item => item?.content.option || '';
22
+ const defaultSelectedOptionFormatter: SelectedOptionFormatter = item =>
23
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
24
+ // @ts-expect-error
25
+ item?.content.option || '';
22
26
 
23
27
  export const FieldSelectMultiple = forwardRef<HTMLInputElement, FieldSelectMultipleProps>(
24
28
  (
@@ -79,6 +83,10 @@ export const FieldSelectMultiple = forwardRef<HTMLInputElement, FieldSelectMulti
79
83
  const onClear = () => {
80
84
  setValue(selectedItems?.filter(item => item.disabled).map(item => item.id));
81
85
  localRef.current?.focus();
86
+
87
+ if (rest.required) {
88
+ setOpen(true);
89
+ }
82
90
  };
83
91
 
84
92
  const { ArrowIcon, arrowIconSize } = getArrowIcon({ size, open });
@@ -13,7 +13,7 @@ import {
13
13
  } from 'react';
14
14
 
15
15
  import { InputPrivate } from '@snack-uikit/input-private';
16
- import { Droplist, ItemProps, SelectionSingleValueType, useFuzzySearch } from '@snack-uikit/list';
16
+ import { Droplist, ItemProps, SelectionSingleValueType } from '@snack-uikit/list';
17
17
  import { extractSupportProps } from '@snack-uikit/utils';
18
18
 
19
19
  import { FieldContainerPrivate } from '../../helperComponents';
@@ -21,11 +21,15 @@ import { useValueControl } from '../../hooks';
21
21
  import { FieldDecorator } from '../FieldDecorator';
22
22
  import { extractFieldDecoratorProps } from '../FieldDecorator/utils';
23
23
  import { useButtons, useHandleOnKeyDown, useSearchInput } from './hooks';
24
+ import { useFuzzySearch } from './legacy';
24
25
  import styles from './styles.module.scss';
25
26
  import { FieldSelectSingleProps, ItemWithId, SelectedOptionFormatter } from './types';
26
27
  import { extractListProps, getArrowIcon, updateItems } from './utils';
27
28
 
28
- const defaultSelectedOptionFormatter: SelectedOptionFormatter = item => item?.content.option || '';
29
+ const defaultSelectedOptionFormatter: SelectedOptionFormatter = item =>
30
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
31
+ // @ts-expect-error
32
+ item?.content.option || '';
29
33
 
30
34
  export const FieldSelectSingle = forwardRef<HTMLInputElement, FieldSelectSingleProps>(
31
35
  (
@@ -72,6 +76,8 @@ export const FieldSelectSingle = forwardRef<HTMLInputElement, FieldSelectSingleP
72
76
 
73
77
  const { inputValue, setInputValue, prevInputValue, updateInputValue } = useSearchInput({
74
78
  ...search,
79
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
80
+ // @ts-expect-error
75
81
  defaultValue: selectedItem?.content.option ?? '',
76
82
  selectedOptionFormatter,
77
83
  });
@@ -86,6 +92,8 @@ export const FieldSelectSingle = forwardRef<HTMLInputElement, FieldSelectSingleP
86
92
  if (
87
93
  prevSelectedItem.current &&
88
94
  prevSelectedItem.current.id === selectedItem?.id &&
95
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
96
+ // @ts-expect-error
89
97
  prevSelectedItem.current.content.option === selectedItem?.content.option
90
98
  ) {
91
99
  return;
@@ -100,7 +108,10 @@ export const FieldSelectSingle = forwardRef<HTMLInputElement, FieldSelectSingleP
100
108
  const onClear = useCallback(() => {
101
109
  setValue(undefined);
102
110
  localRef.current?.focus();
103
- }, [setValue]);
111
+ if (required) {
112
+ setOpen(true);
113
+ }
114
+ }, [required, setOpen, setValue]);
104
115
 
105
116
  const { ArrowIcon, arrowIconSize } = getArrowIcon({ size, open });
106
117
 
@@ -3,13 +3,14 @@ import { Handler } from 'uncontrollable';
3
3
 
4
4
  import { useButtonNavigation, useClearButton } from '@snack-uikit/input-private';
5
5
  import {
6
- extractChildIds,
6
+ // extractChildIds,
7
7
  isAccordionItemProps,
8
8
  isNextListItemProps,
9
9
  SelectionSingleValueType,
10
10
  } from '@snack-uikit/list';
11
11
 
12
12
  import { useCopyButton, useValueControl } from '../../hooks';
13
+ import { extractChildIds } from './legacy';
13
14
  import { ItemWithId, SearchState, SelectedOptionFormatter } from './types';
14
15
  import { isBaseOptionProps } from './utils';
15
16
 
@@ -141,7 +142,15 @@ export function useHandleDeleteItem(setValue: Handler) {
141
142
  }
142
143
 
143
144
  if (isBaseOptionProps(item)) {
144
- setValue((value: SelectionSingleValueType[]) => value?.filter(v => v !== item.id));
145
+ setValue(
146
+ (value: SelectionSingleValueType[]) =>
147
+ value?.filter(
148
+ v =>
149
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
150
+ // @ts-expect-error
151
+ v !== item.id,
152
+ ),
153
+ );
145
154
  }
146
155
  },
147
156
  [setValue],
@@ -0,0 +1,53 @@
1
+ import { useEffect, useMemo } from 'react';
2
+
3
+ import { ItemProps, useSelectionContext } from '@snack-uikit/list';
4
+
5
+ import { extractAllChildIds, extractChildIds } from '../../utils';
6
+
7
+ type UseGroupItemSelectionProps = {
8
+ items: ItemProps[];
9
+ id?: string | number;
10
+ disabled?: boolean;
11
+ };
12
+
13
+ export function useLegacyGroupItemSelection({ id = '', items, disabled }: UseGroupItemSelectionProps) {
14
+ const { value, setValue, isSelectionMultiple } = useSelectionContext();
15
+ const { childIds, allChildIds } = useMemo(
16
+ () => ({ childIds: extractChildIds({ items }), allChildIds: extractAllChildIds({ items }) }),
17
+ [items],
18
+ );
19
+
20
+ const isIndeterminate = isSelectionMultiple
21
+ ? allChildIds.some(childId => value?.includes(childId))
22
+ : allChildIds.includes(value ?? '');
23
+ const checked = isSelectionMultiple ? allChildIds.every(childId => value?.includes(childId)) : undefined;
24
+
25
+ useEffect(() => {
26
+ if (isSelectionMultiple) {
27
+ if (checked && !value?.includes(id)) {
28
+ setValue?.((value: Array<number | string>) => (value ?? []).concat([id ?? '']));
29
+ }
30
+ if (!checked && value?.includes(id)) {
31
+ setValue?.((value: Array<number | string>) => (value ?? []).filter(itemId => itemId !== id));
32
+ }
33
+ }
34
+ }, [checked, disabled, id, isSelectionMultiple, setValue, value]);
35
+
36
+ const handleOnSelect = () => {
37
+ if (checked) {
38
+ setValue?.((value: Array<string | number>) =>
39
+ (value ?? []).filter(itemId => itemId !== id && !childIds.includes(itemId)),
40
+ );
41
+ return;
42
+ }
43
+
44
+ if (isIndeterminate) {
45
+ setValue?.((value: Array<string | number>) => Array.from(new Set([...(value ?? []), ...childIds, id])));
46
+ return;
47
+ }
48
+
49
+ setValue?.((value: Array<string | number>) => (value ?? []).concat([...childIds, id ?? '']));
50
+ };
51
+
52
+ return { checked, isIndeterminate, handleOnSelect };
53
+ }
@@ -0,0 +1 @@
1
+ export { useLegacyGroupItemSelection } from './Items/hooks';
@@ -0,0 +1,32 @@
1
+ import FuzzySearch from 'fuzzy-search';
2
+ import { useCallback, useMemo } from 'react';
3
+
4
+ import { ItemProps, kindFlattenItems } from '@snack-uikit/list';
5
+
6
+ const DEFAULT_MIN_SEARCH_INPUT_LENGTH = 2;
7
+
8
+ /**
9
+ * Нечеткий поиск среди айтемов по полям 'content.option', 'content.caption', 'content.description', 'label'
10
+ */
11
+ export function useFuzzySearch(items: ItemProps[], minSearchInputLength?: number) {
12
+ const flattenItems = useMemo(() => {
13
+ const { flattenItems } = kindFlattenItems({ items });
14
+
15
+ return Object.values(flattenItems);
16
+ }, [items]);
17
+
18
+ return useCallback(
19
+ (search: string) => {
20
+ const searcher = new FuzzySearch(
21
+ flattenItems,
22
+ ['content.option', 'content.caption', 'content.description', 'label'],
23
+ {},
24
+ );
25
+
26
+ return search.length > (minSearchInputLength ?? DEFAULT_MIN_SEARCH_INPUT_LENGTH)
27
+ ? searcher.search(search)
28
+ : items;
29
+ },
30
+ [flattenItems, items, minSearchInputLength],
31
+ );
32
+ }
@@ -0,0 +1,3 @@
1
+ export * from './components';
2
+ export { useFuzzySearch } from './hooks';
3
+ export { extractChildIds, flattenItems } from './utils';