@snack-uikit/fields 0.14.2 → 0.15.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 (62) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +15 -14
  3. package/dist/components/FieldDecorator/Header.js +1 -1
  4. package/dist/components/FieldDecorator/styles.module.css +7 -1
  5. package/dist/components/FieldSelect/FieldSelect.d.ts +1 -7
  6. package/dist/components/FieldSelect/FieldSelect.js +9 -21
  7. package/dist/components/FieldSelect/FieldSelectMultiple.d.ts +17 -0
  8. package/dist/components/FieldSelect/FieldSelectMultiple.js +118 -0
  9. package/dist/components/FieldSelect/FieldSelectSingle.d.ts +9 -28
  10. package/dist/components/FieldSelect/FieldSelectSingle.js +69 -55
  11. package/dist/components/FieldSelect/hooks.d.ts +30 -0
  12. package/dist/components/FieldSelect/hooks.js +72 -0
  13. package/dist/components/FieldSelect/index.d.ts +2 -1
  14. package/dist/components/FieldSelect/index.js +1 -1
  15. package/dist/components/FieldSelect/styles.module.css +129 -27
  16. package/dist/components/FieldSelect/types.d.ts +42 -37
  17. package/dist/components/FieldSelect/utils.d.ts +19 -0
  18. package/dist/components/FieldSelect/utils.js +112 -0
  19. package/dist/helperComponents/FieldContainerPrivate/styles.module.css +30 -6
  20. package/package.json +5 -3
  21. package/src/components/FieldDecorator/Header.tsx +6 -1
  22. package/src/components/FieldDecorator/styles.module.scss +38 -30
  23. package/src/components/FieldSelect/FieldSelect.tsx +13 -21
  24. package/src/components/FieldSelect/FieldSelectMultiple.tsx +255 -0
  25. package/src/components/FieldSelect/FieldSelectSingle.tsx +159 -99
  26. package/src/components/FieldSelect/hooks.ts +125 -0
  27. package/src/components/FieldSelect/index.ts +12 -1
  28. package/src/components/FieldSelect/styles.module.scss +71 -31
  29. package/src/components/FieldSelect/types.ts +55 -40
  30. package/src/components/FieldSelect/utils.ts +163 -0
  31. package/src/helperComponents/FieldContainerPrivate/FieldContainerPrivate.tsx +2 -0
  32. package/src/helperComponents/FieldContainerPrivate/styles.module.scss +32 -11
  33. package/dist/components/FieldSelect/FieldSelectBase.d.ts +0 -31
  34. package/dist/components/FieldSelect/FieldSelectBase.js +0 -51
  35. package/dist/components/FieldSelect/FieldSelectMulti.d.ts +0 -37
  36. package/dist/components/FieldSelect/FieldSelectMulti.js +0 -89
  37. package/dist/components/FieldSelect/constants.d.ts +0 -7
  38. package/dist/components/FieldSelect/constants.js +0 -6
  39. package/dist/components/FieldSelect/helpers/getArrowIcon.d.ts +0 -8
  40. package/dist/components/FieldSelect/helpers/getArrowIcon.js +0 -8
  41. package/dist/components/FieldSelect/helpers/getDisplayedValue.d.ts +0 -10
  42. package/dist/components/FieldSelect/helpers/getDisplayedValue.js +0 -12
  43. package/dist/components/FieldSelect/helpers/index.d.ts +0 -2
  44. package/dist/components/FieldSelect/helpers/index.js +0 -2
  45. package/dist/components/FieldSelect/hooks/index.d.ts +0 -3
  46. package/dist/components/FieldSelect/hooks/index.js +0 -3
  47. package/dist/components/FieldSelect/hooks/useFilteredOptions.d.ts +0 -7
  48. package/dist/components/FieldSelect/hooks/useFilteredOptions.js +0 -6
  49. package/dist/components/FieldSelect/hooks/useList.d.ts +0 -37
  50. package/dist/components/FieldSelect/hooks/useList.js +0 -52
  51. package/dist/components/FieldSelect/hooks/useListNavigation.d.ts +0 -26
  52. package/dist/components/FieldSelect/hooks/useListNavigation.js +0 -48
  53. package/src/components/FieldSelect/FieldSelectBase.tsx +0 -222
  54. package/src/components/FieldSelect/FieldSelectMulti.tsx +0 -163
  55. package/src/components/FieldSelect/constants.ts +0 -9
  56. package/src/components/FieldSelect/helpers/getArrowIcon.ts +0 -9
  57. package/src/components/FieldSelect/helpers/getDisplayedValue.ts +0 -25
  58. package/src/components/FieldSelect/helpers/index.ts +0 -2
  59. package/src/components/FieldSelect/hooks/index.ts +0 -3
  60. package/src/components/FieldSelect/hooks/useFilteredOptions.ts +0 -23
  61. package/src/components/FieldSelect/hooks/useList.ts +0 -87
  62. package/src/components/FieldSelect/hooks/useListNavigation.ts +0 -81
