@true-engineering/true-react-common-ui-kit 3.24.1 → 3.25.1
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/LICENSE +201 -201
- package/README.md +21 -0
- package/dist/components/Select/CustomSelect.stories.d.ts +1 -1
- package/dist/components/Select/MultiSelect.stories.d.ts +2 -2
- package/dist/components/Select/Select.d.ts +14 -9
- package/dist/components/Select/Select.styles.d.ts +5 -5
- package/dist/components/Select/components/SelectList/SelectList.d.ts +7 -6
- package/dist/components/Select/components/SelectList/SelectList.styles.d.ts +1 -1
- package/dist/components/Select/components/SelectListItem/SelectListItem.d.ts +4 -3
- package/dist/components/Select/helpers.d.ts +0 -3
- package/dist/true-react-common-ui-kit.js +176 -142
- package/dist/true-react-common-ui-kit.js.map +1 -1
- package/dist/true-react-common-ui-kit.umd.cjs +175 -141
- package/dist/true-react-common-ui-kit.umd.cjs.map +1 -1
- package/package.json +1 -1
- package/src/components/AccountInfo/AccountInfo.stories.tsx +32 -32
- package/src/components/AccountInfo/AccountInfo.tsx +80 -80
- package/src/components/AddButton/AddButton.stories.tsx +21 -21
- package/src/components/AddButton/AddButton.tsx +52 -52
- package/src/components/Button/Button.tsx +129 -129
- package/src/components/Colors/Colors.stories.tsx +7 -7
- package/src/components/DateInput/DateInput.tsx +90 -90
- package/src/components/DateInput/constants.ts +2 -2
- package/src/components/Description/Description.stories.tsx +27 -27
- package/src/components/Description/Description.tsx +61 -61
- package/src/components/FiltersPane/components/FilterValueView/FilterValueView.tsx +166 -166
- package/src/components/FiltersPane/components/FilterWithDates/FilterWithDates.tsx +210 -210
- package/src/components/FiltersPane/components/FilterWithPeriod/FilterWithPeriod.tsx +177 -177
- package/src/components/Flag/Flag.stories.tsx +29 -29
- package/src/components/Flag/Flag.tsx +26 -26
- package/src/components/Flag/augment.d.ts +1 -1
- package/src/components/FlexibleTable/components/FlexibleTableCell/FlexibleTableCell.styles.ts +38 -38
- package/src/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.styles.ts +25 -25
- package/src/components/FlexibleTable/helpers.ts +13 -13
- package/src/components/Icon/Icon.stories.tsx +86 -86
- package/src/components/Icon/complexIcons/augment.d.ts +1 -1
- package/src/components/Icon/complexIcons/avatarGreen.svg +57 -57
- package/src/components/Icon/complexIcons/index.ts +1 -1
- package/src/components/IncrementInput/IncrementInput.tsx +105 -105
- package/src/components/Input/Input.tsx +297 -297
- package/src/components/Input/types.ts +32 -32
- package/src/components/List/List.stories.tsx +70 -70
- package/src/components/List/List.tsx +33 -33
- package/src/components/List/components/ListItem/ListItem.tsx +57 -57
- package/src/components/Modal/Modal.stories.tsx +105 -105
- package/src/components/MultiSelect/MultiSelect.stories.tsx +46 -46
- package/src/components/MultiSelect/MultiSelect.tsx +106 -106
- package/src/components/MultiSelect/components/MultiSelectInput/MultiSelectInput.tsx +53 -53
- package/src/components/Notification/Notification.stories.tsx +46 -46
- package/src/components/Notification/Notification.tsx +69 -69
- package/src/components/NumberInput/NumberInput.tsx +137 -137
- package/src/components/NumberInput/helpers.ts +4 -6
- package/src/components/NumberInput/index.ts +1 -1
- package/src/components/PhoneInput/PhoneInput.tsx +214 -214
- package/src/components/PhoneInput/components/PhoneInputCountryList/PhoneInputCountryList.tsx +155 -155
- package/src/components/PhoneInput/types.ts +16 -16
- package/src/components/RadioButton/RadioButton.stories.tsx +46 -46
- package/src/components/RadioButton/RadioButton.tsx +57 -57
- package/src/components/ScrollIntoViewIfNeeded/index.ts +1 -1
- package/src/components/Select/CustomSelect.stories.tsx +52 -16
- package/src/components/Select/MultiSelect.stories.tsx +3 -3
- package/src/components/Select/Select.stories.tsx +235 -235
- package/src/components/Select/Select.styles.ts +8 -7
- package/src/components/Select/Select.tsx +106 -62
- package/src/components/Select/components/SelectList/SelectList.styles.ts +6 -4
- package/src/components/Select/components/SelectList/SelectList.tsx +25 -29
- package/src/components/Select/components/SelectListItem/SelectListItem.tsx +23 -19
- package/src/components/Select/constants.ts +2 -2
- package/src/components/Select/helpers.ts +0 -7
- package/src/components/Select/types.ts +1 -1
- package/src/components/Selector/Selector.stories.tsx +62 -62
- package/src/components/Selector/Selector.tsx +115 -115
- package/src/components/Selector/index.ts +2 -2
- package/src/components/Selector/types.ts +12 -12
- package/src/components/Skeleton/Skeleton.stories.tsx +19 -19
- package/src/components/SmartInput/SmartInput.tsx +134 -134
- package/src/components/Status/Status.stories.tsx +73 -73
- package/src/components/Status/Status.styles.ts +143 -143
- package/src/components/Status/Status.tsx +49 -49
- package/src/components/Status/constants.ts +11 -11
- package/src/components/Status/index.ts +3 -3
- package/src/components/Status/types.ts +5 -5
- package/src/components/Switch/Switch.stories.tsx +40 -40
- package/src/components/Switch/Switch.tsx +75 -75
- package/src/components/TextWithInfo/TextWithInfo.stories.tsx +53 -53
- package/src/components/TextWithInfo/TextWithInfo.tsx +62 -62
- package/src/components/TextWithTooltip/TextWithTooltip.stories.tsx +58 -58
- package/src/components/TextWithTooltip/TextWithTooltip.tsx +2 -3
- package/src/components/ThemedPreloader/ThemedPreloader.stories.tsx +41 -41
- package/src/components/ThemedPreloader/ThemedPreloader.tsx +54 -54
- package/src/components/ThemedPreloader/components/DefaultPreloader/index.ts +1 -1
- package/src/components/Toaster/Toaster.stories.tsx +30 -30
- package/src/components/Tooltip/Tooltip.stories.tsx +19 -19
- package/src/components/Tooltip/Tooltip.tsx +35 -35
- package/src/components/Tooltip/types.ts +1 -1
- package/src/helpers/popper-helpers.ts +17 -17
- package/src/hooks/use-dropdown.ts +84 -84
- package/src/hooks/use-is-mounted.ts +15 -15
- package/src/theme/helpers.ts +76 -76
- package/src/vite-env.d.ts +1 -1
|
@@ -7,7 +7,7 @@ import { Input, type IInputStyles } from '../Input';
|
|
|
7
7
|
import { TextButton } from '../TextButton';
|
|
8
8
|
import { Select, type ISelectProps } from './Select';
|
|
9
9
|
|
|
10
|
-
interface ISelectWithCustomProps<
|
|
10
|
+
interface ISelectWithCustomProps<Option> extends ISelectProps<Option> {
|
|
11
11
|
shouldUsePopper?: boolean;
|
|
12
12
|
shouldRenderInBody?: boolean;
|
|
13
13
|
shouldHideOnScroll?: boolean;
|
|
@@ -25,14 +25,10 @@ const useSelectStyles = createUseStyles({
|
|
|
25
25
|
},
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
width: '100%',
|
|
28
|
+
const useCustomListHeaderStyles = createUseStyles({
|
|
29
|
+
customListHeader: {
|
|
31
30
|
padding: [10, 20],
|
|
32
|
-
|
|
33
|
-
alignSelf: 'stretch',
|
|
34
|
-
backgroundColor: '#ffffff',
|
|
35
|
-
cursor: 'default',
|
|
31
|
+
boxSizing: 'border-box',
|
|
36
32
|
},
|
|
37
33
|
|
|
38
34
|
defaultView: {
|
|
@@ -47,6 +43,16 @@ const useDefaultOptionStyles = createUseStyles({
|
|
|
47
43
|
},
|
|
48
44
|
});
|
|
49
45
|
|
|
46
|
+
const useCustomListFooterStyles = createUseStyles({
|
|
47
|
+
customListFooter: {
|
|
48
|
+
gap: 10,
|
|
49
|
+
padding: [6, 20],
|
|
50
|
+
boxSizing: 'border-box',
|
|
51
|
+
display: 'flex',
|
|
52
|
+
justifyContent: 'center',
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
50
56
|
const inputTweakStyles: IInputStyles = {
|
|
51
57
|
inputWrapper: {
|
|
52
58
|
height: 24,
|
|
@@ -67,12 +73,12 @@ const buttonTweakStyles: IButtonStyles = {
|
|
|
67
73
|
},
|
|
68
74
|
};
|
|
69
75
|
|
|
70
|
-
interface
|
|
76
|
+
interface ICustomListHeaderProps {
|
|
71
77
|
onAdd?: (option?: string) => void;
|
|
72
78
|
}
|
|
73
79
|
|
|
74
|
-
function
|
|
75
|
-
const classes =
|
|
80
|
+
function CustomListHeader({ onAdd }: ICustomListHeaderProps) {
|
|
81
|
+
const classes = useCustomListHeaderStyles();
|
|
76
82
|
|
|
77
83
|
const [isAdding, setIsAdding] = useState(false);
|
|
78
84
|
const [inputValue, setInputValue] = useState('');
|
|
@@ -84,7 +90,7 @@ function CustomDefaultOption({ onAdd }: ICustomDefaultOptionProps) {
|
|
|
84
90
|
};
|
|
85
91
|
|
|
86
92
|
return (
|
|
87
|
-
<div className={classes.
|
|
93
|
+
<div className={classes.customListHeader} onClick={(event) => event.stopPropagation()}>
|
|
88
94
|
{!isAdding && (
|
|
89
95
|
<div className={classes.defaultView}>
|
|
90
96
|
<TextButton icon="plus" hasCircleUnderIcon isBold onClick={() => setIsAdding(true)}>
|
|
@@ -109,6 +115,26 @@ function CustomDefaultOption({ onAdd }: ICustomDefaultOptionProps) {
|
|
|
109
115
|
);
|
|
110
116
|
}
|
|
111
117
|
|
|
118
|
+
interface ICustomListFooterProps {
|
|
119
|
+
onReset?: () => void;
|
|
120
|
+
onClear?: () => void;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function CustomListFooter({ onReset, onClear }: ICustomListFooterProps) {
|
|
124
|
+
const classes = useCustomListFooterStyles();
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<div className={classes.customListFooter}>
|
|
128
|
+
<Button view="secondary" size="s" onClick={onReset}>
|
|
129
|
+
Reset
|
|
130
|
+
</Button>
|
|
131
|
+
<Button view="secondary" size="s" onClick={onClear}>
|
|
132
|
+
Clear
|
|
133
|
+
</Button>
|
|
134
|
+
</div>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
112
138
|
function SelectWithCustomProps({
|
|
113
139
|
noMatchesLabel,
|
|
114
140
|
shouldUsePopper,
|
|
@@ -122,13 +148,22 @@ function SelectWithCustomProps({
|
|
|
122
148
|
const [inputValue, setInputValue] = useState<string>();
|
|
123
149
|
const [options, setOptions] = useState<string[]>([]);
|
|
124
150
|
|
|
125
|
-
const
|
|
151
|
+
const handleAdd = (option?: string) => {
|
|
126
152
|
if (isStringNotEmpty(option)) {
|
|
127
153
|
setOptions((prevOptions) => [...prevOptions, option]);
|
|
128
154
|
}
|
|
129
155
|
};
|
|
130
156
|
|
|
131
|
-
const
|
|
157
|
+
const handleReset = () => {
|
|
158
|
+
setInputValue(undefined);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const handleClear = () => {
|
|
162
|
+
setInputValue(undefined);
|
|
163
|
+
setOptions([]);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const handleRemove = (option: string) => {
|
|
132
167
|
setOptions((prevOptions) => prevOptions.filter((entry) => entry !== option));
|
|
133
168
|
};
|
|
134
169
|
|
|
@@ -142,7 +177,7 @@ function SelectWithCustomProps({
|
|
|
142
177
|
onClick={(event) => {
|
|
143
178
|
event.stopPropagation();
|
|
144
179
|
|
|
145
|
-
|
|
180
|
+
handleRemove(value);
|
|
146
181
|
}}
|
|
147
182
|
/>
|
|
148
183
|
</div>
|
|
@@ -159,7 +194,8 @@ function SelectWithCustomProps({
|
|
|
159
194
|
return (
|
|
160
195
|
<Select
|
|
161
196
|
{...props}
|
|
162
|
-
|
|
197
|
+
header={<CustomListHeader onAdd={handleAdd} />}
|
|
198
|
+
footer={<CustomListFooter onReset={handleReset} onClear={handleClear} />}
|
|
163
199
|
noMatchesLabel={isStringNotEmpty(noMatchesLabel) ? noMatchesLabel : undefined}
|
|
164
200
|
tweakStyles={{
|
|
165
201
|
tweakSelectList: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ReactNode, useEffect, useState } from 'react';
|
|
2
2
|
import { isStringNotEmpty } from '@true-engineering/true-react-platform-helpers';
|
|
3
3
|
import { ComponentMeta, ComponentStory } from '@storybook/react';
|
|
4
|
-
import { Select, ISelectProps } from './Select';
|
|
4
|
+
import { Select, ISelectProps, IMultipleSelectProps } from './Select';
|
|
5
5
|
|
|
6
6
|
interface ObjectValue {
|
|
7
7
|
name: string;
|
|
@@ -60,7 +60,7 @@ const objectOptions: ObjectValue[] = [
|
|
|
60
60
|
const getRandomInt = (min: number, max: number) =>
|
|
61
61
|
Math.floor(Math.random() * (Math.floor(max) - Math.ceil(min))) + Math.ceil(min);
|
|
62
62
|
|
|
63
|
-
interface ISelectWithCustomProps<T> extends
|
|
63
|
+
interface ISelectWithCustomProps<T> extends IMultipleSelectProps<T> {
|
|
64
64
|
valuesType: 'strings' | 'objects';
|
|
65
65
|
shouldUseReactNodes?: boolean;
|
|
66
66
|
shouldUsePopper?: boolean;
|
|
@@ -160,7 +160,7 @@ function SelectWithCustomProps<T>({
|
|
|
160
160
|
return (
|
|
161
161
|
<Select
|
|
162
162
|
{...rest}
|
|
163
|
-
{...(props as unknown as
|
|
163
|
+
{...(props as unknown as IMultipleSelectProps<any>)}
|
|
164
164
|
{...(shouldRenderSearchInputInList && {
|
|
165
165
|
searchInput: { shouldRenderInList: true },
|
|
166
166
|
})}
|
|
@@ -1,235 +1,235 @@
|
|
|
1
|
-
import { ReactNode, useEffect, useState } from 'react';
|
|
2
|
-
import { isStringNotEmpty } from '@true-engineering/true-react-platform-helpers';
|
|
3
|
-
import { ComponentMeta, ComponentStory } from '@storybook/react';
|
|
4
|
-
import { Select, ISelectProps } from './Select';
|
|
5
|
-
|
|
6
|
-
interface ObjectValue {
|
|
7
|
-
name: string;
|
|
8
|
-
age: number;
|
|
9
|
-
isDisabled?: boolean;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const inlineStyles = [undefined, 'left', 'right', 'middle'];
|
|
13
|
-
const borders: Array<ISelectProps<any>['border']> = [undefined, 'left', 'top', 'right', 'bottom'];
|
|
14
|
-
const errorPositions: Array<ISelectProps<any>['errorPosition']> = ['bottom', 'top'];
|
|
15
|
-
|
|
16
|
-
const genLetters = (qnt = 1): string =>
|
|
17
|
-
Math.random()
|
|
18
|
-
.toString(36)
|
|
19
|
-
.replace(/[^a-z]+/g, '')
|
|
20
|
-
.substr(0, qnt);
|
|
21
|
-
|
|
22
|
-
const convertObjectToString = (v: ObjectValue): string => v.name;
|
|
23
|
-
|
|
24
|
-
const convertObjectToId = (v: ObjectValue): string => `${v.name}${v.age}`;
|
|
25
|
-
|
|
26
|
-
const convertObjectToReactNode = (v: ObjectValue, isDisabled: boolean): ReactNode => (
|
|
27
|
-
<span style={{ color: isDisabled ? 'red' : undefined }}>
|
|
28
|
-
<i>{v.name}</i>, {v.age}
|
|
29
|
-
</span>
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
const convertStringToReactNode = (v: string): ReactNode => <i>{v}</i>;
|
|
33
|
-
|
|
34
|
-
const isOptionDisabled = (option: string) => option.startsWith('Опция');
|
|
35
|
-
const isObjectOptionDisabled = (option: ObjectValue) => option.age > 30;
|
|
36
|
-
|
|
37
|
-
const stringOptions = [
|
|
38
|
-
'Опция 1',
|
|
39
|
-
'Опция 11',
|
|
40
|
-
'Еще одна опция',
|
|
41
|
-
'Еще выбор',
|
|
42
|
-
'Очень длинная опция вот такой текст',
|
|
43
|
-
'1',
|
|
44
|
-
'2',
|
|
45
|
-
'3',
|
|
46
|
-
'4',
|
|
47
|
-
];
|
|
48
|
-
|
|
49
|
-
const objectOptions: ObjectValue[] = [
|
|
50
|
-
{ name: 'Ivan', age: 34 },
|
|
51
|
-
{ name: 'Ivan', age: 42 },
|
|
52
|
-
{ name: 'Konstantin', age: 11 },
|
|
53
|
-
{ name: 'Mikhail', age: 24 },
|
|
54
|
-
{ name: 'Maria', age: 45, isDisabled: true },
|
|
55
|
-
{ name: 'Elena', age: 14 },
|
|
56
|
-
{ name: 'Artem', age: 23 },
|
|
57
|
-
];
|
|
58
|
-
|
|
59
|
-
// Максимум не включается, минимум включается
|
|
60
|
-
const getRandomInt = (min: number, max: number) =>
|
|
61
|
-
Math.floor(Math.random() * (Math.floor(max) - Math.ceil(min))) + Math.ceil(min);
|
|
62
|
-
|
|
63
|
-
interface ISelectWithCustomProps<T> extends ISelectProps<T> {
|
|
64
|
-
valuesType: 'strings' | 'objects';
|
|
65
|
-
shouldUseReactNodes?: boolean;
|
|
66
|
-
shouldUsePopper?: boolean;
|
|
67
|
-
shouldRenderInBody?: boolean;
|
|
68
|
-
shouldHideOnScroll?: boolean;
|
|
69
|
-
shouldUseCustomIsDisabledFunction?: boolean;
|
|
70
|
-
shouldRenderSearchInputInList?: boolean;
|
|
71
|
-
canBeFlipped?: boolean;
|
|
72
|
-
scrollParent?: 'document' | 'auto';
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function SelectWithCustomProps<T>({
|
|
76
|
-
valuesType,
|
|
77
|
-
optionsMode,
|
|
78
|
-
shouldUseReactNodes,
|
|
79
|
-
shouldUsePopper,
|
|
80
|
-
shouldRenderInBody,
|
|
81
|
-
shouldHideOnScroll,
|
|
82
|
-
shouldUseCustomIsDisabledFunction,
|
|
83
|
-
shouldRenderSearchInputInList,
|
|
84
|
-
canBeFlipped,
|
|
85
|
-
scrollParent,
|
|
86
|
-
noMatchesLabel,
|
|
87
|
-
...rest
|
|
88
|
-
}: ISelectWithCustomProps<T>) {
|
|
89
|
-
const [stringValue, setStringValue] = useState<string>();
|
|
90
|
-
const stringHandler = (newValue?: string) => {
|
|
91
|
-
console.log('change');
|
|
92
|
-
setStringValue(newValue);
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const [objectValue, setObjectValue] = useState<ObjectValue>();
|
|
96
|
-
const objectHandler = (newValue?: ObjectValue) => {
|
|
97
|
-
console.log('change');
|
|
98
|
-
setObjectValue(newValue);
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
const selectValuesType = valuesType;
|
|
102
|
-
const shouldRenderAsReactNodes = shouldUseReactNodes;
|
|
103
|
-
|
|
104
|
-
const getOptions = async (): Promise<Array<string | ObjectValue>> =>
|
|
105
|
-
new Promise((resolve) =>
|
|
106
|
-
setTimeout(() => {
|
|
107
|
-
resolve(
|
|
108
|
-
[...Array(10)].map((_) => {
|
|
109
|
-
if (selectValuesType === 'strings') {
|
|
110
|
-
return genLetters(getRandomInt(3, 10));
|
|
111
|
-
}
|
|
112
|
-
return {
|
|
113
|
-
name: genLetters(getRandomInt(3, 10)),
|
|
114
|
-
age: getRandomInt(10, 70),
|
|
115
|
-
} as ObjectValue;
|
|
116
|
-
}),
|
|
117
|
-
);
|
|
118
|
-
}, 1000),
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
const [dynamicOptions, setDynamicOptions] = useState<Array<string | ObjectValue>>([]);
|
|
122
|
-
|
|
123
|
-
const handleOpen = () => {
|
|
124
|
-
console.log('isOpen');
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const handleBlur = () => {
|
|
128
|
-
console.log('blur');
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
useEffect(() => {
|
|
132
|
-
const api = async () => {
|
|
133
|
-
setDynamicOptions(await getOptions());
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
api();
|
|
137
|
-
}, [selectValuesType]);
|
|
138
|
-
|
|
139
|
-
const props =
|
|
140
|
-
selectValuesType === 'strings'
|
|
141
|
-
? {
|
|
142
|
-
onChange: stringHandler,
|
|
143
|
-
value: stringValue,
|
|
144
|
-
options: optionsMode === 'dynamic' ? dynamicOptions : stringOptions,
|
|
145
|
-
convertValueToReactNode: shouldRenderAsReactNodes ? convertStringToReactNode : undefined,
|
|
146
|
-
isOptionDisabled: shouldUseCustomIsDisabledFunction ? isOptionDisabled : undefined,
|
|
147
|
-
}
|
|
148
|
-
: {
|
|
149
|
-
onChange: objectHandler,
|
|
150
|
-
value: objectValue,
|
|
151
|
-
options: optionsMode === 'dynamic' ? dynamicOptions : objectOptions,
|
|
152
|
-
convertValueToString: convertObjectToString,
|
|
153
|
-
convertValueToId: convertObjectToId,
|
|
154
|
-
convertValueToReactNode: shouldRenderAsReactNodes ? convertObjectToReactNode : undefined,
|
|
155
|
-
isOptionDisabled: shouldUseCustomIsDisabledFunction ? isObjectOptionDisabled : undefined,
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
return (
|
|
159
|
-
<Select
|
|
160
|
-
{...rest}
|
|
161
|
-
{...(props as unknown as ISelectProps<any>)}
|
|
162
|
-
{...(shouldRenderSearchInputInList && {
|
|
163
|
-
searchInput: { shouldRenderInList: true },
|
|
164
|
-
})}
|
|
165
|
-
noMatchesLabel={isStringNotEmpty(noMatchesLabel) ? noMatchesLabel : undefined}
|
|
166
|
-
optionsMode={optionsMode}
|
|
167
|
-
onType={async () => setDynamicOptions(await getOptions())}
|
|
168
|
-
onOpen={handleOpen}
|
|
169
|
-
onBlur={handleBlur}
|
|
170
|
-
dropdownOptions={{
|
|
171
|
-
shouldUsePopper,
|
|
172
|
-
shouldRenderInBody,
|
|
173
|
-
shouldHideOnScroll,
|
|
174
|
-
canBeFlipped,
|
|
175
|
-
scrollParent,
|
|
176
|
-
}}
|
|
177
|
-
/>
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
export default {
|
|
182
|
-
title: 'Controls/Select',
|
|
183
|
-
component: SelectWithCustomProps,
|
|
184
|
-
argTypes: {
|
|
185
|
-
debounceTime: {
|
|
186
|
-
control: { type: 'range', min: 0, max: 1000, step: 100 },
|
|
187
|
-
},
|
|
188
|
-
errorPosition: { control: 'inline-radio', options: errorPositions },
|
|
189
|
-
border: { control: 'inline-radio', options: borders },
|
|
190
|
-
inlineStyle: { control: 'select', options: inlineStyles },
|
|
191
|
-
optionsMode: {
|
|
192
|
-
control: 'inline-radio',
|
|
193
|
-
options: ['normal', 'search', 'dynamic'],
|
|
194
|
-
},
|
|
195
|
-
valuesType: {
|
|
196
|
-
control: 'inline-radio',
|
|
197
|
-
options: ['strings', 'objects'],
|
|
198
|
-
},
|
|
199
|
-
},
|
|
200
|
-
} as ComponentMeta<typeof SelectWithCustomProps>;
|
|
201
|
-
|
|
202
|
-
const Template: ComponentStory<typeof SelectWithCustomProps> = (args) => (
|
|
203
|
-
<SelectWithCustomProps {...args} />
|
|
204
|
-
);
|
|
205
|
-
|
|
206
|
-
export const Default = Template.bind({});
|
|
207
|
-
|
|
208
|
-
Default.args = {
|
|
209
|
-
label: 'Dropdown',
|
|
210
|
-
defaultOptionLabel: 'Default Option',
|
|
211
|
-
noMatchesLabel: 'No matches',
|
|
212
|
-
border: undefined,
|
|
213
|
-
isInvalid: false,
|
|
214
|
-
errorMessage: 'Error Text',
|
|
215
|
-
errorPosition: 'bottom',
|
|
216
|
-
hasFloatingLabel: true,
|
|
217
|
-
hasRequiredLabel: true,
|
|
218
|
-
isDisabled: false,
|
|
219
|
-
isRequired: false,
|
|
220
|
-
isClearable: false,
|
|
221
|
-
isLoading: false,
|
|
222
|
-
debounceTime: 400,
|
|
223
|
-
// custom options
|
|
224
|
-
shouldUseReactNodes: false,
|
|
225
|
-
valuesType: 'strings',
|
|
226
|
-
optionsMode: 'normal',
|
|
227
|
-
shouldUsePopper: false,
|
|
228
|
-
shouldRenderInBody: false,
|
|
229
|
-
shouldHideOnScroll: false,
|
|
230
|
-
shouldUseCustomIsDisabledFunction: false,
|
|
231
|
-
shouldRenderSearchInputInList: false,
|
|
232
|
-
shouldScrollToList: true,
|
|
233
|
-
canBeFlipped: false,
|
|
234
|
-
scrollParent: 'document',
|
|
235
|
-
};
|
|
1
|
+
import { ReactNode, useEffect, useState } from 'react';
|
|
2
|
+
import { isStringNotEmpty } from '@true-engineering/true-react-platform-helpers';
|
|
3
|
+
import { ComponentMeta, ComponentStory } from '@storybook/react';
|
|
4
|
+
import { Select, ISelectProps } from './Select';
|
|
5
|
+
|
|
6
|
+
interface ObjectValue {
|
|
7
|
+
name: string;
|
|
8
|
+
age: number;
|
|
9
|
+
isDisabled?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const inlineStyles = [undefined, 'left', 'right', 'middle'];
|
|
13
|
+
const borders: Array<ISelectProps<any>['border']> = [undefined, 'left', 'top', 'right', 'bottom'];
|
|
14
|
+
const errorPositions: Array<ISelectProps<any>['errorPosition']> = ['bottom', 'top'];
|
|
15
|
+
|
|
16
|
+
const genLetters = (qnt = 1): string =>
|
|
17
|
+
Math.random()
|
|
18
|
+
.toString(36)
|
|
19
|
+
.replace(/[^a-z]+/g, '')
|
|
20
|
+
.substr(0, qnt);
|
|
21
|
+
|
|
22
|
+
const convertObjectToString = (v: ObjectValue): string => v.name;
|
|
23
|
+
|
|
24
|
+
const convertObjectToId = (v: ObjectValue): string => `${v.name}${v.age}`;
|
|
25
|
+
|
|
26
|
+
const convertObjectToReactNode = (v: ObjectValue, isDisabled: boolean): ReactNode => (
|
|
27
|
+
<span style={{ color: isDisabled ? 'red' : undefined }}>
|
|
28
|
+
<i>{v.name}</i>, {v.age}
|
|
29
|
+
</span>
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const convertStringToReactNode = (v: string): ReactNode => <i>{v}</i>;
|
|
33
|
+
|
|
34
|
+
const isOptionDisabled = (option: string) => option.startsWith('Опция');
|
|
35
|
+
const isObjectOptionDisabled = (option: ObjectValue) => option.age > 30;
|
|
36
|
+
|
|
37
|
+
const stringOptions = [
|
|
38
|
+
'Опция 1',
|
|
39
|
+
'Опция 11',
|
|
40
|
+
'Еще одна опция',
|
|
41
|
+
'Еще выбор',
|
|
42
|
+
'Очень длинная опция вот такой текст',
|
|
43
|
+
'1',
|
|
44
|
+
'2',
|
|
45
|
+
'3',
|
|
46
|
+
'4',
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
const objectOptions: ObjectValue[] = [
|
|
50
|
+
{ name: 'Ivan', age: 34 },
|
|
51
|
+
{ name: 'Ivan', age: 42 },
|
|
52
|
+
{ name: 'Konstantin', age: 11 },
|
|
53
|
+
{ name: 'Mikhail', age: 24 },
|
|
54
|
+
{ name: 'Maria', age: 45, isDisabled: true },
|
|
55
|
+
{ name: 'Elena', age: 14 },
|
|
56
|
+
{ name: 'Artem', age: 23 },
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
// Максимум не включается, минимум включается
|
|
60
|
+
const getRandomInt = (min: number, max: number) =>
|
|
61
|
+
Math.floor(Math.random() * (Math.floor(max) - Math.ceil(min))) + Math.ceil(min);
|
|
62
|
+
|
|
63
|
+
interface ISelectWithCustomProps<T> extends ISelectProps<T> {
|
|
64
|
+
valuesType: 'strings' | 'objects';
|
|
65
|
+
shouldUseReactNodes?: boolean;
|
|
66
|
+
shouldUsePopper?: boolean;
|
|
67
|
+
shouldRenderInBody?: boolean;
|
|
68
|
+
shouldHideOnScroll?: boolean;
|
|
69
|
+
shouldUseCustomIsDisabledFunction?: boolean;
|
|
70
|
+
shouldRenderSearchInputInList?: boolean;
|
|
71
|
+
canBeFlipped?: boolean;
|
|
72
|
+
scrollParent?: 'document' | 'auto';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function SelectWithCustomProps<T>({
|
|
76
|
+
valuesType,
|
|
77
|
+
optionsMode,
|
|
78
|
+
shouldUseReactNodes,
|
|
79
|
+
shouldUsePopper,
|
|
80
|
+
shouldRenderInBody,
|
|
81
|
+
shouldHideOnScroll,
|
|
82
|
+
shouldUseCustomIsDisabledFunction,
|
|
83
|
+
shouldRenderSearchInputInList,
|
|
84
|
+
canBeFlipped,
|
|
85
|
+
scrollParent,
|
|
86
|
+
noMatchesLabel,
|
|
87
|
+
...rest
|
|
88
|
+
}: ISelectWithCustomProps<T>) {
|
|
89
|
+
const [stringValue, setStringValue] = useState<string>();
|
|
90
|
+
const stringHandler = (newValue?: string) => {
|
|
91
|
+
console.log('change');
|
|
92
|
+
setStringValue(newValue);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const [objectValue, setObjectValue] = useState<ObjectValue>();
|
|
96
|
+
const objectHandler = (newValue?: ObjectValue) => {
|
|
97
|
+
console.log('change');
|
|
98
|
+
setObjectValue(newValue);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const selectValuesType = valuesType;
|
|
102
|
+
const shouldRenderAsReactNodes = shouldUseReactNodes;
|
|
103
|
+
|
|
104
|
+
const getOptions = async (): Promise<Array<string | ObjectValue>> =>
|
|
105
|
+
new Promise((resolve) =>
|
|
106
|
+
setTimeout(() => {
|
|
107
|
+
resolve(
|
|
108
|
+
[...Array(10)].map((_) => {
|
|
109
|
+
if (selectValuesType === 'strings') {
|
|
110
|
+
return genLetters(getRandomInt(3, 10));
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
name: genLetters(getRandomInt(3, 10)),
|
|
114
|
+
age: getRandomInt(10, 70),
|
|
115
|
+
} as ObjectValue;
|
|
116
|
+
}),
|
|
117
|
+
);
|
|
118
|
+
}, 1000),
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const [dynamicOptions, setDynamicOptions] = useState<Array<string | ObjectValue>>([]);
|
|
122
|
+
|
|
123
|
+
const handleOpen = () => {
|
|
124
|
+
console.log('isOpen');
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const handleBlur = () => {
|
|
128
|
+
console.log('blur');
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
useEffect(() => {
|
|
132
|
+
const api = async () => {
|
|
133
|
+
setDynamicOptions(await getOptions());
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
api();
|
|
137
|
+
}, [selectValuesType]);
|
|
138
|
+
|
|
139
|
+
const props =
|
|
140
|
+
selectValuesType === 'strings'
|
|
141
|
+
? {
|
|
142
|
+
onChange: stringHandler,
|
|
143
|
+
value: stringValue,
|
|
144
|
+
options: optionsMode === 'dynamic' ? dynamicOptions : stringOptions,
|
|
145
|
+
convertValueToReactNode: shouldRenderAsReactNodes ? convertStringToReactNode : undefined,
|
|
146
|
+
isOptionDisabled: shouldUseCustomIsDisabledFunction ? isOptionDisabled : undefined,
|
|
147
|
+
}
|
|
148
|
+
: {
|
|
149
|
+
onChange: objectHandler,
|
|
150
|
+
value: objectValue,
|
|
151
|
+
options: optionsMode === 'dynamic' ? dynamicOptions : objectOptions,
|
|
152
|
+
convertValueToString: convertObjectToString,
|
|
153
|
+
convertValueToId: convertObjectToId,
|
|
154
|
+
convertValueToReactNode: shouldRenderAsReactNodes ? convertObjectToReactNode : undefined,
|
|
155
|
+
isOptionDisabled: shouldUseCustomIsDisabledFunction ? isObjectOptionDisabled : undefined,
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<Select
|
|
160
|
+
{...rest}
|
|
161
|
+
{...(props as unknown as ISelectProps<any>)}
|
|
162
|
+
{...(shouldRenderSearchInputInList && {
|
|
163
|
+
searchInput: { shouldRenderInList: true },
|
|
164
|
+
})}
|
|
165
|
+
noMatchesLabel={isStringNotEmpty(noMatchesLabel) ? noMatchesLabel : undefined}
|
|
166
|
+
optionsMode={optionsMode}
|
|
167
|
+
onType={async () => setDynamicOptions(await getOptions())}
|
|
168
|
+
onOpen={handleOpen}
|
|
169
|
+
onBlur={handleBlur}
|
|
170
|
+
dropdownOptions={{
|
|
171
|
+
shouldUsePopper,
|
|
172
|
+
shouldRenderInBody,
|
|
173
|
+
shouldHideOnScroll,
|
|
174
|
+
canBeFlipped,
|
|
175
|
+
scrollParent,
|
|
176
|
+
}}
|
|
177
|
+
/>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export default {
|
|
182
|
+
title: 'Controls/Select',
|
|
183
|
+
component: SelectWithCustomProps,
|
|
184
|
+
argTypes: {
|
|
185
|
+
debounceTime: {
|
|
186
|
+
control: { type: 'range', min: 0, max: 1000, step: 100 },
|
|
187
|
+
},
|
|
188
|
+
errorPosition: { control: 'inline-radio', options: errorPositions },
|
|
189
|
+
border: { control: 'inline-radio', options: borders },
|
|
190
|
+
inlineStyle: { control: 'select', options: inlineStyles },
|
|
191
|
+
optionsMode: {
|
|
192
|
+
control: 'inline-radio',
|
|
193
|
+
options: ['normal', 'search', 'dynamic'],
|
|
194
|
+
},
|
|
195
|
+
valuesType: {
|
|
196
|
+
control: 'inline-radio',
|
|
197
|
+
options: ['strings', 'objects'],
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
} as ComponentMeta<typeof SelectWithCustomProps>;
|
|
201
|
+
|
|
202
|
+
const Template: ComponentStory<typeof SelectWithCustomProps> = (args) => (
|
|
203
|
+
<SelectWithCustomProps {...args} />
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
export const Default = Template.bind({});
|
|
207
|
+
|
|
208
|
+
Default.args = {
|
|
209
|
+
label: 'Dropdown',
|
|
210
|
+
defaultOptionLabel: 'Default Option',
|
|
211
|
+
noMatchesLabel: 'No matches',
|
|
212
|
+
border: undefined,
|
|
213
|
+
isInvalid: false,
|
|
214
|
+
errorMessage: 'Error Text',
|
|
215
|
+
errorPosition: 'bottom',
|
|
216
|
+
hasFloatingLabel: true,
|
|
217
|
+
hasRequiredLabel: true,
|
|
218
|
+
isDisabled: false,
|
|
219
|
+
isRequired: false,
|
|
220
|
+
isClearable: false,
|
|
221
|
+
isLoading: false,
|
|
222
|
+
debounceTime: 400,
|
|
223
|
+
// custom options
|
|
224
|
+
shouldUseReactNodes: false,
|
|
225
|
+
valuesType: 'strings',
|
|
226
|
+
optionsMode: 'normal',
|
|
227
|
+
shouldUsePopper: false,
|
|
228
|
+
shouldRenderInBody: false,
|
|
229
|
+
shouldHideOnScroll: false,
|
|
230
|
+
shouldUseCustomIsDisabledFunction: false,
|
|
231
|
+
shouldRenderSearchInputInList: false,
|
|
232
|
+
shouldScrollToList: true,
|
|
233
|
+
canBeFlipped: false,
|
|
234
|
+
scrollParent: 'document',
|
|
235
|
+
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { mergeStyles } from '@true-engineering/true-react-platform-helpers';
|
|
2
|
-
import {
|
|
3
|
-
import { IInputStyles } from '../Input';
|
|
4
|
-
import { ISearchInputStyles } from '../SearchInput';
|
|
5
|
-
import { ISelectListStyles } from './components';
|
|
2
|
+
import { animations, createThemedStyles, type ITweakStyles } from '../../theme';
|
|
3
|
+
import { type IInputStyles } from '../Input';
|
|
4
|
+
import { type ISearchInputStyles } from '../SearchInput';
|
|
5
|
+
import { type ISelectListStyles } from './components';
|
|
6
6
|
|
|
7
7
|
export const useStyles = createThemedStyles('Select', {
|
|
8
8
|
root: {
|
|
@@ -26,7 +26,7 @@ export const useStyles = createThemedStyles('Select', {
|
|
|
26
26
|
|
|
27
27
|
withoutPopper: {
|
|
28
28
|
position: 'absolute',
|
|
29
|
-
top: 'calc(100% + 6px)',
|
|
29
|
+
top: 'calc(var(--dropdown-offset, 100%) + 6px)',
|
|
30
30
|
},
|
|
31
31
|
|
|
32
32
|
listWrapperInBody: {
|
|
@@ -43,7 +43,8 @@ export const useStyles = createThemedStyles('Select', {
|
|
|
43
43
|
height: 20,
|
|
44
44
|
cursor: 'pointer',
|
|
45
45
|
zIndex: 1,
|
|
46
|
-
transition:
|
|
46
|
+
transition: animations.defaultTransition,
|
|
47
|
+
transitionProperty: 'transform',
|
|
47
48
|
},
|
|
48
49
|
|
|
49
50
|
activeArrow: {
|
|
@@ -111,7 +112,7 @@ export const getInputStyles = ({
|
|
|
111
112
|
isMultiSelect,
|
|
112
113
|
}: {
|
|
113
114
|
hasReadonlyInput: boolean;
|
|
114
|
-
isMultiSelect
|
|
115
|
+
isMultiSelect?: boolean;
|
|
115
116
|
}): IInputStyles => {
|
|
116
117
|
if (hasReadonlyInput && isMultiSelect) {
|
|
117
118
|
return readonlyMultiSelectStyles;
|