@true-engineering/true-react-common-ui-kit 1.9.0 → 1.11.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/dist/components/Input/Input.styles.d.ts +1 -0
- package/dist/components/Select/Select.d.ts +12 -3
- package/dist/components/Select/Select.styles.d.ts +9 -0
- package/dist/components/Select/SelectList/SelectList.d.ts +7 -4
- package/dist/components/Select/SelectList/SelectList.styles.d.ts +5 -0
- package/dist/components/Select/SelectListItem/SelectListItem.d.ts +14 -0
- package/dist/components/Select/SelectListItem/SelectListItem.styles.d.ts +2 -0
- package/dist/components/Select/constants.d.ts +2 -0
- package/dist/components/Select/helpers.d.ts +4 -1
- package/dist/components/Select/index.d.ts +1 -0
- package/dist/components/Select/types.d.ts +1 -0
- package/dist/helpers/utils.d.ts +2 -0
- package/dist/true-react-common-ui-kit.js +365 -163
- package/dist/true-react-common-ui-kit.js.map +1 -1
- package/dist/true-react-common-ui-kit.umd.cjs +365 -163
- package/dist/true-react-common-ui-kit.umd.cjs.map +1 -1
- package/package.json +1 -1
- package/src/components/Input/Input.styles.ts +2 -0
- package/src/components/Input/Input.tsx +4 -1
- package/src/components/Select/MultiSelect.stories.tsx +263 -0
- package/src/components/Select/Select.styles.ts +11 -0
- package/src/components/Select/Select.tsx +234 -114
- package/src/components/Select/SelectList/SelectList.styles.ts +6 -2
- package/src/components/Select/SelectList/SelectList.tsx +65 -39
- package/src/components/Select/SelectListItem/SelectListItem.styles.ts +14 -0
- package/src/components/Select/SelectListItem/SelectListItem.tsx +73 -0
- package/src/components/Select/constants.ts +2 -0
- package/src/components/Select/helpers.ts +16 -8
- package/src/components/Select/index.ts +1 -0
- package/src/components/Select/types.ts +1 -0
- package/src/helpers/utils.ts +29 -0
|
@@ -26,20 +26,31 @@ import {
|
|
|
26
26
|
useTweakStyles,
|
|
27
27
|
} from '../../hooks';
|
|
28
28
|
import { IDropdownWithPopperOptions } from '../../types';
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
createFilter,
|
|
31
|
+
getTestId,
|
|
32
|
+
hasExactParent,
|
|
33
|
+
isNotEmpty,
|
|
34
|
+
isStringNotEmpty,
|
|
35
|
+
} from '../../helpers';
|
|
30
36
|
import {
|
|
31
37
|
defaultConvertFunction,
|
|
32
38
|
defaultCompareFunction,
|
|
33
|
-
getActiveValueIndex,
|
|
34
39
|
defaultIsOptionDisabled,
|
|
40
|
+
getDefaultConvertToIdFunction,
|
|
41
|
+
isMultiSelectValue,
|
|
35
42
|
} from './helpers';
|
|
36
43
|
import { SelectStyles, styles } from './Select.styles';
|
|
37
44
|
import { ISearchInputProps, SearchInput } from '../SearchInput';
|
|
45
|
+
import { IMultipleSelectValue } from './types';
|
|
46
|
+
import { ALL_OPTION_INDEX, DEFAULT_OPTION_INDEX } from './constants';
|
|
47
|
+
import { renderIcon } from '../../helpers/snippets';
|
|
38
48
|
|
|
39
49
|
export interface ISelectProps<Value>
|
|
40
50
|
extends Omit<IInputProps, 'value' | 'onChange' | 'onBlur' | 'type'> {
|
|
41
51
|
tweakStyles?: SelectStyles;
|
|
42
52
|
defaultOptionLabel?: string;
|
|
53
|
+
allOptionsLabel?: string;
|
|
43
54
|
noMatchesLabel?: string;
|
|
44
55
|
loadingLabel?: ReactNode;
|
|
45
56
|
optionsMode?: 'search' | 'dynamic' | 'normal';
|
|
@@ -50,17 +61,18 @@ export interface ISelectProps<Value>
|
|
|
50
61
|
options: Value[];
|
|
51
62
|
value: Value | undefined;
|
|
52
63
|
shouldScrollToList?: boolean;
|
|
64
|
+
isMultiSelect?: boolean;
|
|
53
65
|
searchInput?: { shouldRenderInList: true } & Pick<
|
|
54
66
|
ISearchInputProps,
|
|
55
67
|
'placeholder'
|
|
56
68
|
>;
|
|
57
69
|
isOptionDisabled?(option: Value): boolean;
|
|
58
|
-
onChange(value
|
|
70
|
+
onChange(value?: Value): void; // подумать как возвращать индекс
|
|
59
71
|
onBlur?(event: Event | SyntheticEvent): void;
|
|
60
72
|
onType?(value: string): Promise<void>;
|
|
61
73
|
optionsFilter?(options: Value[], query: string): Value[];
|
|
62
74
|
onOpen?(): void;
|
|
63
|
-
compareValuesOnChange?(v1
|
|
75
|
+
compareValuesOnChange?(v1?: Value, v2?: Value): boolean;
|
|
64
76
|
// Для избежания проблем юзайте useCallback на эти функции
|
|
65
77
|
// или выносите их из компонента (чтобы не было сайдэфектов от перерендеринга их)
|
|
66
78
|
convertValueToString?(value: Value): string | undefined;
|
|
@@ -68,45 +80,64 @@ export interface ISelectProps<Value>
|
|
|
68
80
|
convertValueToId?(value: Value): string | undefined;
|
|
69
81
|
}
|
|
70
82
|
|
|
71
|
-
export
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
83
|
+
export interface IMultipleSelectProps<Value>
|
|
84
|
+
extends Omit<
|
|
85
|
+
ISelectProps<Value>,
|
|
86
|
+
'value' | 'onChange' | 'compareValuesOnChange'
|
|
87
|
+
> {
|
|
88
|
+
isMultiSelect: true;
|
|
89
|
+
value: IMultipleSelectValue<Value> | undefined;
|
|
90
|
+
onChange(value?: IMultipleSelectValue<Value>): void;
|
|
91
|
+
compareValuesOnChange?(
|
|
92
|
+
v1?: IMultipleSelectValue<Value>,
|
|
93
|
+
v2?: IMultipleSelectValue<Value>,
|
|
94
|
+
): boolean;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function Select<Value>(
|
|
98
|
+
props: ISelectProps<Value> | IMultipleSelectProps<Value>,
|
|
99
|
+
): JSX.Element {
|
|
100
|
+
const {
|
|
101
|
+
options,
|
|
102
|
+
value,
|
|
103
|
+
defaultOptionLabel,
|
|
104
|
+
allOptionsLabel,
|
|
105
|
+
debounceTime = 400,
|
|
106
|
+
optionsMode = 'normal',
|
|
107
|
+
noMatchesLabel,
|
|
108
|
+
loadingLabel,
|
|
109
|
+
tweakStyles,
|
|
110
|
+
testId,
|
|
111
|
+
isReadonly,
|
|
112
|
+
isDisabled,
|
|
113
|
+
dropdownOptions,
|
|
114
|
+
minSymbolsCountToOpenList = 0,
|
|
115
|
+
dropdownIcon = 'chevron-down',
|
|
116
|
+
shouldScrollToList = true,
|
|
117
|
+
searchInput,
|
|
118
|
+
iconType,
|
|
119
|
+
onChange,
|
|
120
|
+
onFocus,
|
|
121
|
+
onBlur,
|
|
122
|
+
onType,
|
|
123
|
+
onOpen,
|
|
124
|
+
isOptionDisabled = defaultIsOptionDisabled,
|
|
125
|
+
compareValuesOnChange = defaultCompareFunction,
|
|
126
|
+
convertValueToString = defaultConvertFunction,
|
|
127
|
+
convertValueToId,
|
|
128
|
+
convertValueToReactNode,
|
|
129
|
+
optionsFilter,
|
|
130
|
+
...inputProps
|
|
131
|
+
} = props;
|
|
101
132
|
const { classes, componentStyles } = useTheme('Select', styles, tweakStyles);
|
|
102
133
|
const isMounted = useIsMounted();
|
|
103
134
|
const [isListOpen, setIsListOpen] = useState(false);
|
|
104
135
|
const [areOptionsLoading, setAreOptionsLoading] = useState(false);
|
|
105
|
-
const hasDefaultOption = defaultOptionLabel
|
|
136
|
+
const hasDefaultOption = isStringNotEmpty(defaultOptionLabel);
|
|
106
137
|
|
|
107
|
-
const [focusedListCellIndex, setFocusedListCellIndex] =
|
|
138
|
+
const [focusedListCellIndex, setFocusedListCellIndex] =
|
|
139
|
+
useState(DEFAULT_OPTION_INDEX);
|
|
108
140
|
const [searchValue, setSearchValue] = useState('');
|
|
109
|
-
|
|
110
141
|
// если мы ввели что то в строку поиска - то этот булеан будет отключаться
|
|
111
142
|
// вынесен отдельно, из-за проблем с дебаунсом при динамич. опциях
|
|
112
143
|
const [shouldShowDefaultOption, setShouldShowDefaultOption] = useState(true);
|
|
@@ -117,36 +148,73 @@ export function Select<Value>({
|
|
|
117
148
|
|
|
118
149
|
const shouldRenderSearchInputInList =
|
|
119
150
|
searchInput?.shouldRenderInList === true;
|
|
120
|
-
|
|
121
151
|
const hasSearchInputInList =
|
|
122
152
|
optionsMode !== 'normal' && shouldRenderSearchInputInList;
|
|
123
153
|
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
154
|
+
const isMultiSelect = isMultiSelectValue(props, value);
|
|
155
|
+
const strValue = isMultiSelect ? value?.[0] : value;
|
|
156
|
+
const shouldShowAllOption =
|
|
157
|
+
isMultiSelect && isNotEmpty(allOptionsLabel) && searchValue === '';
|
|
127
158
|
|
|
128
159
|
const filteredOptions = useMemo(() => {
|
|
129
160
|
if (optionsMode !== 'search') {
|
|
130
161
|
return options;
|
|
131
162
|
}
|
|
132
|
-
if (isNotEmpty(optionsFilter)) {
|
|
133
|
-
return optionsFilter(options, searchValue);
|
|
134
|
-
}
|
|
135
|
-
const lowerCaseValue = searchValue?.toLowerCase();
|
|
136
|
-
return options.filter((option) => {
|
|
137
|
-
const convertedOption = convertValueToString(option);
|
|
138
|
-
return (
|
|
139
|
-
convertedOption !== undefined &&
|
|
140
|
-
convertedOption.toLowerCase().includes(lowerCaseValue)
|
|
141
|
-
);
|
|
142
|
-
});
|
|
143
|
-
}, [optionsMode, optionsFilter, options, convertValueToString, searchValue]);
|
|
144
163
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
164
|
+
const filter =
|
|
165
|
+
optionsFilter ??
|
|
166
|
+
createFilter<Value>((option) => [convertValueToString(option) ?? '']);
|
|
167
|
+
|
|
168
|
+
return filter(options, searchValue);
|
|
169
|
+
}, [optionsFilter, options, convertValueToString, searchValue, optionsMode]);
|
|
170
|
+
|
|
171
|
+
const availableOptions = useMemo(
|
|
172
|
+
() => options.filter((o) => !isOptionDisabled(o)),
|
|
173
|
+
[options, isOptionDisabled],
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
const areAllOptionsSelected =
|
|
177
|
+
isMultiSelect && value?.length === availableOptions.length;
|
|
178
|
+
const shouldShowMultiSelectCounter =
|
|
179
|
+
isMultiSelect &&
|
|
180
|
+
isNotEmpty(value) &&
|
|
181
|
+
value.length > 1 &&
|
|
182
|
+
!areAllOptionsSelected;
|
|
183
|
+
|
|
184
|
+
const optionsIndexesForNavigation = useMemo(() => {
|
|
185
|
+
const result: number[] = [];
|
|
186
|
+
if (shouldShowDefaultOption && hasDefaultOption) {
|
|
187
|
+
result.push(DEFAULT_OPTION_INDEX);
|
|
188
|
+
}
|
|
189
|
+
if (shouldShowAllOption) {
|
|
190
|
+
result.push(ALL_OPTION_INDEX);
|
|
191
|
+
}
|
|
192
|
+
return result.concat(
|
|
193
|
+
filteredOptions.reduce((acc, cur, i) => {
|
|
194
|
+
if (!isOptionDisabled(cur)) {
|
|
195
|
+
acc.push(i);
|
|
196
|
+
}
|
|
197
|
+
return acc;
|
|
198
|
+
}, [] as number[]),
|
|
148
199
|
);
|
|
149
|
-
}, [filteredOptions
|
|
200
|
+
}, [filteredOptions]);
|
|
201
|
+
|
|
202
|
+
const stringValue = isNotEmpty(strValue)
|
|
203
|
+
? convertValueToString(strValue)
|
|
204
|
+
: undefined;
|
|
205
|
+
// Для мультиселекта пытаемся показать "Все опции" если выбраны все опции
|
|
206
|
+
const showedStringValue =
|
|
207
|
+
areAllOptionsSelected && isNotEmpty(allOptionsLabel)
|
|
208
|
+
? allOptionsLabel
|
|
209
|
+
: stringValue;
|
|
210
|
+
|
|
211
|
+
const convertToId = useCallback(
|
|
212
|
+
(v: Value) =>
|
|
213
|
+
(convertValueToId ?? getDefaultConvertToIdFunction(convertValueToString))(
|
|
214
|
+
v,
|
|
215
|
+
),
|
|
216
|
+
[convertValueToId, convertValueToString],
|
|
217
|
+
);
|
|
150
218
|
|
|
151
219
|
const handleListClose = useCallback(
|
|
152
220
|
(event: Event | SyntheticEvent) => {
|
|
@@ -200,25 +268,57 @@ export function Select<Value>({
|
|
|
200
268
|
};
|
|
201
269
|
|
|
202
270
|
const handleOnChange = useCallback(
|
|
203
|
-
(newValue: Value | undefined) => {
|
|
204
|
-
|
|
205
|
-
if (
|
|
206
|
-
|
|
271
|
+
(newValue: Value | IMultipleSelectValue<Value> | undefined) => {
|
|
272
|
+
// Тут беда с типами, сорри
|
|
273
|
+
if (!compareValuesOnChange(value as never, newValue as never)) {
|
|
274
|
+
onChange(newValue as (Value & IMultipleSelectValue<Value>) | undefined);
|
|
207
275
|
}
|
|
208
|
-
onChange(newValue);
|
|
209
276
|
},
|
|
210
277
|
[value, compareValuesOnChange, onChange],
|
|
211
278
|
);
|
|
212
279
|
|
|
213
280
|
const handleOptionSelect = useCallback(
|
|
214
281
|
(index: number, event: MouseEvent<HTMLElement> | KeyboardEvent) => {
|
|
215
|
-
handleOnChange(
|
|
282
|
+
handleOnChange(
|
|
283
|
+
index === DEFAULT_OPTION_INDEX ? undefined : filteredOptions[index],
|
|
284
|
+
);
|
|
216
285
|
handleListClose(event);
|
|
217
286
|
input.current?.blur();
|
|
218
287
|
},
|
|
219
288
|
[handleOnChange, handleListClose, filteredOptions],
|
|
220
289
|
);
|
|
221
290
|
|
|
291
|
+
// MultiSelect
|
|
292
|
+
const handleToggleOptionCheckbox = useCallback(
|
|
293
|
+
(index: number, isSelected: boolean) => {
|
|
294
|
+
if (!isMultiSelect) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Если выбрана не дефолтная опция, которая сетит андеф
|
|
299
|
+
if (
|
|
300
|
+
index === DEFAULT_OPTION_INDEX ||
|
|
301
|
+
(index === ALL_OPTION_INDEX && !isSelected)
|
|
302
|
+
) {
|
|
303
|
+
handleOnChange(undefined);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
if (index === ALL_OPTION_INDEX && isSelected) {
|
|
307
|
+
handleOnChange(availableOptions as IMultipleSelectValue<Value>);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
const option = filteredOptions[index];
|
|
311
|
+
handleOnChange(
|
|
312
|
+
isSelected
|
|
313
|
+
? // Добавляем
|
|
314
|
+
([...(value ?? []), option] as IMultipleSelectValue<Value>)
|
|
315
|
+
: // Убираем
|
|
316
|
+
value?.filter((o) => convertToId(o) !== convertToId(option)),
|
|
317
|
+
);
|
|
318
|
+
},
|
|
319
|
+
[handleOnChange, filteredOptions, isMultiSelect, value],
|
|
320
|
+
);
|
|
321
|
+
|
|
222
322
|
const handleOnType = useCallback(
|
|
223
323
|
async (v: string) => {
|
|
224
324
|
if (onType === undefined) {
|
|
@@ -265,71 +365,65 @@ export function Select<Value>({
|
|
|
265
365
|
}
|
|
266
366
|
|
|
267
367
|
event.stopPropagation();
|
|
368
|
+
const curIndexInNavigation = optionsIndexesForNavigation.findIndex(
|
|
369
|
+
(index) => index === focusedListCellIndex,
|
|
370
|
+
);
|
|
268
371
|
|
|
269
372
|
switch (event.code) {
|
|
270
373
|
case 'Enter':
|
|
271
374
|
case 'NumpadEnter': {
|
|
272
|
-
let
|
|
375
|
+
let indexToSelect = focusedListCellIndex;
|
|
273
376
|
|
|
274
|
-
// если
|
|
377
|
+
// если осталась одна опция в списке,
|
|
275
378
|
// то выбираем ее нажатием на enter
|
|
276
|
-
if (
|
|
277
|
-
|
|
379
|
+
if (
|
|
380
|
+
indexToSelect === DEFAULT_OPTION_INDEX &&
|
|
381
|
+
filteredOptions.length === 1
|
|
382
|
+
) {
|
|
383
|
+
indexToSelect = 0;
|
|
278
384
|
}
|
|
279
385
|
|
|
280
|
-
|
|
386
|
+
if (isMultiSelect) {
|
|
387
|
+
let isThisValueAlreadySelected: boolean;
|
|
388
|
+
if (indexToSelect === ALL_OPTION_INDEX) {
|
|
389
|
+
isThisValueAlreadySelected = areAllOptionsSelected;
|
|
390
|
+
} else {
|
|
391
|
+
// подумать над концептом реального фокуса на опциях, а не вот эти вот focusedCell
|
|
392
|
+
const valueIdToSelect = convertToId(filteredOptions[indexToSelect]);
|
|
393
|
+
isThisValueAlreadySelected =
|
|
394
|
+
value?.some((opt) => convertToId(opt) === valueIdToSelect) ??
|
|
395
|
+
false;
|
|
396
|
+
}
|
|
397
|
+
handleToggleOptionCheckbox(
|
|
398
|
+
indexToSelect,
|
|
399
|
+
!isThisValueAlreadySelected,
|
|
400
|
+
);
|
|
401
|
+
} else {
|
|
402
|
+
handleOptionSelect(indexToSelect, event);
|
|
403
|
+
}
|
|
281
404
|
break;
|
|
282
405
|
}
|
|
283
406
|
|
|
284
407
|
case 'ArrowDown': {
|
|
285
408
|
// чтобы убрать перемещение курсора в инпуте
|
|
286
409
|
event.preventDefault();
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
shouldShowDefaultOption &&
|
|
293
|
-
hasDefaultOption &&
|
|
294
|
-
newIndex > -1 &&
|
|
295
|
-
targetIndex === 0
|
|
296
|
-
) {
|
|
297
|
-
newIndex = -1;
|
|
298
|
-
break;
|
|
299
|
-
}
|
|
300
|
-
if (!isOptionDisabled(filteredOptions[targetIndex])) {
|
|
301
|
-
newIndex = targetIndex;
|
|
302
|
-
break;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
setFocusedListCellIndex(newIndex);
|
|
410
|
+
const targetIndexInNavigation =
|
|
411
|
+
(curIndexInNavigation + 1) % optionsIndexesForNavigation.length;
|
|
412
|
+
setFocusedListCellIndex(
|
|
413
|
+
optionsIndexesForNavigation[targetIndexInNavigation],
|
|
414
|
+
);
|
|
306
415
|
break;
|
|
307
416
|
}
|
|
308
417
|
|
|
309
418
|
case 'ArrowUp': {
|
|
310
419
|
// чтобы убрать перемещение курсора в инпуте
|
|
311
420
|
event.preventDefault();
|
|
312
|
-
const
|
|
313
|
-
(
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
shouldShowDefaultOption &&
|
|
319
|
-
hasDefaultOption &&
|
|
320
|
-
focusedListCellIndex !== -1 &&
|
|
321
|
-
targetIndex === filteredOptions.length - 1
|
|
322
|
-
) {
|
|
323
|
-
// не выносить сет наружу (приведет к багу)
|
|
324
|
-
setFocusedListCellIndex(-1);
|
|
325
|
-
break;
|
|
326
|
-
}
|
|
327
|
-
if (!isOptionDisabled(filteredOptions[targetIndex])) {
|
|
328
|
-
// не выносить сет наружу (приведет к багу)
|
|
329
|
-
setFocusedListCellIndex(targetIndex);
|
|
330
|
-
break;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
421
|
+
const targetIndexInNavigation =
|
|
422
|
+
(curIndexInNavigation - 1 + optionsIndexesForNavigation.length) %
|
|
423
|
+
optionsIndexesForNavigation.length;
|
|
424
|
+
setFocusedListCellIndex(
|
|
425
|
+
optionsIndexesForNavigation[targetIndexInNavigation],
|
|
426
|
+
);
|
|
333
427
|
break;
|
|
334
428
|
}
|
|
335
429
|
}
|
|
@@ -374,6 +468,7 @@ export function Select<Value>({
|
|
|
374
468
|
{},
|
|
375
469
|
componentStyles.tweakInput,
|
|
376
470
|
{ ...(hasReadonlyInput && { input: { cursor: 'pointer' } }) },
|
|
471
|
+
{ ...(isMultiSelect && { inputIcon: { width: 'auto' } }) },
|
|
377
472
|
tweakStyles?.tweakInput,
|
|
378
473
|
) as Styles,
|
|
379
474
|
[tweakStyles?.tweakInput, hasReadonlyInput],
|
|
@@ -405,10 +500,17 @@ export function Select<Value>({
|
|
|
405
500
|
});
|
|
406
501
|
|
|
407
502
|
useEffect(() => {
|
|
408
|
-
|
|
409
|
-
|
|
503
|
+
const val = isMultiSelect ? value?.[0] : value;
|
|
504
|
+
setFocusedListCellIndex(
|
|
505
|
+
optionsIndexesForNavigation.find(
|
|
506
|
+
(index) => filteredOptions[index] === val,
|
|
507
|
+
) ?? optionsIndexesForNavigation[0],
|
|
508
|
+
);
|
|
509
|
+
|
|
510
|
+
if (isOpen) {
|
|
511
|
+
onOpen?.();
|
|
410
512
|
}
|
|
411
|
-
}, [isOpen
|
|
513
|
+
}, [isOpen]);
|
|
412
514
|
|
|
413
515
|
const listEl = (
|
|
414
516
|
<div
|
|
@@ -429,6 +531,8 @@ export function Select<Value>({
|
|
|
429
531
|
? defaultOptionLabel
|
|
430
532
|
: undefined
|
|
431
533
|
}
|
|
534
|
+
allOptionsLabel={shouldShowAllOption ? allOptionsLabel : undefined}
|
|
535
|
+
areAllOptionsSelected={areAllOptionsSelected}
|
|
432
536
|
customListHeader={
|
|
433
537
|
hasSearchInputInList ? (
|
|
434
538
|
<SearchInput
|
|
@@ -454,13 +558,28 @@ export function Select<Value>({
|
|
|
454
558
|
isOptionDisabled={isOptionDisabled}
|
|
455
559
|
convertValueToString={convertValueToString}
|
|
456
560
|
convertValueToReactNode={convertValueToReactNode}
|
|
457
|
-
convertValueToId={
|
|
458
|
-
|
|
561
|
+
convertValueToId={convertToId}
|
|
562
|
+
onOptionSelect={handleOptionSelect}
|
|
563
|
+
onToggleCheckbox={
|
|
564
|
+
isMultiSelect ? handleToggleOptionCheckbox : undefined
|
|
565
|
+
}
|
|
459
566
|
/>
|
|
460
567
|
)}
|
|
461
568
|
</div>
|
|
462
569
|
);
|
|
463
570
|
|
|
571
|
+
const multiSelectCounterWithIcon =
|
|
572
|
+
shouldShowMultiSelectCounter || isNotEmpty(iconType) ? (
|
|
573
|
+
<>
|
|
574
|
+
{shouldShowMultiSelectCounter && (
|
|
575
|
+
<div className={classes.counter}>(+{value.length - 1})</div>
|
|
576
|
+
)}
|
|
577
|
+
{isNotEmpty(iconType) && (
|
|
578
|
+
<div className={classes.icon}>{renderIcon(iconType)}</div>
|
|
579
|
+
)}
|
|
580
|
+
</>
|
|
581
|
+
) : undefined;
|
|
582
|
+
|
|
464
583
|
return (
|
|
465
584
|
<div className={classes.root} onKeyDown={handleKeyDown}>
|
|
466
585
|
<div
|
|
@@ -472,7 +591,7 @@ export function Select<Value>({
|
|
|
472
591
|
value={
|
|
473
592
|
searchValue !== '' && !shouldRenderSearchInputInList
|
|
474
593
|
? searchValue
|
|
475
|
-
:
|
|
594
|
+
: showedStringValue
|
|
476
595
|
}
|
|
477
596
|
onChange={handleInputChange}
|
|
478
597
|
isActive={isListOpen}
|
|
@@ -484,6 +603,7 @@ export function Select<Value>({
|
|
|
484
603
|
isLoading={areOptionsLoading}
|
|
485
604
|
tweakStyles={tweakInputStyles}
|
|
486
605
|
testId={testId}
|
|
606
|
+
iconType={isMultiSelect ? multiSelectCounterWithIcon : iconType}
|
|
487
607
|
{...inputProps}
|
|
488
608
|
/>
|
|
489
609
|
<div
|
|
@@ -2,8 +2,8 @@ import { colors, dimensions, helpers } from '../../../theme';
|
|
|
2
2
|
import { ComponentStyles } from '../../../types';
|
|
3
3
|
|
|
4
4
|
export const ROW_HEIGHT = 40;
|
|
5
|
-
const CONTAINER_PADDING = 10;
|
|
6
|
-
const CELL_PADDING = [10, 20];
|
|
5
|
+
export const CONTAINER_PADDING = 10;
|
|
6
|
+
export const CELL_PADDING = [10, 20];
|
|
7
7
|
|
|
8
8
|
export const styles = {
|
|
9
9
|
root: {
|
|
@@ -45,6 +45,10 @@ export const styles = {
|
|
|
45
45
|
fontSize: 14,
|
|
46
46
|
},
|
|
47
47
|
|
|
48
|
+
cellWithCheckbox: {
|
|
49
|
+
padding: 0,
|
|
50
|
+
},
|
|
51
|
+
|
|
48
52
|
noMatchesLabel: {
|
|
49
53
|
pointerEvents: 'none',
|
|
50
54
|
},
|