@@ -1,26 +1,38 @@
1
1
  import { ReactElement } from 'react';
2
2
 
3
- import { ItemSingleProps } from '@snack-uikit/droplist';
4
3
  import { InputPrivateProps } from '@snack-uikit/input-private';
4
+ import {
5
+ AccordionItemProps,
6
+ BaseItemProps,
7
+ GroupItemProps,
8
+ ListProps,
9
+ NextListItemProps,
10
+ SelectionMultipleState,
11
+ SelectionSingleState,
12
+ } from '@snack-uikit/list';
5
13
  import { WithSupportProps } from '@snack-uikit/utils';
6
14
 
7
15
  import { FieldDecoratorProps } from '../FieldDecorator';
8
16
 
9
- export type Option = Pick<ItemSingleProps, 'caption' | 'description' | 'tagLabel' | 'icon' | 'avatar' | 'disabled'> & {
10
- value: string;
11
- label: string;
12
- };
17
+ // eslint-disable-next-line no-use-before-define
18
+ export type OptionProps = BaseOptionProps | AccordionOptionProps | GroupOptionProps | NestListOptionProps;
13
19
 
14
- export type ExtendedOption = Option & {
15
- checked: boolean;
16
- };
20
+ // eslint-disable-next-line no-use-before-define
21
+ export type OptionWithoutGroup = BaseOptionProps | AccordionOptionProps | NestListOptionProps;
22
+
23
+ export type BaseOptionProps = Pick<BaseItemProps, 'beforeContent' | 'afterContent' | 'disabled'> &
24
+ BaseItemProps['content'] & { value: string | number };
25
+
26
+ export type AccordionOptionProps = Pick<AccordionItemProps, 'type'> & BaseOptionProps & { options: OptionProps[] };
27
+ export type GroupOptionProps = Omit<GroupItemProps, 'items' | 'id'> & { options: OptionProps[] };
28
+ export type NestListOptionProps = Pick<NextListItemProps, 'type'> & BaseOptionProps & { options: OptionProps[] };
17
29
 
18
- type InputProps = Pick<
30
+ export type InputProps = Pick<
19
31
  InputPrivateProps,
20
- 'id' | 'name' | 'placeholder' | 'disabled' | 'readonly' | 'onFocus' | 'onBlur'
32
+ 'id' | 'name' | 'placeholder' | 'disabled' | 'readonly' | 'onFocus' | 'onBlur' | 'onKeyDown'
21
33
  >;
22
34
 
23
- type WrapperProps = Pick<
35
+ export type WrapperProps = Pick<
24
36
  FieldDecoratorProps,
25
37
  | 'className'
26
38
  | 'label'
@@ -33,14 +45,16 @@ type WrapperProps = Pick<
33
45
  | 'labelTooltipPlacement'
34
46
  >;
35
47
 
