@true-engineering/true-react-common-ui-kit 1.9.0 → 1.10.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 +10 -0
- package/dist/components/Select/SelectList/SelectList.d.ts +6 -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 +340 -163
- package/dist/true-react-common-ui-kit.js.map +1 -1
- package/dist/true-react-common-ui-kit.umd.cjs +340 -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 +262 -0
- package/src/components/Select/Select.styles.ts +13 -0
- package/src/components/Select/Select.tsx +215 -114
- package/src/components/Select/SelectList/SelectList.styles.ts +6 -2
- package/src/components/Select/SelectList/SelectList.tsx +64 -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
package/package.json
CHANGED
|
@@ -175,12 +175,15 @@ export const Input = forwardRef<HTMLInputElement, IInputProps>(
|
|
|
175
175
|
const hasLabel = isNotEmpty(label);
|
|
176
176
|
const hasPlaceholder =
|
|
177
177
|
(!hasLabel || (hasFocus && !isReadonly)) && isNotEmpty(placeholder);
|
|
178
|
+
const shouldShowUnits =
|
|
179
|
+
(hasValue || (isFocused && !hasPlaceholder)) && hasUnits;
|
|
178
180
|
|
|
179
181
|
const props: InputHTMLAttributes<HTMLInputElement> = {
|
|
180
182
|
className: clsx(classes.input, {
|
|
181
183
|
[classes.withFloatingLabel]: hasFloatingLabel && hasLabel,
|
|
182
184
|
[classes.withIcons]: hasControls,
|
|
183
185
|
[classes.withControls]: hasControls,
|
|
186
|
+
[classes.withUnits]: shouldShowUnits,
|
|
184
187
|
[classes.floatingLabelWithoutPadding]:
|
|
185
188
|
hasFloatingLabel && hasLabel && border === 'bottom',
|
|
186
189
|
}),
|
|
@@ -257,7 +260,7 @@ export const Input = forwardRef<HTMLInputElement, IInputProps>(
|
|
|
257
260
|
</span>
|
|
258
261
|
)}
|
|
259
262
|
|
|
260
|
-
{
|
|
263
|
+
{shouldShowUnits && (
|
|
261
264
|
<div
|
|
262
265
|
className={clsx(classes.unitsWrapper, {
|
|
263
266
|
[classes.withFloatingLabel]: hasFloatingLabel && hasLabel,
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { ReactNode, useEffect, useState } from 'react';
|
|
2
|
+
import { Select, ISelectProps } from './Select';
|
|
3
|
+
import { ComponentMeta, ComponentStory } from '@storybook/react';
|
|
4
|
+
import { isNotEmpty } from '../../helpers';
|
|
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']> = [
|
|
14
|
+
undefined,
|
|
15
|
+
'left',
|
|
16
|
+
'top',
|
|
17
|
+
'right',
|
|
18
|
+
'bottom',
|
|
19
|
+
];
|
|
20
|
+
const errorPositions: Array<ISelectProps<any>['errorPosition']> = [
|
|
21
|
+
'bottom',
|
|
22
|
+
'top',
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const genLetters = (qnt = 1): string =>
|
|
26
|
+
Math.random()
|
|
27
|
+
.toString(36)
|
|
28
|
+
.replace(/[^a-z]+/g, '')
|
|
29
|
+
.substr(0, qnt);
|
|
30
|
+
|
|
31
|
+
const convertObjectToString = (v: ObjectValue): string => v.name;
|
|
32
|
+
|
|
33
|
+
const convertObjectToId = (v: ObjectValue): string => `${v.name}${v.age}`;
|
|
34
|
+
|
|
35
|
+
const convertObjectToReactNode = (
|
|
36
|
+
v: ObjectValue,
|
|
37
|
+
isDisabled: boolean,
|
|
38
|
+
): ReactNode => (
|
|
39
|
+
<span style={{ color: isDisabled ? 'red' : undefined }}>
|
|
40
|
+
<i>{v.name}</i>, {v.age}
|
|
41
|
+
</span>
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const convertStringToReactNode = (v: string): ReactNode => <i>{v}</i>;
|
|
45
|
+
|
|
46
|
+
const isOptionDisabled = (option: string) => option.startsWith('Опция');
|
|
47
|
+
const isObjectOptionDisabled = (option: ObjectValue) => option.age > 30;
|
|
48
|
+
|
|
49
|
+
const stringOptions = [
|
|
50
|
+
'Опция 1',
|
|
51
|
+
'Опция 11',
|
|
52
|
+
'Еще одна опция',
|
|
53
|
+
'Еще выбор',
|
|
54
|
+
'Очень длинная опция вот такой текст',
|
|
55
|
+
'1',
|
|
56
|
+
'2',
|
|
57
|
+
'3',
|
|
58
|
+
'4',
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
const objectOptions: ObjectValue[] = [
|
|
62
|
+
{ name: 'Ivan', age: 34 },
|
|
63
|
+
{ name: 'Ivan', age: 42 },
|
|
64
|
+
{ name: 'Konstantin', age: 11 },
|
|
65
|
+
{ name: 'Mikhail', age: 24 },
|
|
66
|
+
{ name: 'Maria', age: 45, isDisabled: true },
|
|
67
|
+
{ name: 'Elena', age: 14 },
|
|
68
|
+
{ name: 'Artem', age: 23 },
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
// Максимум не включается, минимум включается
|
|
72
|
+
const getRandomInt = (min: number, max: number) =>
|
|
73
|
+
Math.floor(Math.random() * (Math.floor(max) - Math.ceil(min))) +
|
|
74
|
+
Math.ceil(min);
|
|
75
|
+
|
|
76
|
+
interface ISelectWithCustomProps<T> extends ISelectProps<T> {
|
|
77
|
+
valuesType: 'strings' | 'objects';
|
|
78
|
+
shouldUseReactNodes?: boolean;
|
|
79
|
+
shouldUsePopper?: boolean;
|
|
80
|
+
shouldRenderInBody?: boolean;
|
|
81
|
+
shouldHideOnScroll?: boolean;
|
|
82
|
+
shouldUseCustomIsDisabledFunction?: boolean;
|
|
83
|
+
shouldRenderSearchInputInList?: boolean;
|
|
84
|
+
canBeFlipped?: boolean;
|
|
85
|
+
scrollParent?: 'document' | 'auto';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function SelectWithCustomProps<T>({
|
|
89
|
+
valuesType,
|
|
90
|
+
optionsMode,
|
|
91
|
+
shouldUseReactNodes,
|
|
92
|
+
shouldUsePopper,
|
|
93
|
+
shouldRenderInBody,
|
|
94
|
+
shouldHideOnScroll,
|
|
95
|
+
shouldUseCustomIsDisabledFunction,
|
|
96
|
+
shouldRenderSearchInputInList,
|
|
97
|
+
canBeFlipped,
|
|
98
|
+
scrollParent,
|
|
99
|
+
noMatchesLabel,
|
|
100
|
+
...rest
|
|
101
|
+
}: ISelectWithCustomProps<T>) {
|
|
102
|
+
const [stringValue, setStringValue] = useState<string[]>();
|
|
103
|
+
|
|
104
|
+
const stringHandler = (newValue?: string[]) => {
|
|
105
|
+
console.log('change');
|
|
106
|
+
setStringValue(newValue);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const [objectValue, setObjectValue] = useState<ObjectValue[]>();
|
|
110
|
+
|
|
111
|
+
const objectHandler = (newValue?: ObjectValue[]) => {
|
|
112
|
+
console.log('change');
|
|
113
|
+
setObjectValue(newValue);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const selectValuesType = valuesType;
|
|
117
|
+
const shouldRenderAsReactNodes = shouldUseReactNodes;
|
|
118
|
+
|
|
119
|
+
const getOptions = async (): Promise<Array<string | ObjectValue>> =>
|
|
120
|
+
new Promise((resolve) =>
|
|
121
|
+
setTimeout(() => {
|
|
122
|
+
resolve(
|
|
123
|
+
[...Array(10)].map((_) => {
|
|
124
|
+
if (selectValuesType === 'strings') {
|
|
125
|
+
return genLetters(getRandomInt(3, 10));
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
name: genLetters(getRandomInt(3, 10)),
|
|
129
|
+
age: getRandomInt(10, 70),
|
|
130
|
+
} as ObjectValue;
|
|
131
|
+
}),
|
|
132
|
+
);
|
|
133
|
+
}, 1000),
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const [dynamicOptions, setDynamicOptions] = useState<
|
|
137
|
+
Array<string | ObjectValue>
|
|
138
|
+
>([]);
|
|
139
|
+
|
|
140
|
+
const handleOpen = () => {
|
|
141
|
+
console.log('isOpen');
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const handleBlur = () => {
|
|
145
|
+
console.log('blur');
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
useEffect(() => {
|
|
149
|
+
const api = async () => {
|
|
150
|
+
setDynamicOptions(await getOptions());
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
api();
|
|
154
|
+
}, [selectValuesType]);
|
|
155
|
+
|
|
156
|
+
const props =
|
|
157
|
+
selectValuesType === 'strings'
|
|
158
|
+
? {
|
|
159
|
+
onChange: stringHandler,
|
|
160
|
+
value: stringValue,
|
|
161
|
+
options: optionsMode === 'dynamic' ? dynamicOptions : stringOptions,
|
|
162
|
+
convertValueToReactNode: shouldRenderAsReactNodes
|
|
163
|
+
? convertStringToReactNode
|
|
164
|
+
: undefined,
|
|
165
|
+
isOptionDisabled: shouldUseCustomIsDisabledFunction
|
|
166
|
+
? isOptionDisabled
|
|
167
|
+
: undefined,
|
|
168
|
+
}
|
|
169
|
+
: {
|
|
170
|
+
onChange: objectHandler,
|
|
171
|
+
value: objectValue,
|
|
172
|
+
options: optionsMode === 'dynamic' ? dynamicOptions : objectOptions,
|
|
173
|
+
convertValueToString: convertObjectToString,
|
|
174
|
+
convertValueToId: convertObjectToId,
|
|
175
|
+
convertValueToReactNode: shouldRenderAsReactNodes
|
|
176
|
+
? convertObjectToReactNode
|
|
177
|
+
: undefined,
|
|
178
|
+
isOptionDisabled: shouldUseCustomIsDisabledFunction
|
|
179
|
+
? isObjectOptionDisabled
|
|
180
|
+
: undefined,
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<Select
|
|
185
|
+
{...rest}
|
|
186
|
+
{...(props as unknown as ISelectProps<any>)}
|
|
187
|
+
{...(shouldRenderSearchInputInList && {
|
|
188
|
+
searchInput: { shouldRenderInList: true },
|
|
189
|
+
})}
|
|
190
|
+
isMultiSelect
|
|
191
|
+
noMatchesLabel={isNotEmpty(noMatchesLabel) ? noMatchesLabel : undefined}
|
|
192
|
+
optionsMode={optionsMode}
|
|
193
|
+
onType={async () => setDynamicOptions(await getOptions())}
|
|
194
|
+
onOpen={handleOpen}
|
|
195
|
+
onBlur={handleBlur}
|
|
196
|
+
dropdownOptions={{
|
|
197
|
+
shouldUsePopper,
|
|
198
|
+
shouldRenderInBody,
|
|
199
|
+
shouldHideOnScroll,
|
|
200
|
+
canBeFlipped,
|
|
201
|
+
scrollParent,
|
|
202
|
+
}}
|
|
203
|
+
/>
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export default {
|
|
208
|
+
title: 'Select',
|
|
209
|
+
component: SelectWithCustomProps,
|
|
210
|
+
argTypes: {
|
|
211
|
+
debounceTime: {
|
|
212
|
+
control: { type: 'range', min: 0, max: 1000, step: 100 },
|
|
213
|
+
},
|
|
214
|
+
errorPosition: { control: 'inline-radio', options: errorPositions },
|
|
215
|
+
border: { control: 'inline-radio', options: borders },
|
|
216
|
+
inlineStyle: { control: 'select', options: inlineStyles },
|
|
217
|
+
optionsMode: {
|
|
218
|
+
control: 'inline-radio',
|
|
219
|
+
options: ['normal', 'search', 'dynamic'],
|
|
220
|
+
},
|
|
221
|
+
valuesType: {
|
|
222
|
+
control: 'inline-radio',
|
|
223
|
+
options: ['strings', 'objects'],
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
} as ComponentMeta<typeof SelectWithCustomProps>;
|
|
227
|
+
|
|
228
|
+
const Template: ComponentStory<typeof SelectWithCustomProps> = (args) => (
|
|
229
|
+
<SelectWithCustomProps {...args} />
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
export const MultiSelect = Template.bind({});
|
|
233
|
+
|
|
234
|
+
MultiSelect.args = {
|
|
235
|
+
label: 'Dropdown',
|
|
236
|
+
noMatchesLabel: 'No matches',
|
|
237
|
+
border: undefined,
|
|
238
|
+
isInvalid: false,
|
|
239
|
+
errorMessage: 'Error Text',
|
|
240
|
+
errorPosition: 'bottom',
|
|
241
|
+
hasFloatingLabel: true,
|
|
242
|
+
hasRequiredLabel: true,
|
|
243
|
+
defaultOptionLabel: '',
|
|
244
|
+
allOptionsLabel: 'Все опции',
|
|
245
|
+
isDisabled: false,
|
|
246
|
+
isRequired: false,
|
|
247
|
+
isClearable: false,
|
|
248
|
+
isLoading: false,
|
|
249
|
+
debounceTime: 400,
|
|
250
|
+
// custom options
|
|
251
|
+
shouldUseReactNodes: false,
|
|
252
|
+
valuesType: 'strings',
|
|
253
|
+
optionsMode: 'normal',
|
|
254
|
+
shouldUsePopper: false,
|
|
255
|
+
shouldRenderInBody: false,
|
|
256
|
+
shouldHideOnScroll: false,
|
|
257
|
+
shouldUseCustomIsDisabledFunction: false,
|
|
258
|
+
shouldRenderSearchInputInList: false,
|
|
259
|
+
shouldScrollToList: true,
|
|
260
|
+
canBeFlipped: false,
|
|
261
|
+
scrollParent: 'document',
|
|
262
|
+
};
|
|
@@ -57,6 +57,19 @@ export const styles = {
|
|
|
57
57
|
paddingRight: 32,
|
|
58
58
|
},
|
|
59
59
|
|
|
60
|
+
withUnits: {
|
|
61
|
+
paddingRight: 8,
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
unitsWrapper: {
|
|
65
|
+
position: 'unset',
|
|
66
|
+
padding: [0, 32, 0, 0],
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
fakeValue: {
|
|
70
|
+
display: 'none',
|
|
71
|
+
},
|
|
72
|
+
|
|
60
73
|
disabled: {
|
|
61
74
|
'& $input': {
|
|
62
75
|
cursor: 'default',
|