@true-engineering/true-react-common-ui-kit 3.5.0 → 3.7.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/README.md +28 -0
- package/dist/components/FiltersPane/FiltersPane.stories.d.ts +1 -2
- package/dist/components/FlexibleTable/FlexibleTable.d.ts +13 -21
- package/dist/components/FlexibleTable/FlexibleTable.stories.d.ts +3 -6
- package/dist/components/FlexibleTable/FlexibleTable.styles.d.ts +1 -1
- package/dist/components/FlexibleTable/components/FlexibleTableCell/FlexibleTableCell.d.ts +10 -14
- package/dist/components/FlexibleTable/components/FlexibleTableCell/FlexibleTableCell.styles.d.ts +1 -1
- package/dist/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.d.ts +19 -12
- package/dist/components/FlexibleTable/types.d.ts +8 -8
- package/dist/components/Icon/Icon.stories.d.ts +2 -2
- package/dist/components/ScrollIntoViewIfNeeded/ScrollIntoViewIfNeeded.d.ts +60 -56
- package/dist/components/Select/CustomSelect.stories.d.ts +13 -0
- package/dist/components/Select/MultiSelect.stories.d.ts +1 -2
- package/dist/components/Select/Select.d.ts +1 -1
- package/dist/components/Select/Select.stories.d.ts +1 -2
- package/dist/components/Select/components/SelectList/SelectList.d.ts +1 -1
- package/dist/true-react-common-ui-kit.js +485 -358
- package/dist/true-react-common-ui-kit.js.map +1 -1
- package/dist/true-react-common-ui-kit.umd.cjs +484 -357
- package/dist/true-react-common-ui-kit.umd.cjs.map +1 -1
- package/package.json +2 -1
- package/src/components/FiltersPane/components/Filter/Filter.tsx +1 -1
- package/src/components/FiltersPane/components/FilterValueView/FilterValueView.tsx +10 -9
- package/src/components/FlexibleTable/FlexibleTable.stories.tsx +28 -114
- package/src/components/FlexibleTable/FlexibleTable.styles.ts +1 -8
- package/src/components/FlexibleTable/FlexibleTable.tsx +89 -98
- package/src/components/FlexibleTable/components/FlexibleTableCell/FlexibleTableCell.styles.ts +6 -0
- package/src/components/FlexibleTable/components/FlexibleTableCell/FlexibleTableCell.tsx +48 -39
- package/src/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.tsx +89 -57
- package/src/components/FlexibleTable/helpers.ts +1 -3
- package/src/components/FlexibleTable/types.ts +9 -9
- package/src/components/Select/CustomSelect.stories.tsx +217 -0
- package/src/components/Select/Select.tsx +3 -2
- package/src/components/Select/components/SelectList/SelectList.tsx +2 -2
- package/src/hooks/use-dropdown.ts +2 -0
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { FC, ReactNode } from 'react';
|
|
1
|
+
import { CSSProperties, FC, ReactNode } from 'react';
|
|
2
2
|
|
|
3
|
-
export type
|
|
3
|
+
export type IFlexibleTableRenderMode = 'table' | 'divs';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export type IPosition = 'sticky' | 'absolute' | 'relative' | 'static';
|
|
5
|
+
// TODO: Заменить Record<string, any> на Record<string, unknown>
|
|
6
|
+
export type ITableRow = Record<string, any>;
|
|
8
7
|
|
|
9
8
|
export type ITitleComponent<Value> = FC<{
|
|
10
9
|
value?: Value;
|
|
@@ -28,10 +27,11 @@ export type IFlexibleTableConfigType<Values> = {
|
|
|
28
27
|
minWidth?: string | number;
|
|
29
28
|
width?: string | number;
|
|
30
29
|
maxWidth?: string | number;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
/** @default 'left' */
|
|
31
|
+
titleAlign?: CSSProperties['textAlign'];
|
|
32
|
+
cellAlign?: CSSProperties['textAlign'];
|
|
33
|
+
cellVerticalAlign?: CSSProperties['verticalAlign'];
|
|
34
|
+
position?: CSSProperties['position'];
|
|
35
35
|
right?: number;
|
|
36
36
|
left?: number;
|
|
37
37
|
};
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { createUseStyles } from 'react-jss';
|
|
3
|
+
import { isStringNotEmpty } from '@true-engineering/true-react-platform-helpers';
|
|
4
|
+
import { type ComponentMeta, type ComponentStory } from '@storybook/react';
|
|
5
|
+
import { Button, type IButtonStyles } from '../Button';
|
|
6
|
+
import { Input, type IInputStyles } from '../Input';
|
|
7
|
+
import { TextButton } from '../TextButton';
|
|
8
|
+
import { Select, type ISelectProps } from './Select';
|
|
9
|
+
|
|
10
|
+
interface ISelectWithCustomProps<T> extends ISelectProps<T> {
|
|
11
|
+
shouldUsePopper?: boolean;
|
|
12
|
+
shouldRenderInBody?: boolean;
|
|
13
|
+
shouldHideOnScroll?: boolean;
|
|
14
|
+
canBeFlipped?: boolean;
|
|
15
|
+
scrollParent?: 'document' | 'auto';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const useSelectStyles = createUseStyles({
|
|
19
|
+
option: {
|
|
20
|
+
width: '100%',
|
|
21
|
+
gap: 10,
|
|
22
|
+
display: 'grid',
|
|
23
|
+
gridTemplateColumns: 'minmax(0, 1fr) max-content',
|
|
24
|
+
alignItems: 'center',
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const useDefaultOptionStyles = createUseStyles({
|
|
29
|
+
customDefaultOption: {
|
|
30
|
+
width: '100%',
|
|
31
|
+
padding: [10, 20],
|
|
32
|
+
justifySelf: 'stretch',
|
|
33
|
+
alignSelf: 'stretch',
|
|
34
|
+
backgroundColor: '#ffffff',
|
|
35
|
+
cursor: 'default',
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
defaultView: {
|
|
39
|
+
display: 'grid',
|
|
40
|
+
placeItems: 'center',
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
inputView: {
|
|
44
|
+
gap: 10,
|
|
45
|
+
display: 'grid',
|
|
46
|
+
gridTemplateColumns: '1fr max-content',
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const inputTweakStyles: IInputStyles = {
|
|
51
|
+
inputWrapper: {
|
|
52
|
+
height: 24,
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
input: {
|
|
56
|
+
padding: [0, 4],
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const buttonTweakStyles: IButtonStyles = {
|
|
61
|
+
s: {
|
|
62
|
+
height: 24,
|
|
63
|
+
|
|
64
|
+
'&$onlyIcon': {
|
|
65
|
+
width: 24,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
interface ICustomDefaultOptionProps {
|
|
71
|
+
onAdd?: (option?: string) => void;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function CustomDefaultOption({ onAdd }: ICustomDefaultOptionProps) {
|
|
75
|
+
const classes = useDefaultOptionStyles();
|
|
76
|
+
|
|
77
|
+
const [isAdding, setIsAdding] = useState(false);
|
|
78
|
+
const [inputValue, setInputValue] = useState('');
|
|
79
|
+
|
|
80
|
+
const onAddClick = () => {
|
|
81
|
+
onAdd?.(inputValue);
|
|
82
|
+
setIsAdding(false);
|
|
83
|
+
setInputValue('');
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<div className={classes.customDefaultOption} onClick={(event) => event.stopPropagation()}>
|
|
88
|
+
{!isAdding && (
|
|
89
|
+
<div className={classes.defaultView}>
|
|
90
|
+
<TextButton icon="plus" hasCircleUnderIcon isBold onClick={() => setIsAdding(true)}>
|
|
91
|
+
Add Option
|
|
92
|
+
</TextButton>
|
|
93
|
+
</div>
|
|
94
|
+
)}
|
|
95
|
+
{isAdding && (
|
|
96
|
+
<div className={classes.inputView}>
|
|
97
|
+
<Input
|
|
98
|
+
value={inputValue}
|
|
99
|
+
border="bottom"
|
|
100
|
+
placeholder="Option Value"
|
|
101
|
+
tweakStyles={inputTweakStyles}
|
|
102
|
+
shouldFocusOnMount
|
|
103
|
+
onChange={setInputValue}
|
|
104
|
+
/>
|
|
105
|
+
<Button size="s" icon="plus" tweakStyles={buttonTweakStyles} onClick={onAddClick} />
|
|
106
|
+
</div>
|
|
107
|
+
)}
|
|
108
|
+
</div>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function SelectWithCustomProps({
|
|
113
|
+
noMatchesLabel,
|
|
114
|
+
shouldUsePopper,
|
|
115
|
+
shouldRenderInBody,
|
|
116
|
+
shouldHideOnScroll,
|
|
117
|
+
canBeFlipped,
|
|
118
|
+
scrollParent,
|
|
119
|
+
...restProps
|
|
120
|
+
}: ISelectWithCustomProps<string>) {
|
|
121
|
+
const classes = useSelectStyles();
|
|
122
|
+
const [inputValue, setInputValue] = useState<string>();
|
|
123
|
+
const [options, setOptions] = useState<string[]>([]);
|
|
124
|
+
|
|
125
|
+
const onAdd = (option?: string) => {
|
|
126
|
+
if (isStringNotEmpty(option)) {
|
|
127
|
+
setOptions((prevOptions) => [...prevOptions, option]);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const onRemove = (option: string) => {
|
|
132
|
+
setOptions((prevOptions) => prevOptions.filter((entry) => entry !== option));
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const renderOption = (value: string) => (
|
|
136
|
+
<div className={classes.option}>
|
|
137
|
+
<span>{value}</span>
|
|
138
|
+
<Button
|
|
139
|
+
size="s"
|
|
140
|
+
icon="trash-can"
|
|
141
|
+
tweakStyles={buttonTweakStyles}
|
|
142
|
+
onClick={(event) => {
|
|
143
|
+
event.stopPropagation();
|
|
144
|
+
|
|
145
|
+
onRemove(value);
|
|
146
|
+
}}
|
|
147
|
+
/>
|
|
148
|
+
</div>
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const props = {
|
|
152
|
+
...restProps,
|
|
153
|
+
onChange: setInputValue,
|
|
154
|
+
value: inputValue,
|
|
155
|
+
options,
|
|
156
|
+
convertValueToReactNode: renderOption,
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<Select
|
|
161
|
+
{...props}
|
|
162
|
+
defaultOptionLabel={<CustomDefaultOption onAdd={onAdd} />}
|
|
163
|
+
noMatchesLabel={isStringNotEmpty(noMatchesLabel) ? noMatchesLabel : undefined}
|
|
164
|
+
tweakStyles={{
|
|
165
|
+
tweakSelectList: {
|
|
166
|
+
defaultCell: {
|
|
167
|
+
padding: 0,
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
}}
|
|
171
|
+
dropdownOptions={{
|
|
172
|
+
shouldUsePopper,
|
|
173
|
+
shouldRenderInBody,
|
|
174
|
+
shouldHideOnScroll,
|
|
175
|
+
canBeFlipped,
|
|
176
|
+
scrollParent,
|
|
177
|
+
}}
|
|
178
|
+
/>
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export default {
|
|
183
|
+
title: 'Select',
|
|
184
|
+
component: SelectWithCustomProps,
|
|
185
|
+
argTypes: {
|
|
186
|
+
scrollParent: {
|
|
187
|
+
options: ['document', 'auto'],
|
|
188
|
+
control: { type: 'select' },
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
} as ComponentMeta<typeof SelectWithCustomProps>;
|
|
192
|
+
|
|
193
|
+
const Template: ComponentStory<typeof SelectWithCustomProps> = (args) => (
|
|
194
|
+
<SelectWithCustomProps {...args} />
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
export const CustomSelect = Template.bind({});
|
|
198
|
+
|
|
199
|
+
CustomSelect.args = {
|
|
200
|
+
label: 'Dropdown',
|
|
201
|
+
noMatchesLabel: 'No matches',
|
|
202
|
+
isInvalid: false,
|
|
203
|
+
errorMessage: '',
|
|
204
|
+
errorPosition: 'bottom',
|
|
205
|
+
hasFloatingLabel: true,
|
|
206
|
+
hasRequiredLabel: false,
|
|
207
|
+
isDisabled: false,
|
|
208
|
+
isRequired: false,
|
|
209
|
+
isClearable: false,
|
|
210
|
+
|
|
211
|
+
shouldUsePopper: false,
|
|
212
|
+
shouldRenderInBody: false,
|
|
213
|
+
shouldHideOnScroll: false,
|
|
214
|
+
shouldScrollToList: true,
|
|
215
|
+
canBeFlipped: false,
|
|
216
|
+
scrollParent: 'document',
|
|
217
|
+
};
|
|
@@ -17,6 +17,7 @@ import { debounce } from 'ts-debounce';
|
|
|
17
17
|
import {
|
|
18
18
|
getTestId,
|
|
19
19
|
isNotEmpty,
|
|
20
|
+
isReactNodeNotEmpty,
|
|
20
21
|
isStringNotEmpty,
|
|
21
22
|
createFilter,
|
|
22
23
|
} from '@true-engineering/true-react-platform-helpers';
|
|
@@ -41,7 +42,7 @@ import { useStyles, ISelectStyles, searchInputStyles, getInputStyles } from './S
|
|
|
41
42
|
export interface ISelectProps<Value>
|
|
42
43
|
extends Omit<IInputProps, 'value' | 'onChange' | 'onBlur' | 'type' | 'tweakStyles'>,
|
|
43
44
|
ICommonProps<ISelectStyles> {
|
|
44
|
-
defaultOptionLabel?:
|
|
45
|
+
defaultOptionLabel?: ReactNode;
|
|
45
46
|
allOptionsLabel?: string;
|
|
46
47
|
noMatchesLabel?: string;
|
|
47
48
|
loadingLabel?: ReactNode;
|
|
@@ -150,7 +151,7 @@ export function Select<Value>(
|
|
|
150
151
|
const isMounted = useIsMounted();
|
|
151
152
|
const [isListOpen, setIsListOpen] = useState(false);
|
|
152
153
|
const [areOptionsLoading, setAreOptionsLoading] = useState(false);
|
|
153
|
-
const hasDefaultOption =
|
|
154
|
+
const hasDefaultOption = isReactNodeNotEmpty(defaultOptionLabel);
|
|
154
155
|
|
|
155
156
|
const [focusedListCellIndex, setFocusedListCellIndex] = useState(DEFAULT_OPTION_INDEX);
|
|
156
157
|
const [searchValue, setSearchValue] = useState('');
|
|
@@ -20,7 +20,7 @@ export interface ISelectListProps<Value> extends ICommonProps<ISelectListStyles>
|
|
|
20
20
|
noMatchesLabel?: string;
|
|
21
21
|
isLoading?: boolean;
|
|
22
22
|
loadingLabel?: ReactNode;
|
|
23
|
-
defaultOptionLabel?:
|
|
23
|
+
defaultOptionLabel?: ReactNode;
|
|
24
24
|
allOptionsLabel?: string;
|
|
25
25
|
areAllOptionsSelected?: boolean;
|
|
26
26
|
shouldScrollToList?: boolean;
|
|
@@ -97,7 +97,7 @@ export function SelectList<Value>({
|
|
|
97
97
|
<div className={clsx(classes.cell, classes.loading)}>{loadingLabel}</div>
|
|
98
98
|
) : (
|
|
99
99
|
<>
|
|
100
|
-
{
|
|
100
|
+
{isReactNodeNotEmpty(defaultOptionLabel) && (
|
|
101
101
|
<ScrollIntoViewIfNeeded
|
|
102
102
|
active={focusedIndex === DEFAULT_OPTION_INDEX}
|
|
103
103
|
options={{ block: 'nearest' }}
|
|
@@ -49,6 +49,8 @@ export const useDropdown = ({
|
|
|
49
49
|
|
|
50
50
|
let popperData: ReturnType<typeof usePopper> | undefined;
|
|
51
51
|
if (shouldUsePopper) {
|
|
52
|
+
// TODO: Вытащить хук из под условия???
|
|
53
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
52
54
|
popperData = usePopper(referenceElement, dropdownElement, {
|
|
53
55
|
enabled: isOpen,
|
|
54
56
|
placement,
|