36
- type FieldSelectOwnProps = {
37
- /** Массив опций выпадающего списка */
38
- options: Option[];
39
- /** Открыт ли выпадающий список */
40
- open?: boolean;
41
- /** Колбек открытия выпадающего списка */
42
- onOpenChange?(value: boolean): void;
43
- /** Можно ли искать опции внутри списка */
48
+ export type SearchState = {
49
+ value?: string;
50
+ defaultValue?: string;
51
+ onChange?(value: string): void;
52
+ };
53
+
54
+ export type FieldSelectPrivateProps = InputProps & WrapperProps & { options: OptionProps[]; loading?: boolean };
55
+
56
+ type FiledSelectCommonProps = WithSupportProps<{
57
+ options: OptionProps[];
44
58
  searchable?: boolean;
45
59
  /** Отображение кнопки Копировать для поля (актуально только для `readonly = true`) */
46
60
  showCopyButton?: boolean;
@@ -49,30 +63,31 @@ type FieldSelectOwnProps = {
49
63
  * @default true
50
64
  */
51
65
  showClearButton?: boolean;
66
+ /**
67
+ * Является ли поле доступным только для чтения
68
+ * @default false
69
+ */
70
+ readonly?: boolean;
71
+
52
72
  /** Иконка-префикс для поля */
53
73
  prefixIcon?: ReactElement;
54
- /** Текст отсутствия доступных значений */
55
- noDataText?: string;
56
- /** Текущая локаль */
57
- locale?: Intl.Locale;
58
- };
59
74
 
60
- export type FieldSelectBaseProps = FieldSelectOwnProps & InputProps & WrapperProps;
75
+ footer?: ListProps['footer'];
61
76
 
62
- type SingleModeProps = {
63
- /** Выбранное значение: <br> - одно для single mode */
64
- value?: Option['value'];
65
- /** Колбек смены значения */
66
- onChange?(value: Option['value']): void;
67
- };
77
+ search?: SearchState;
68
78
 
69
- type MultiModeProps = {
70
- /** <br> - массив для multi mode */
71
- value?: Option['value'][];
72
- onChange?(value: Option['value'][]): void;
73
- /** Колбек формирования текста */
74
- getSelectedItemsText?(amount: number): string;
75
- };
79
+ autocomplete?: boolean;
80
+ }>;
81
+
82
+ export type FieldSelectSingleProps = FieldSelectPrivateProps &
83
+ Omit<SelectionSingleState, 'mode'> &
84
+ WrapperProps &
85
+ FiledSelectCommonProps;
86
+
87
+ export type FieldSelectMultipleProps = FieldSelectPrivateProps &
88
+ Omit<SelectionMultipleState, 'mode'> &
89
+ FiledSelectCommonProps;
76
90
 
77
- export type FieldSelectSingleProps = WithSupportProps<FieldSelectBaseProps & SingleModeProps>;
78
- export type FieldSelectMultiProps = WithSupportProps<FieldSelectBaseProps & MultiModeProps>;
91
+ export type FieldSelectProps =
92
+ | (FieldSelectSingleProps & { selection?: 'single' })
93
+ | (FieldSelectMultipleProps & { selection: 'multiple' });
@@ -0,0 +1,163 @@
1
+ import { ChevronDownSVG, ChevronUpSVG } from '@snack-uikit/icons';
2
+ import { ICON_SIZE, SIZE, Size } from '@snack-uikit/input-private';
3
+ import { ItemProps } from '@snack-uikit/list';
4
+
5
+ import {
6
+ AccordionOptionProps,
7
+ BaseOptionProps,
8
+ FieldSelectMultipleProps,
9
+ FieldSelectSingleProps,
10
+ GroupOptionProps,
11
+ NestListOptionProps,
12
+ OptionProps,
13
+ OptionWithoutGroup,
14
+ } from './types';
15
+
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ export function isBaseOptionProps(option: any): option is BaseOptionProps {
18
+ return !('options' in option);
19
+ }
20
+
21
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
+ export function isAccordionOptionProps(option: any): option is AccordionOptionProps {
23
+ return 'options' in option && option['type'] === 'collapse';
24
+ }
25
+
26
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
27
+ export function isNextListOptionProps(option: any): option is NestListOptionProps {
28
+ return 'options' in option && option['type'] === 'next-list';
29
+ }
30
+
31
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
32
+ export function isGroupOptionProps(option: any): option is GroupOptionProps {
33
+ return 'options' in option && option['type'] === undefined;
34
+ }
35
+
36
+ export function transformOptionsToItems(options: OptionProps[]): ItemProps[] {
37
+ return options.map(option => {
38
+ if (isAccordionOptionProps(option) || isNextListOptionProps(option)) {
39
+ const { description, option: contentOption, caption, options, value, ...rest } = option;
40
+
41
+ return {
42
+ 'data-test-id': 'field-select__list-option-' + option.value,
43
+ ...rest,
44
+ id: value,
45
+ content: { option: contentOption, caption, description },
46
+ items: transformOptionsToItems(options),
47
+ };
48
+ }
49
+
50
+ if (isGroupOptionProps(option)) {
51
+ const { options, ...rest } = option;
52
+
53
+ return {
54
+ ...rest,
55
+ items: transformOptionsToItems(options),
56
+ };
57
+ }
58
+
59
+ const { description, option: contentOption, caption, value, ...rest } = option as BaseOptionProps;
60
+
61
+ return {
62
+ 'data-test-id': 'field-select__list-option-' + option.value,
63
+ ...rest,
64
+ id: value,
65
+ content: { option: contentOption, caption, description },
66
+ };
67
+ });
68
+ }
69
+
70
+ export function extractSelectedOptions(
71
+ options: OptionProps[],
72
+ value: string | number | undefined,
73
+ ): OptionWithoutGroup | undefined {
74
+ for (let i = 0; i < options.length; i++) {
75
+ const option = options[i];
76
+ if (isAccordionOptionProps(option) || isNextListOptionProps(option)) {
77
+ const { value: optionValue } = option;
78
+
79
+ if (optionValue === value) {
80
+ return option;
81
+ }
82
+
83
+ const selectedOptionFromNestedOptions = extractSelectedOptions(option.options, value);
84
+
85
+ if (selectedOptionFromNestedOptions) {
86
+ return selectedOptionFromNestedOptions;
87
+ }
88
+ }
89
+
90
+ if (isGroupOptionProps(option)) {
91
+ const selectedOptionFromNestedOptions = extractSelectedOptions(option.options, value);
92
+
93
+ if (selectedOptionFromNestedOptions) {
94
+ return selectedOptionFromNestedOptions;
95
+ }
96
+ }
97
+
98
+ if (isBaseOptionProps(option)) {
99
+ if (option.value === value) {
100
+ return option;
101
+ }
102
+ }
103
+ }
104
+
105
+ return undefined;
106
+ }
107
+
108
+ export function extractSelectedMultipleOptions(
109
+ options: OptionProps[],
110
+ value?: (string | number | undefined)[],
111
+ ): OptionWithoutGroup[] | undefined {
112
+ let selectedOptions: OptionWithoutGroup[] = [];
113
+
114
+ for (let i = 0; i < options.length; i++) {
115
+ const option = options[i];
116
+ if (isAccordionOptionProps(option) || isNextListOptionProps(option)) {
117
+ const { value: optionValue } = option;
118
+
119
+ if (value?.includes(optionValue)) {
120
+ selectedOptions.push(option);
121
+ }
122
+
123
+ const selectedOptionFromNestedOptions = extractSelectedMultipleOptions(option.options, value);
124
+
125
+ if (selectedOptionFromNestedOptions) {
126
+ selectedOptions = selectedOptions.concat(selectedOptionFromNestedOptions);
127
+ }
128
+ }
129
+
130
+ if (isGroupOptionProps(option)) {
131
+ const selectedOptionFromNestedOptions = extractSelectedMultipleOptions(option.options, value);
132
+
133
+ if (selectedOptionFromNestedOptions) {
134
+ selectedOptions = selectedOptions.concat(selectedOptionFromNestedOptions);
135
+ }
136
+ }
137
+
138
+ if (isBaseOptionProps(option)) {
139
+ if (value?.includes(option.value)) {
140
+ selectedOptions.push(option);
141
+ }
142
+ }
143
+ }
144
+
145
+ return selectedOptions.length ? selectedOptions : undefined;
146
+ }
147
+
148
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
149
+ export function isFieldSelectMultipleProps(props: any): props is FieldSelectMultipleProps {
150
+ return 'selection' in props && props['selection'] === 'multiple';
151
+ }
152
+
153
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
154
+ export function isFieldSelectSingleProps(props: any): props is FieldSelectSingleProps {
155
+ return ('selection' in props && props['selection'] === 'single') || props['selection'] === undefined;
156
+ }
157
+
158
+ export function getArrowIcon({ size, open }: { size: Size; open: boolean }) {
159
+ return {
160
+ ArrowIcon: open ? ChevronUpSVG : ChevronDownSVG,
161
+ arrowIconSize: size === SIZE.S ? ICON_SIZE.Xs : ICON_SIZE.S,
162
+ };
163
+ }
@@ -72,7 +72,9 @@ export function FieldContainerPrivate({
72
72
  {prefix}
73
73
  </span>
74
74
  )}
75
+
75
76
  {children}
77
+
76
78
  {postfix && <span className={styles.postfix}>{postfix}</span>}
77
79
  </div>
78
80
  );
