@true-engineering/true-react-common-ui-kit 1.1.0 → 1.2.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/Select/Select.d.ts +7 -6
- package/dist/components/Select/SelectList/SelectList.d.ts +8 -8
- package/dist/components/Select/helpers.d.ts +1 -0
- package/dist/true-react-common-ui-kit.js +24 -22
- package/dist/true-react-common-ui-kit.js.map +1 -1
- package/dist/true-react-common-ui-kit.umd.cjs +24 -22
- package/dist/true-react-common-ui-kit.umd.cjs.map +1 -1
- package/package.json +1 -1
- package/src/components/Select/Select.stories.tsx +16 -12
- package/src/components/Select/Select.tsx +31 -29
- package/src/components/Select/SelectList/SelectList.tsx +12 -19
- package/src/components/Select/helpers.ts +5 -0
package/package.json
CHANGED
|
@@ -27,21 +27,19 @@ const genLetters = (qnt = 1): string =>
|
|
|
27
27
|
.replace(/[^a-z]+/g, '')
|
|
28
28
|
.substr(0, qnt);
|
|
29
29
|
|
|
30
|
-
const convertObjectToString = (v
|
|
31
|
-
v !== undefined ? `${v.name}` : undefined;
|
|
30
|
+
const convertObjectToString = (v: ObjectValue): string => v.name;
|
|
32
31
|
|
|
33
|
-
const convertObjectToId = (v
|
|
34
|
-
v !== undefined ? `${v.name}${v.age}` : undefined;
|
|
32
|
+
const convertObjectToId = (v: ObjectValue): string => `${v.name}${v.age}`;
|
|
35
33
|
|
|
36
|
-
const convertObjectToReactNode = (v
|
|
37
|
-
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
34
|
+
const convertObjectToReactNode = (v: ObjectValue): ReactNode => (
|
|
35
|
+
<span>
|
|
36
|
+
<i>{v.name}</i>, {v.age}
|
|
37
|
+
</span>
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const convertStringToReactNode = (v: string): ReactNode => <i>{v}</i>;
|
|
42
41
|
|
|
43
|
-
const
|
|
44
|
-
v !== undefined ? <i>{v}</i> : undefined;
|
|
42
|
+
const isOptionDisabled = (option: string) => option.startsWith('Опция');
|
|
45
43
|
|
|
46
44
|
const stringOptions = [
|
|
47
45
|
'Опция 1',
|
|
@@ -76,6 +74,7 @@ interface ISelectWithCustomProps<T> extends ISelectProps<T> {
|
|
|
76
74
|
shouldUsePopper?: boolean;
|
|
77
75
|
shouldRenderInBody?: boolean;
|
|
78
76
|
shouldHideOnScroll?: boolean;
|
|
77
|
+
shouldUseCustomIsDisabledFunction?: boolean;
|
|
79
78
|
canBeFlipped?: boolean;
|
|
80
79
|
scrollParent?: 'document' | 'auto';
|
|
81
80
|
}
|
|
@@ -87,6 +86,7 @@ function SelectWithCustomProps<T>({
|
|
|
87
86
|
shouldUsePopper,
|
|
88
87
|
shouldRenderInBody,
|
|
89
88
|
shouldHideOnScroll,
|
|
89
|
+
shouldUseCustomIsDisabledFunction,
|
|
90
90
|
canBeFlipped,
|
|
91
91
|
scrollParent,
|
|
92
92
|
...rest
|
|
@@ -165,6 +165,9 @@ function SelectWithCustomProps<T>({
|
|
|
165
165
|
optionsMode={optionsMode}
|
|
166
166
|
onType={async () => setDynamicOptions(await getOptions())}
|
|
167
167
|
onOpen={handleOpen}
|
|
168
|
+
isOptionDisabled={
|
|
169
|
+
shouldUseCustomIsDisabledFunction ? isOptionDisabled : undefined
|
|
170
|
+
}
|
|
168
171
|
dropdownOptions={{
|
|
169
172
|
shouldUsePopper,
|
|
170
173
|
shouldRenderInBody,
|
|
@@ -225,6 +228,7 @@ Default.args = {
|
|
|
225
228
|
shouldUsePopper: false,
|
|
226
229
|
shouldRenderInBody: false,
|
|
227
230
|
shouldHideOnScroll: false,
|
|
231
|
+
shouldUseCustomIsDisabledFunction: false,
|
|
228
232
|
canBeFlipped: false,
|
|
229
233
|
scrollParent: 'document',
|
|
230
234
|
};
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
2
|
ReactNode,
|
|
3
|
+
FocusEvent,
|
|
4
|
+
KeyboardEvent,
|
|
5
|
+
MouseEvent,
|
|
3
6
|
useCallback,
|
|
4
7
|
useEffect,
|
|
5
8
|
useMemo,
|
|
@@ -11,8 +14,7 @@ import clsx from 'clsx';
|
|
|
11
14
|
import { merge } from 'lodash';
|
|
12
15
|
import { debounce } from 'ts-debounce';
|
|
13
16
|
import { Portal } from 'react-overlays';
|
|
14
|
-
|
|
15
|
-
import { SelectList, isOptionDisabled } from './SelectList';
|
|
17
|
+
import { SelectList } from './SelectList';
|
|
16
18
|
import { IInputProps, Input } from '../Input';
|
|
17
19
|
import { IIconType, Icon } from '../Icon';
|
|
18
20
|
import {
|
|
@@ -27,8 +29,8 @@ import {
|
|
|
27
29
|
defaultConvertFunction,
|
|
28
30
|
defaultCompareFunction,
|
|
29
31
|
getActiveValueIndex,
|
|
32
|
+
defaultIsOptionDisabled,
|
|
30
33
|
} from './helpers';
|
|
31
|
-
|
|
32
34
|
import { SelectStyles, styles } from './Select.styles';
|
|
33
35
|
|
|
34
36
|
export interface ISelectProps<Value>
|
|
@@ -36,23 +38,21 @@ export interface ISelectProps<Value>
|
|
|
36
38
|
tweakStyles?: SelectStyles;
|
|
37
39
|
defaultOptionLabel?: string;
|
|
38
40
|
noMatchesLabel?: string;
|
|
39
|
-
loadingLabel?:
|
|
41
|
+
loadingLabel?: ReactNode;
|
|
40
42
|
optionsMode?: 'search' | 'dynamic' | 'normal';
|
|
41
|
-
onType?: (value: string) => Promise<void>;
|
|
42
43
|
debounceTime?: number;
|
|
43
44
|
minSymbolsCountToOpenList?: number;
|
|
44
45
|
dropdownOptions?: IDropdownWithPopperOptions;
|
|
45
46
|
dropdownIcon?: IIconType;
|
|
46
|
-
onOpen?: () => void;
|
|
47
|
-
|
|
48
|
-
optionsFilter?: (options: Value[], query: string) => Value[];
|
|
49
47
|
options: Value[];
|
|
50
48
|
value: Value | undefined;
|
|
51
49
|
shouldScrollToList?: boolean;
|
|
50
|
+
isOptionDisabled?(option: Value): boolean;
|
|
52
51
|
onChange(value: Value | undefined): void; // подумать как возвращать индекс
|
|
52
|
+
onType?(value: string): Promise<void>;
|
|
53
|
+
optionsFilter?(options: Value[], query: string): Value[];
|
|
54
|
+
onOpen?(): void;
|
|
53
55
|
compareValuesOnChange?(v1: Value | undefined, v2: Value | undefined): boolean;
|
|
54
|
-
// возможно делать какую-то индексацию опций
|
|
55
|
-
|
|
56
56
|
// Для избежания проблем юзайте useCallback на эти функции
|
|
57
57
|
// или выносите их из компонента (чтобы не было сайдэфектов от перерендеринга их)
|
|
58
58
|
convertValueToString?(value: Value): string | undefined;
|
|
@@ -63,19 +63,9 @@ export interface ISelectProps<Value>
|
|
|
63
63
|
export function Select<Value>({
|
|
64
64
|
options,
|
|
65
65
|
value,
|
|
66
|
-
onChange,
|
|
67
|
-
compareValuesOnChange = defaultCompareFunction,
|
|
68
|
-
convertValueToString = defaultConvertFunction,
|
|
69
|
-
convertValueToId,
|
|
70
|
-
convertValueToReactNode,
|
|
71
66
|
defaultOptionLabel,
|
|
72
|
-
onFocus,
|
|
73
|
-
onBlur,
|
|
74
|
-
onType,
|
|
75
|
-
onOpen,
|
|
76
67
|
debounceTime = 400,
|
|
77
68
|
optionsMode = 'normal',
|
|
78
|
-
optionsFilter,
|
|
79
69
|
noMatchesLabel,
|
|
80
70
|
loadingLabel,
|
|
81
71
|
tweakStyles,
|
|
@@ -85,6 +75,17 @@ export function Select<Value>({
|
|
|
85
75
|
minSymbolsCountToOpenList = 0,
|
|
86
76
|
dropdownIcon = 'chevron-down',
|
|
87
77
|
shouldScrollToList = true,
|
|
78
|
+
onChange,
|
|
79
|
+
onFocus,
|
|
80
|
+
onBlur,
|
|
81
|
+
onType,
|
|
82
|
+
onOpen,
|
|
83
|
+
isOptionDisabled = defaultIsOptionDisabled,
|
|
84
|
+
compareValuesOnChange = defaultCompareFunction,
|
|
85
|
+
convertValueToString = defaultConvertFunction,
|
|
86
|
+
convertValueToId,
|
|
87
|
+
convertValueToReactNode,
|
|
88
|
+
optionsFilter,
|
|
88
89
|
...inputProps
|
|
89
90
|
}: ISelectProps<Value>): JSX.Element {
|
|
90
91
|
const { classes, componentStyles } = useTheme('Select', styles, tweakStyles);
|
|
@@ -144,7 +145,7 @@ export function Select<Value>({
|
|
|
144
145
|
setIsListOpen(true);
|
|
145
146
|
};
|
|
146
147
|
|
|
147
|
-
const handleFocus = (event:
|
|
148
|
+
const handleFocus = (event: FocusEvent<HTMLInputElement>) => {
|
|
148
149
|
if (onFocus !== undefined) {
|
|
149
150
|
onFocus(event);
|
|
150
151
|
}
|
|
@@ -155,7 +156,7 @@ export function Select<Value>({
|
|
|
155
156
|
handleListOpen();
|
|
156
157
|
};
|
|
157
158
|
|
|
158
|
-
const handleBlur = (event:
|
|
159
|
+
const handleBlur = (event: FocusEvent<HTMLInputElement>) => {
|
|
159
160
|
if (onBlur !== undefined) {
|
|
160
161
|
onBlur(event);
|
|
161
162
|
}
|
|
@@ -222,7 +223,7 @@ export function Select<Value>({
|
|
|
222
223
|
setSearchValue(v);
|
|
223
224
|
};
|
|
224
225
|
|
|
225
|
-
const handleKeyDown = (event:
|
|
226
|
+
const handleKeyDown = (event: KeyboardEvent) => {
|
|
226
227
|
if (!isListOpen) {
|
|
227
228
|
return;
|
|
228
229
|
}
|
|
@@ -387,7 +388,7 @@ export function Select<Value>({
|
|
|
387
388
|
{...inputProps}
|
|
388
389
|
/>
|
|
389
390
|
<div
|
|
390
|
-
onMouseDown={(event:
|
|
391
|
+
onMouseDown={(event: MouseEvent) => {
|
|
391
392
|
event.preventDefault();
|
|
392
393
|
}}
|
|
393
394
|
onClick={onArrowClick}
|
|
@@ -415,10 +416,6 @@ export function Select<Value>({
|
|
|
415
416
|
{isOpen && (
|
|
416
417
|
<SelectList
|
|
417
418
|
options={filteredOptions}
|
|
418
|
-
convertValueToString={convertValueToString}
|
|
419
|
-
convertValueToReactNode={convertValueToReactNode}
|
|
420
|
-
convertValueToId={convertValueToId}
|
|
421
|
-
onOptionClick={handleOptionClick}
|
|
422
419
|
defaultOptionLabel={
|
|
423
420
|
hasDefaultOption && shouldShowDefaultOption
|
|
424
421
|
? defaultOptionLabel
|
|
@@ -437,6 +434,11 @@ export function Select<Value>({
|
|
|
437
434
|
!shouldUsePopper &&
|
|
438
435
|
!shouldHideOnScroll
|
|
439
436
|
}
|
|
437
|
+
isOptionDisabled={isOptionDisabled}
|
|
438
|
+
convertValueToString={convertValueToString}
|
|
439
|
+
convertValueToReactNode={convertValueToReactNode}
|
|
440
|
+
convertValueToId={convertValueToId}
|
|
441
|
+
onOptionClick={handleOptionClick}
|
|
440
442
|
/>
|
|
441
443
|
)}
|
|
442
444
|
</div>
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ReactNode, useMemo } from 'react';
|
|
2
2
|
import clsx from 'clsx';
|
|
3
3
|
import { ScrollIntoViewIfNeeded } from '../../ScrollIntoViewIfNeeded';
|
|
4
4
|
import { useTheme } from '../../../hooks';
|
|
5
5
|
import { ICommonProps } from '../../../types';
|
|
6
6
|
import { isNotEmpty } from '../../../helpers';
|
|
7
|
-
|
|
8
7
|
import { SelectListStyles, styles } from './SelectList.styles';
|
|
9
8
|
|
|
10
9
|
export interface ISelectListProps<Value> extends ICommonProps {
|
|
@@ -14,41 +13,35 @@ export interface ISelectListProps<Value> extends ICommonProps {
|
|
|
14
13
|
activeValue?: Value;
|
|
15
14
|
noMatchesLabel?: string;
|
|
16
15
|
isLoading?: boolean;
|
|
17
|
-
loadingLabel?:
|
|
16
|
+
loadingLabel?: ReactNode;
|
|
18
17
|
defaultOptionLabel?: string;
|
|
19
|
-
onOptionClick: (index: number) => void;
|
|
20
18
|
testId?: string;
|
|
21
19
|
shouldScrollToList?: boolean;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
export function isOptionDisabled<Value>(option: Value): boolean {
|
|
28
|
-
return (
|
|
29
|
-
typeof option === 'object' &&
|
|
30
|
-
option !== null &&
|
|
31
|
-
((option as { isDisabled?: boolean })?.isDisabled ?? false)
|
|
32
|
-
);
|
|
20
|
+
onOptionClick(index: number): void;
|
|
21
|
+
isOptionDisabled(value: Value): boolean;
|
|
22
|
+
convertValueToString(value: Value): string | undefined;
|
|
23
|
+
convertValueToReactNode?(value: Value): ReactNode;
|
|
24
|
+
convertValueToId?(value: Value): string | undefined;
|
|
33
25
|
}
|
|
34
26
|
|
|
35
27
|
const DEFAULT_OPTION_INDEX = -1;
|
|
36
28
|
|
|
37
29
|
export function SelectList<Value>({
|
|
38
30
|
options,
|
|
39
|
-
onOptionClick,
|
|
40
31
|
focusedIndex,
|
|
41
32
|
activeValue,
|
|
42
33
|
defaultOptionLabel,
|
|
43
34
|
noMatchesLabel = 'Совпадений не найдено',
|
|
44
35
|
isLoading,
|
|
45
36
|
loadingLabel = 'Загрузка...',
|
|
46
|
-
convertValueToString,
|
|
47
|
-
convertValueToReactNode,
|
|
48
|
-
convertValueToId = convertValueToString,
|
|
49
37
|
tweakStyles,
|
|
50
38
|
testId,
|
|
51
39
|
shouldScrollToList = true,
|
|
40
|
+
isOptionDisabled,
|
|
41
|
+
onOptionClick,
|
|
42
|
+
convertValueToString,
|
|
43
|
+
convertValueToReactNode,
|
|
44
|
+
convertValueToId = convertValueToString,
|
|
52
45
|
}: ISelectListProps<Value>): JSX.Element {
|
|
53
46
|
const { classes } = useTheme('SelectList', styles, tweakStyles);
|
|
54
47
|
const activeValueId = isNotEmpty(activeValue)
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { isNotEmpty } from '../../helpers';
|
|
2
2
|
|
|
3
|
+
export const defaultIsOptionDisabled = <Value>(option: Value): boolean =>
|
|
4
|
+
typeof option === 'object' &&
|
|
5
|
+
option !== null &&
|
|
6
|
+
((option as { isDisabled?: boolean })?.isDisabled ?? false);
|
|
7
|
+
|
|
3
8
|
export const defaultConvertFunction = (v: unknown) =>
|
|
4
9
|
v === undefined ? undefined : String(v);
|
|
5
10
|
|