@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.
- package/CHANGELOG.md +16 -0
- package/README.md +15 -14
- package/dist/components/FieldDecorator/Header.js +1 -1
- package/dist/components/FieldDecorator/styles.module.css +7 -1
- package/dist/components/FieldSelect/FieldSelect.d.ts +1 -7
- package/dist/components/FieldSelect/FieldSelect.js +9 -21
- package/dist/components/FieldSelect/FieldSelectMultiple.d.ts +17 -0
- package/dist/components/FieldSelect/FieldSelectMultiple.js +118 -0
- package/dist/components/FieldSelect/FieldSelectSingle.d.ts +9 -28
- package/dist/components/FieldSelect/FieldSelectSingle.js +69 -55
- package/dist/components/FieldSelect/hooks.d.ts +30 -0
- package/dist/components/FieldSelect/hooks.js +72 -0
- package/dist/components/FieldSelect/index.d.ts +2 -1
- package/dist/components/FieldSelect/index.js +1 -1
- package/dist/components/FieldSelect/styles.module.css +129 -27
- package/dist/components/FieldSelect/types.d.ts +42 -37
- package/dist/components/FieldSelect/utils.d.ts +19 -0
- package/dist/components/FieldSelect/utils.js +112 -0
- package/dist/helperComponents/FieldContainerPrivate/styles.module.css +30 -6
- package/package.json +5 -3
- package/src/components/FieldDecorator/Header.tsx +6 -1
- package/src/components/FieldDecorator/styles.module.scss +38 -30
- package/src/components/FieldSelect/FieldSelect.tsx +13 -21
- package/src/components/FieldSelect/FieldSelectMultiple.tsx +255 -0
- package/src/components/FieldSelect/FieldSelectSingle.tsx +159 -99
- package/src/components/FieldSelect/hooks.ts +125 -0
- package/src/components/FieldSelect/index.ts +12 -1
- package/src/components/FieldSelect/styles.module.scss +71 -31
- package/src/components/FieldSelect/types.ts +55 -40
- package/src/components/FieldSelect/utils.ts +163 -0
- package/src/helperComponents/FieldContainerPrivate/FieldContainerPrivate.tsx +2 -0
- package/src/helperComponents/FieldContainerPrivate/styles.module.scss +32 -11
- package/dist/components/FieldSelect/FieldSelectBase.d.ts +0 -31
- package/dist/components/FieldSelect/FieldSelectBase.js +0 -51
- package/dist/components/FieldSelect/FieldSelectMulti.d.ts +0 -37
- package/dist/components/FieldSelect/FieldSelectMulti.js +0 -89
- package/dist/components/FieldSelect/constants.d.ts +0 -7
- package/dist/components/FieldSelect/constants.js +0 -6
- package/dist/components/FieldSelect/helpers/getArrowIcon.d.ts +0 -8
- package/dist/components/FieldSelect/helpers/getArrowIcon.js +0 -8
- package/dist/components/FieldSelect/helpers/getDisplayedValue.d.ts +0 -10
- package/dist/components/FieldSelect/helpers/getDisplayedValue.js +0 -12
- package/dist/components/FieldSelect/helpers/index.d.ts +0 -2
- package/dist/components/FieldSelect/helpers/index.js +0 -2
- package/dist/components/FieldSelect/hooks/index.d.ts +0 -3
- package/dist/components/FieldSelect/hooks/index.js +0 -3
- package/dist/components/FieldSelect/hooks/useFilteredOptions.d.ts +0 -7
- package/dist/components/FieldSelect/hooks/useFilteredOptions.js +0 -6
- package/dist/components/FieldSelect/hooks/useList.d.ts +0 -37
- package/dist/components/FieldSelect/hooks/useList.js +0 -52
- package/dist/components/FieldSelect/hooks/useListNavigation.d.ts +0 -26
- package/dist/components/FieldSelect/hooks/useListNavigation.js +0 -48
- package/src/components/FieldSelect/FieldSelectBase.tsx +0 -222
- package/src/components/FieldSelect/FieldSelectMulti.tsx +0 -163
- package/src/components/FieldSelect/constants.ts +0 -9
- package/src/components/FieldSelect/helpers/getArrowIcon.ts +0 -9
- package/src/components/FieldSelect/helpers/getDisplayedValue.ts +0 -25
- package/src/components/FieldSelect/helpers/index.ts +0 -2
- package/src/components/FieldSelect/hooks/index.ts +0 -3
- package/src/components/FieldSelect/hooks/useFilteredOptions.ts +0 -23
- package/src/components/FieldSelect/hooks/useList.ts +0 -87
- 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
|
-
|
|
10
|
-
|
|
11
|
-
label: string;
|
|
12
|
-
};
|
|
17
|
+
// eslint-disable-next-line no-use-before-define
|
|
18
|
+
export type OptionProps = BaseOptionProps | AccordionOptionProps | GroupOptionProps | NestListOptionProps;
|
|
13
19
|
|
|
14
|
-
|
|
15
|
-
|
|
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
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
75
|
+
footer?: ListProps['footer'];
|
|
61
76
|
|
|
62
|
-
|
|
63
|
-
/** Выбранное значение: <br> - одно для single mode */
|
|
64
|
-
value?: Option['value'];
|
|
65
|
-
/** Колбек смены значения */
|
|
66
|
-
onChange?(value: Option['value']): void;
|
|
67
|
-
};
|
|
77
|
+
search?: SearchState;
|
|
68
78
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
|
78
|
-
|
|
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
|
+
}
|
|
@@ -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
|
-
&,
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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
|
-
&,
|
|
119
|
+
&,
|
|
120
|
+
input,
|
|
121
|
+
select,
|
|
122
|
+
textarea,
|
|
123
|
+
span {
|
|
116
124
|
cursor: pointer;
|
|
117
125
|
}
|
|
118
126
|
}
|
|
119
127
|
|
|
120
128
|
&[data-readonly] {
|
|
121
|
-
&,
|
|
129
|
+
&,
|
|
130
|
+
input,
|
|
131
|
+
select,
|
|
132
|
+
textarea,
|
|
133
|
+
span {
|
|
122
134
|
cursor: default;
|
|
123
135
|
}
|
|
124
136
|
|
|
125
|
-
&,
|
|
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,
|
|
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
|
-
&,
|
|
154
|
+
&,
|
|
155
|
+
input,
|
|
156
|
+
select,
|
|
157
|
+
textarea,
|
|
158
|
+
span {
|
|
141
159
|
cursor: not-allowed;
|
|
142
160
|
}
|
|
143
161
|
|
|
144
|
-
&,
|
|
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,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
|
-
}
|