@@ -21,7 +21,11 @@ $variants: 'single-line-container', 'multi-line-container';
21
21
  &[data-size='#{$size}'] {
22
22
  @include composite-var($fields, 'container', $size);
23
23
 
24
- &, input, select, textarea, span {
24
+ &,
25
+ input,
26
+ select,
27
+ textarea,
28
+ span {
25
29
  @include composite-var($theme-variables, 'sans', 'body', $size);
26
30
 
27
31
  cursor: text;
@@ -35,7 +39,7 @@ $variants: 'single-line-container', 'multi-line-container';
35
39
  }
36
40
  }
37
41
 
38
- &[data-validation="default"] {
42
+ &[data-validation='default'] {
39
43
  background-color: simple-var($sys-neutral-background1-level);
40
44
  border-color: simple-var($sys-neutral-decor-default);
41
45
 
@@ -54,7 +58,7 @@ $variants: 'single-line-container', 'multi-line-container';
54
58
  }
55
59
  }
56
60
 
57
- &[data-validation="error"] {
61
+ &[data-validation='error'] {
58
62
  background-color: simple-var($sys-red-background1-level);
59
63
  border-color: simple-var($sys-red-decor-default);
60
64
 
@@ -73,7 +77,7 @@ $variants: 'single-line-container', 'multi-line-container';
73
77
  }
74
78
  }
75
79
 
76
- &[data-validation="warning"] {
80
+ &[data-validation='warning'] {
77
81
  background-color: simple-var($sys-yellow-background1-level);
78
82
  border-color: simple-var($sys-yellow-decor-default);
79
83
 
@@ -92,7 +96,7 @@ $variants: 'single-line-container', 'multi-line-container';
92
96
  }
93
97
  }
94
98
 
95
- &[data-validation="success"] {
99
+ &[data-validation='success'] {
96
100
  background-color: simple-var($sys-green-background1-level);
97
101
  border-color: simple-var($sys-green-decor-default);
98
102
 
@@ -112,22 +116,32 @@ $variants: 'single-line-container', 'multi-line-container';
112
116
  }
113
117
 
114
118
  &[data-selectable] {
115
- &, input, select, textarea, span {
119
+ &,
120
+ input,
121
+ select,
122
+ textarea,
123
+ span {
116
124
  cursor: pointer;
117
125
  }
118
126
  }
119
127
 
120
128
  &[data-readonly] {
121
- &, input, select, textarea, span {
129
+ &,
130
+ input,
131
+ select,
132
+ textarea,
133
+ span {
122
134
  cursor: default;
123
135
  }
124
136
 
125
- &, &:hover {
137
+ &,
138
+ &:hover {
126
139
  background-color: simple-var($sys-neutral-decor-disabled);
127
140
  border-color: simple-var($sys-neutral-decor-disabled);
128
141
  }
129
142
 
130
- &:focus-within, &[data-focused] {
143
+ &:focus-within,
144
+ &[data-focused] {
131
145
  @include outline-var($container-focused-m);
132
146
 
133
147
  background-color: simple-var($sys-neutral-decor-disabled);
@@ -137,11 +151,18 @@ $variants: 'single-line-container', 'multi-line-container';
137
151
  }
138
152
 
139
153
  &[data-disabled] {
140
- &, input, select, textarea, span {
154
+ &,
155
+ input,
156
+ select,
157
+ textarea,
158
+ span {
141
159
  cursor: not-allowed;
142
160
  }
143
161
 
144
- &, &:focus-within, &[data-focused], &:hover {
162
+ &,
163
+ &:focus-within,
164
+ &[data-focused],
165
+ &:hover {
145
166
  background-color: simple-var($sys-neutral-background);
146
167
  border-color: simple-var($sys-neutral-decor-disabled);
147
168
  outline: none;
@@ -1,31 +0,0 @@
1
- import { KeyboardEventHandler, MouseEventHandler, RefCallback, RefObject } from 'react';
2
- import { SELECTION_MODE } from './constants';
3
- import { ExtendedOption, FieldSelectBaseProps, Option } from './types';
4
- type BaseProps = Omit<FieldSelectBaseProps, 'open' | 'onOpenChange' | 'options'> & Required<Pick<FieldSelectBaseProps, 'open' | 'onOpenChange'>> & {
5
- localRef: RefObject<HTMLInputElement>;
6
- options: ExtendedOption[];
7
- onClear: MouseEventHandler<HTMLButtonElement>;
8
- onChange(option: Option): () => void;
9
- inputValue: string;
10
- onInputValueChange(value: string): void;
11
- displayedValue?: string;
12
- valueToCopy: string;
13
- onInputKeyDown: KeyboardEventHandler<HTMLInputElement>;
14
- onButtonKeyDown: KeyboardEventHandler<HTMLButtonElement>;
15
- showClearButton: boolean;
16
- clearButtonRef: RefObject<HTMLButtonElement>;
17
- copyButtonRef: RefObject<HTMLButtonElement>;
18
- onClick?: MouseEventHandler<HTMLElement>;
19
- onContainerPrivateMouseDown?: MouseEventHandler<HTMLElement>;
20
- onDroplistFocusLeave(direction: string): void;
21
- firstDroplistItemRefCallback: RefCallback<HTMLButtonElement>;
22
- };
23
- type Props = ({
24
- selectionMode: typeof SELECTION_MODE.Single;
25
- selected: Option;
26
- } & BaseProps) | ({
27
- selectionMode: typeof SELECTION_MODE.Multi;
28
- selected: Option[];
29
- } & BaseProps);
30
- export declare const FieldSelectBase: import("react").ForwardRefExoticComponent<Props & import("react").RefAttributes<HTMLInputElement>>;
31
- export {};
@@ -1,51 +0,0 @@
1
- var __rest = (this && this.__rest) || function (s, e) {
2
- var t = {};
3
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
- t[p] = s[p];
5
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
- t[p[i]] = s[p[i]];
9
- }
10
- return t;
11
- };
12
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
13
- import mergeRefs from 'merge-refs';
14
- import { forwardRef, useMemo, } from 'react';
15
- import { Droplist } from '@snack-uikit/droplist';
16
- import { InputPrivate, SIZE, useButtonNavigation, useClearButton } from '@snack-uikit/input-private';
17
- import { extractSupportProps } from '@snack-uikit/utils';
18
- import { CONTAINER_VARIANT, VALIDATION_STATE } from '../../constants';
19
- import { FieldContainerPrivate } from '../../helperComponents';
20
- import { useCopyButton } from '../../hooks';
21
- import { useHandlers } from '../FieldDate/hooks/useHandlers';
22
- import { FieldDecorator } from '../FieldDecorator';
23
- import { SELECTION_MODE } from './constants';
24
- import { getArrowIcon } from './helpers';
25
- import styles from './styles.module.css';
26
- export const FieldSelectBase = forwardRef((_a, ref) => {
27
- var { selectionMode, onClear, showClearButton, clearButtonRef, copyButtonRef, onChange, inputValue, onInputValueChange, displayedValue, valueToCopy, localRef, onInputKeyDown: onInputKeyDownProp, onButtonKeyDown: onButtonKeyDownProp, id, name, placeholder, options, disabled = false, readonly = false, searchable = true, showCopyButton = true, open, onOpenChange, onFocus, onBlur, onContainerPrivateMouseDown, className, label, labelTooltip, labelTooltipPlacement, required = false, hint, showHintIcon, size = SIZE.S, validationState = VALIDATION_STATE.Default, prefixIcon, locale, noDataText = (locale === null || locale === void 0 ? void 0 : locale.language) === 'ru' ? 'Нет данных' : 'No data', firstDroplistItemRefCallback, onDroplistFocusLeave } = _a, rest = __rest(_a, ["selectionMode", "onClear", "showClearButton", "clearButtonRef", "copyButtonRef", "onChange", "inputValue", "onInputValueChange", "displayedValue", "valueToCopy", "localRef", "onInputKeyDown", "onButtonKeyDown", "id", "name", "placeholder", "options", "disabled", "readonly", "searchable", "showCopyButton", "open", "onOpenChange", "onFocus", "onBlur", "onContainerPrivateMouseDown", "className", "label", "labelTooltip", "labelTooltipPlacement", "required", "hint", "showHintIcon", "size", "validationState", "prefixIcon", "locale", "noDataText", "firstDroplistItemRefCallback", "onDroplistFocusLeave"]);
28
- const { ArrowIcon, arrowIconSize } = getArrowIcon({ size, open });
29
- const Item = selectionMode === SELECTION_MODE.Single ? Droplist.ItemSingle : Droplist.ItemMultiple;
30
- const clearButtonSettings = useClearButton({ clearButtonRef, showClearButton, size, onClear });
31
- const copyButtonSettings = useCopyButton({ copyButtonRef, showCopyButton, size, valueToCopy });
32
- const { onInputKeyDown: inputKeyDownNavigationHandler, inputTabIndex, setInitialTabIndices, buttons, } = useButtonNavigation({
33
- inputRef: localRef,
34
- buttons: useMemo(() => [clearButtonSettings, copyButtonSettings], [clearButtonSettings, copyButtonSettings]),
35
- onButtonKeyDown: onButtonKeyDownProp,
36
- readonly,
37
- submitKeys: ['Enter', 'Space', 'Tab'],
38
- });
39
- const onInputKeyDownHandler = useHandlers([
40
- inputKeyDownNavigationHandler,
41
- onInputKeyDownProp,
42
- ]);
43
- const onFocusLeaveHandler = useHandlers([
44
- (direction) => {
45
- if (direction === 'common')
46
- setInitialTabIndices();
47
- },
48
- onDroplistFocusLeave,
49
- ]);
50
- return (_jsx(FieldDecorator, Object.assign({ className: className, label: label, labelTooltip: labelTooltip, labelTooltipPlacement: labelTooltipPlacement, labelFor: id, required: required, hint: hint, disabled: disabled, readonly: readonly, showHintIcon: showHintIcon, size: size, validationState: validationState }, extractSupportProps(rest), { children: _jsx(Droplist, Object.assign({ trigger: 'click', triggerClassName: styles.triggerClassName, triggerClickByKeys: !searchable, placement: 'bottom', onFocusLeave: onFocusLeaveHandler, firstElementRefCallback: firstDroplistItemRefCallback, "data-test-id": 'field-select__list' }, (readonly || disabled ? { open: false } : { open, onOpenChange }), { triggerElement: _jsxs(FieldContainerPrivate, { className: styles.container, size: size, validationState: validationState, disabled: disabled, readonly: readonly, variant: CONTAINER_VARIANT.SingleLine, focused: open, selectable: !searchable, inputRef: localRef, prefix: prefixIcon, onMouseDown: onContainerPrivateMouseDown, postfix: _jsxs(_Fragment, { children: [buttons, _jsx(ArrowIcon, { size: arrowIconSize, className: styles.arrowIcon })] }), children: [_jsx(InputPrivate, { id: id, name: name, type: 'text', placeholder: displayedValue ? undefined : placeholder, ref: mergeRefs(ref, localRef), onFocus: onFocus, onBlur: onBlur, disabled: disabled, "data-size": size, "data-test-id": 'field-select__input', onKeyDown: onInputKeyDownHandler, onChange: searchable ? onInputValueChange : undefined, readonly: !searchable || readonly, value: inputValue, tabIndex: inputTabIndex }), displayedValue && (_jsx("span", { className: styles.displayValue, "data-test-id": 'field-select__displayed-value', children: displayedValue }))] }), children: options.length === 0 ? (_jsx(Droplist.NoData, { size: size, label: noDataText, "data-test-id": 'field-select__no-data' })) : (options.map(option => (_jsx(Item, Object.assign({ onChange: onChange(option), "data-test-id": 'field-select__list-option-' + option.value }, option, { option: option.label }), option.value)))) })) })));
51
- });
@@ -1,37 +0,0 @@
1
- import { FocusEventHandler } from 'react';
2
- import { Option } from './types';
3
- export declare const FieldSelectMulti: import("react").ForwardRefExoticComponent<{
4
- 'data-test-id'?: string | undefined;
5
- } & import("react").AriaAttributes & {
6
- options: Option[];
7
- open?: boolean | undefined;
8
- onOpenChange?(value: boolean): void;
9
- searchable?: boolean | undefined;
10
- showCopyButton?: boolean | undefined;
11
- showClearButton?: boolean | undefined;
12
- prefixIcon?: import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>> | undefined;
13
- noDataText?: string | undefined;
14
- locale?: Intl.Locale | undefined;
15
- } & {
16
- disabled?: boolean | undefined;
17
- readonly?: boolean | undefined;
18
- name?: string | undefined;
19
- placeholder?: string | undefined;
20
- id?: string | undefined;
21
- onFocus?: FocusEventHandler<HTMLInputElement> | undefined;
22
- onBlur?: FocusEventHandler<HTMLInputElement> | undefined;
23
- } & {
24
- showHintIcon?: boolean | undefined;
25
- validationState?: import("../../types").ValidationState | undefined;
26
- hint?: string | undefined;
27
- size?: import("@snack-uikit/input-private").Size | undefined;
28
- label?: string | undefined;
29
- className?: string | undefined;
30
- labelTooltip?: string | undefined;
31
- required?: boolean | undefined;
32
- labelTooltipPlacement?: import("@snack-uikit/popover-private/dist/types").Placement | undefined;
33
- } & {
34
- value?: string[] | undefined;
35
- onChange?(value: string[]): void;
36
- getSelectedItemsText?(amount: number): string;
37
- } & import("react").RefAttributes<HTMLInputElement>>;
@@ -1,89 +0,0 @@
1
- var __rest = (this && this.__rest) || function (s, e) {
2
- var t = {};
3
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
- t[p] = s[p];
5
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
- t[p[i]] = s[p[i]];
9
- }
10
- return t;
11
- };
12
- import { jsx as _jsx } from "react/jsx-runtime";
13
- import { forwardRef, useCallback, useMemo, useRef, useState } from 'react';
14
- import { useUncontrolledProp } from 'uncontrollable';
15
- import { DEFAULT_LOCALE, SELECTION_MODE } from './constants';
16
- import { FieldSelectBase } from './FieldSelectBase';
17
- import { getDisplayedValue } from './helpers';
18
- import { useList } from './hooks';
19
- export const FieldSelectMulti = forwardRef((_a, ref) => {
20
- var { value: valueProp, onChange, options, disabled = false, readonly = false, searchable = true, required = false, open, onOpenChange, locale = DEFAULT_LOCALE, getSelectedItemsText = number => ((locale === null || locale === void 0 ? void 0 : locale.language) === 'ru' ? `Выбрано: ${number}` : `Selected: ${number}`), showCopyButton: showCopyButtonProp = true, showClearButton: showClearButtonProp = true, onFocus, onBlur } = _a, rest = __rest(_a, ["value", "onChange", "options", "disabled", "readonly", "searchable", "required", "open", "onOpenChange", "locale", "getSelectedItemsText", "showCopyButton", "showClearButton", "onFocus", "onBlur"]);
21
- const selectionMode = SELECTION_MODE.Multi;
22
- const [value, setValue] = useUncontrolledProp(valueProp, [], onChange);
23
- const selected = useMemo(() => options.filter(option => value === null || value === void 0 ? void 0 : value.includes(option.value)), [options, value]);
24
- const displayedValue = getDisplayedValue({ selectionMode, selected, getSelectedItemsText });
25
- const valueToCopy = selected.map(op => op.label).join(', ');
26
- const [inputValue, setInputValue] = useState('');
27
- const showAdditionalButton = Boolean((value === null || value === void 0 ? void 0 : value.length) && value.length > 0 && !disabled);
28
- const isChecked = useCallback((option) => Boolean(selected.find(op => op.value === option.value)), [selected]);
29
- const [isFocused, setIsFocused] = useState(false);
30
- const stayOpen = useRef(false);
31
- const waitingForSearchStart = searchable && !isFocused;
32
- const showDisplayValue = displayedValue && (!searchable || waitingForSearchStart);
33
- const { isOpen, setIsOpen, localRef, extendedOptions, onInputKeyDown, onInputValueChange, onButtonKeyDown, clearButtonRef, copyButtonRef, showClearButton, showCopyButton, onDroplistFocusLeave, firstDroplistItemRefCallback, } = useList({
34
- open,
35
- onOpenChange,
36
- disabled,
37
- readonly,
38
- inputValue,
39
- setInputValue,
40
- searchable,
41
- options,
42
- isChecked,
43
- showAdditionalButton,
44
- showCopyButton: showCopyButtonProp,
45
- showClearButton: showClearButtonProp,
46
- });
47
- const handleOpenChange = (isOpen) => {
48
- if (stayOpen.current) {
49
- stayOpen.current = false;
50
- return;
51
- }
52
- setInputValue('');
53
- setIsOpen(isOpen);
54
- };
55
- const handlePreventListClose = event => {
56
- event.preventDefault();
57
- if (isOpen && waitingForSearchStart) {
58
- stayOpen.current = true;
59
- }
60
- };
61
- const handleClear = () => {
62
- var _a, _b;
63
- setValue([]);
64
- setInputValue('');
65
- stayOpen.current = false;
66
- if (required) {
67
- (_a = localRef.current) === null || _a === void 0 ? void 0 : _a.focus();
68
- setIsOpen(true);
69
- }
70
- else {
71
- (_b = localRef.current) === null || _b === void 0 ? void 0 : _b.blur();
72
- setIsOpen(false);
73
- }
74
- };
75
- const handleChange = (option) => () => {
76
- setValue((selected.find(op => op.value === option.value)
77
- ? selected.filter(op => op.value !== option.value)
78
- : [...selected, option]).map(op => op.value));
79
- };
80
- const handleFocus = event => {
81
- setIsFocused(true);
82
- onFocus === null || onFocus === void 0 ? void 0 : onFocus(event);
83
- };
84
- const handleBlur = event => {
85
- setIsFocused(false);
86
- onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
87
- };
88
- return (_jsx(FieldSelectBase, Object.assign({}, rest, { ref: ref, localRef: localRef, selectionMode: selectionMode, options: extendedOptions, selected: selected, disabled: disabled, readonly: readonly, required: required, searchable: searchable, onChange: handleChange, onClear: handleClear, displayedValue: showDisplayValue ? displayedValue : '', valueToCopy: valueToCopy, inputValue: inputValue, onInputValueChange: onInputValueChange, onInputKeyDown: onInputKeyDown, clearButtonRef: clearButtonRef, copyButtonRef: copyButtonRef, onButtonKeyDown: onButtonKeyDown, open: isOpen, onOpenChange: handleOpenChange, locale: locale, showCopyButton: showCopyButton, showClearButton: showClearButton, onFocus: handleFocus, onBlur: handleBlur, onContainerPrivateMouseDown: handlePreventListClose, onDroplistFocusLeave: onDroplistFocusLeave, firstDroplistItemRefCallback: firstDroplistItemRefCallback })));
89
- });
@@ -1,7 +0,0 @@
1
- import { Option } from './types';
2
- export declare const EMPTY_OPTION: Option;
3
- export declare const DEFAULT_LOCALE: Intl.Locale;
4
- export declare const SELECTION_MODE: {
5
- readonly Single: "single";
6
- readonly Multi: "multi";
7
- };
@@ -1,6 +0,0 @@
1
- export const EMPTY_OPTION = { value: '', label: '' };
2
- export const DEFAULT_LOCALE = new Intl.Locale('ru-RU');
3
- export const SELECTION_MODE = {
4
- Single: 'single',
5
- Multi: 'multi',
6
- };
@@ -1,8 +0,0 @@
1
- import { Size } from '@snack-uikit/input-private';
2
- export declare function getArrowIcon({ size, open }: {
3
- size: Size;
4
- open: boolean;
5
- }): {
6
- ArrowIcon: ({ size, ...props }: import("@snack-uikit/icons/dist/components/interface-icons/chevronUp").ISvgIconProps) => import("react/jsx-runtime").JSX.Element;
7
- arrowIconSize: 16 | 24;
8
- };
@@ -1,8 +0,0 @@
1
- import { ChevronDownSVG, ChevronUpSVG } from '@snack-uikit/icons';
2
- import { ICON_SIZE, SIZE } from '@snack-uikit/input-private';
3
- export function getArrowIcon({ size, open }) {
4
- return {
5
- ArrowIcon: open ? ChevronUpSVG : ChevronDownSVG,
6
- arrowIconSize: size === SIZE.S ? ICON_SIZE.Xs : ICON_SIZE.S,
7
- };
8
- }