@true-engineering/true-react-common-ui-kit 3.0.0-alpha.2 → 3.0.0-alpha.20
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/Checkbox/Checkbox.d.ts +1 -1
- package/dist/components/FiltersPane/components/FilterSelect/FilterSelect.d.ts +1 -5
- package/dist/components/FiltersPane/components/FiltersPaneSearch/FiltersPaneSearch.d.ts +1 -3
- package/dist/components/FlexibleTable/FlexibleTable.d.ts +6 -2
- package/dist/components/FlexibleTable/FlexibleTable.styles.d.ts +1 -1
- package/dist/components/FlexibleTable/components/FlexibleTableCell/FlexibleTableCell.d.ts +1 -1
- package/dist/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.d.ts +1 -1
- package/dist/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.styles.d.ts +1 -1
- package/dist/components/FlexibleTable/helpers.d.ts +3 -0
- package/dist/components/FlexibleTable/types.d.ts +1 -1
- package/dist/components/MultiSelectList/MultiSelectList.d.ts +1 -2
- package/dist/components/Skeleton/Skeleton.d.ts +7 -0
- package/dist/components/Skeleton/Skeleton.styles.d.ts +3 -0
- package/dist/components/Skeleton/index.d.ts +2 -0
- package/dist/components/Switch/Switch.d.ts +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/helpers/phone.d.ts +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/use-merged-refs.d.ts +2 -0
- package/dist/theme/types.d.ts +2 -1
- package/dist/true-react-common-ui-kit.js +1100 -757
- package/dist/true-react-common-ui-kit.js.map +1 -1
- package/dist/true-react-common-ui-kit.umd.cjs +1099 -756
- package/dist/true-react-common-ui-kit.umd.cjs.map +1 -1
- package/package.json +1 -1
- package/src/components/Checkbox/Checkbox.tsx +1 -1
- package/src/components/DateInput/DateInput.tsx +1 -9
- package/src/components/DatePicker/DatePicker.stories.tsx +1 -0
- package/src/components/DatePicker/DatePicker.tsx +1 -3
- package/src/components/FiltersPane/FiltersPane.stories.tsx +0 -8
- package/src/components/FiltersPane/components/FilterInterval/FilterInterval.styles.ts +2 -1
- package/src/components/FiltersPane/components/FilterSelect/FilterSelect.styles.ts +3 -1
- package/src/components/FiltersPane/components/FilterSelect/FilterSelect.tsx +1 -6
- package/src/components/FiltersPane/components/FilterWithDates/FilterWithDates.styles.ts +2 -1
- package/src/components/FiltersPane/components/FiltersPaneSearch/FiltersPaneSearch.styles.ts +1 -0
- package/src/components/FiltersPane/components/FiltersPaneSearch/FiltersPaneSearch.tsx +1 -4
- package/src/components/FiltersPane/types.ts +1 -1
- package/src/components/FlexibleTable/FlexibleTable.stories.tsx +15 -12
- package/src/components/FlexibleTable/FlexibleTable.styles.ts +9 -10
- package/src/components/FlexibleTable/FlexibleTable.tsx +124 -60
- package/src/components/FlexibleTable/components/FlexibleTableCell/FlexibleTableCell.styles.ts +4 -0
- package/src/components/FlexibleTable/components/FlexibleTableCell/FlexibleTableCell.tsx +25 -27
- package/src/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.styles.ts +4 -0
- package/src/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.tsx +12 -11
- package/src/components/FlexibleTable/helpers.ts +15 -0
- package/src/components/FlexibleTable/types.ts +1 -1
- package/src/components/MultiSelectList/MultiSelectList.styles.ts +4 -0
- package/src/components/MultiSelectList/MultiSelectList.tsx +1 -3
- package/src/components/Skeleton/Skeleton.stories.tsx +19 -0
- package/src/components/Skeleton/Skeleton.styles.ts +46 -0
- package/src/components/Skeleton/Skeleton.tsx +12 -0
- package/src/components/Skeleton/index.ts +2 -0
- package/src/components/Switch/Switch.tsx +2 -2
- package/src/components/index.ts +1 -0
- package/src/helpers/phone.ts +1 -1
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-merged-refs.ts +4 -0
- package/src/theme/types.ts +2 -0
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@ import { useStyles, ICheckboxStyles } from './Checkbox.styles';
|
|
|
12
12
|
|
|
13
13
|
export interface ICheckboxProps<V> extends ICommonProps<ICheckboxStyles> {
|
|
14
14
|
children?: ReactNode;
|
|
15
|
-
isChecked
|
|
15
|
+
isChecked: boolean | undefined;
|
|
16
16
|
isSemiChecked?: boolean;
|
|
17
17
|
isDisabled?: boolean;
|
|
18
18
|
isReadonly?: boolean;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { FormEvent, MouseEvent, forwardRef, ChangeEvent } from 'react';
|
|
2
2
|
import clsx from 'clsx';
|
|
3
|
-
import { addDataTestId, getTestId } from '@true-engineering/true-react-platform-helpers';
|
|
4
3
|
import { addDataAttributes } from '../../helpers';
|
|
5
4
|
import { useTweakStyles } from '../../hooks';
|
|
6
5
|
import { ICommonProps } from '../../types';
|
|
@@ -33,7 +32,6 @@ export const DateInput = forwardRef<HTMLInputElement, IDateInputProps>(
|
|
|
33
32
|
data,
|
|
34
33
|
isRange,
|
|
35
34
|
tweakStyles,
|
|
36
|
-
testId,
|
|
37
35
|
onClick,
|
|
38
36
|
onChange,
|
|
39
37
|
...inputProps
|
|
@@ -70,12 +68,7 @@ export const DateInput = forwardRef<HTMLInputElement, IDateInputProps>(
|
|
|
70
68
|
};
|
|
71
69
|
|
|
72
70
|
return (
|
|
73
|
-
<div
|
|
74
|
-
className={clsx(classes.root, className)}
|
|
75
|
-
onClick={onClick}
|
|
76
|
-
{...addDataTestId(testId)}
|
|
77
|
-
{...addDataAttributes(data)}
|
|
78
|
-
>
|
|
71
|
+
<div className={clsx(classes.root, className)} onClick={onClick} {...addDataAttributes(data)}>
|
|
79
72
|
<Input
|
|
80
73
|
{...inputProps}
|
|
81
74
|
ref={ref}
|
|
@@ -84,7 +77,6 @@ export const DateInput = forwardRef<HTMLInputElement, IDateInputProps>(
|
|
|
84
77
|
placeholder={
|
|
85
78
|
placeholder ?? (isRange ? EMPTY_DATE_RANGE_INPUT_VALUE : EMPTY_DATE_INPUT_VALUE)
|
|
86
79
|
}
|
|
87
|
-
testId={getTestId(testId, 'input')}
|
|
88
80
|
tweakStyles={tweakInputStyles}
|
|
89
81
|
onChange={handleChange}
|
|
90
82
|
beforeMaskedStateChange={beforeMaskedStateChange}
|
|
@@ -4,7 +4,6 @@ import 'react-datepicker/dist/react-datepicker.css';
|
|
|
4
4
|
import clsx from 'clsx';
|
|
5
5
|
import { isAfter, isBefore, isValid } from 'date-fns';
|
|
6
6
|
import {
|
|
7
|
-
addDataTestId,
|
|
8
7
|
isEmpty,
|
|
9
8
|
isNotEmpty,
|
|
10
9
|
isStringNotEmpty,
|
|
@@ -40,7 +39,6 @@ export const DatePicker = forwardRef<ReactDatePicker, IDatePickerProps>(
|
|
|
40
39
|
(
|
|
41
40
|
{
|
|
42
41
|
data,
|
|
43
|
-
testId,
|
|
44
42
|
selectedDate = null,
|
|
45
43
|
minDate,
|
|
46
44
|
maxDate,
|
|
@@ -234,7 +232,7 @@ export const DatePicker = forwardRef<ReactDatePicker, IDatePickerProps>(
|
|
|
234
232
|
}, [selectedDate, startDate, endDate]);
|
|
235
233
|
|
|
236
234
|
return (
|
|
237
|
-
<div className={classes.root} {...
|
|
235
|
+
<div className={classes.root} {...addDataAttributes(data)}>
|
|
238
236
|
<DatePickerComponent
|
|
239
237
|
ref={ref}
|
|
240
238
|
minDate={minDate}
|
|
@@ -72,7 +72,6 @@ type ConfigValues = {
|
|
|
72
72
|
|
|
73
73
|
interface IFiltersPaneWithCustomProps<Values, Content> extends IFiltersPaneProps<Values, Content> {
|
|
74
74
|
containerWidth: number;
|
|
75
|
-
multiselectWidth: number;
|
|
76
75
|
isSearchDisabled: boolean;
|
|
77
76
|
isSearchAutosizeable: boolean;
|
|
78
77
|
shouldShowSettingsButton: boolean;
|
|
@@ -83,7 +82,6 @@ interface IFiltersPaneWithCustomProps<Values, Content> extends IFiltersPaneProps
|
|
|
83
82
|
|
|
84
83
|
function FiltersPaneWithCustomProps<Values, Content>({
|
|
85
84
|
containerWidth,
|
|
86
|
-
multiselectWidth,
|
|
87
85
|
isSearchDisabled,
|
|
88
86
|
isSearchAutosizeable,
|
|
89
87
|
shouldShowSettingsButton,
|
|
@@ -141,7 +139,6 @@ function FiltersPaneWithCustomProps<Values, Content>({
|
|
|
141
139
|
multiSelect: {
|
|
142
140
|
name: 'multiSelect',
|
|
143
141
|
type: 'multiSelect',
|
|
144
|
-
width: multiselectWidth,
|
|
145
142
|
pageSize: 15,
|
|
146
143
|
checkboxPosition,
|
|
147
144
|
isGroupingEnabled,
|
|
@@ -163,7 +160,6 @@ function FiltersPaneWithCustomProps<Values, Content>({
|
|
|
163
160
|
isGroupingEnabled,
|
|
164
161
|
checkboxPosition,
|
|
165
162
|
searchMaxLength: 12,
|
|
166
|
-
width: multiselectWidth,
|
|
167
163
|
fetchOptions: (q?: string): Promise<Array<{ v: string }>> => {
|
|
168
164
|
if (q === '' || q === 'undefined' || q === '123') {
|
|
169
165
|
return new Promise((resolve) => setTimeout(() => resolve([])));
|
|
@@ -265,9 +261,6 @@ export default {
|
|
|
265
261
|
containerWidth: {
|
|
266
262
|
control: { type: 'range', min: 100, max: 1000, step: 100 },
|
|
267
263
|
},
|
|
268
|
-
multiselectWidth: {
|
|
269
|
-
control: { type: 'range', min: 100, max: 400, step: 50 },
|
|
270
|
-
},
|
|
271
264
|
checkboxPosition: {
|
|
272
265
|
control: 'inline-radio',
|
|
273
266
|
options: ['left', 'right'],
|
|
@@ -288,7 +281,6 @@ Default.args = {
|
|
|
288
281
|
hasClearButton: true,
|
|
289
282
|
isDisabled: false,
|
|
290
283
|
containerWidth: 400,
|
|
291
|
-
multiselectWidth: 200,
|
|
292
284
|
withFieldNameInLabel: true,
|
|
293
285
|
isGroupingEnabled: true,
|
|
294
286
|
checkboxPosition: 'left',
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import cloneDeep from 'lodash-es/cloneDeep';
|
|
1
2
|
import { colors, createThemedStyles, ITweakStyles } from '../../../../theme';
|
|
2
3
|
import { IButtonStyles } from '../../../Button';
|
|
3
4
|
import { IInputStyles } from '../../../Input';
|
|
@@ -49,7 +50,7 @@ export const inputStyles: IInputStyles = {
|
|
|
49
50
|
},
|
|
50
51
|
};
|
|
51
52
|
|
|
52
|
-
export const clearButtonStyles = innerTextButtonStyles;
|
|
53
|
+
export const clearButtonStyles = cloneDeep(innerTextButtonStyles);
|
|
53
54
|
|
|
54
55
|
export type IFilterIntervalStyles = ITweakStyles<
|
|
55
56
|
typeof useStyles,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import cloneDeep from 'lodash-es/cloneDeep';
|
|
1
2
|
import { colors, createThemedStyles, helpers, ITweakStyles } from '../../../../theme';
|
|
2
3
|
import { IButtonStyles } from '../../../Button';
|
|
3
4
|
import { ISearchInputStyles } from '../../../SearchInput';
|
|
@@ -8,6 +9,7 @@ const LIST_GAP = 12;
|
|
|
8
9
|
|
|
9
10
|
export const useStyles = createThemedStyles('FilterSelect', {
|
|
10
11
|
root: {
|
|
12
|
+
width: 220,
|
|
11
13
|
background: colors.CLASSIC_WHITE,
|
|
12
14
|
},
|
|
13
15
|
|
|
@@ -127,7 +129,7 @@ export const searchInputStyles: ISearchInputStyles = {
|
|
|
127
129
|
},
|
|
128
130
|
};
|
|
129
131
|
|
|
130
|
-
export const clearButtonStyles = innerTextButtonStyles;
|
|
132
|
+
export const clearButtonStyles = cloneDeep(innerTextButtonStyles);
|
|
131
133
|
|
|
132
134
|
export type IFilterSelectStyles = ITweakStyles<
|
|
133
135
|
typeof useStyles,
|
|
@@ -30,10 +30,6 @@ export interface IFilterSelectProps<Value> extends ICommonProps<IFilterSelectSty
|
|
|
30
30
|
* @default false
|
|
31
31
|
*/
|
|
32
32
|
isGroupingEnabled?: boolean;
|
|
33
|
-
/**
|
|
34
|
-
* @default `220px`
|
|
35
|
-
*/
|
|
36
|
-
width?: string | number;
|
|
37
33
|
localeKey?: IFilterLocaleKey;
|
|
38
34
|
locale?: Partial<ISelectLocale>;
|
|
39
35
|
options?: Value[];
|
|
@@ -54,7 +50,6 @@ export function FilterSelect<Value>({
|
|
|
54
50
|
value,
|
|
55
51
|
isSearchEnabled = false,
|
|
56
52
|
isGroupingEnabled = false,
|
|
57
|
-
width = 220,
|
|
58
53
|
localeKey,
|
|
59
54
|
locale,
|
|
60
55
|
onChange,
|
|
@@ -225,7 +220,7 @@ export function FilterSelect<Value>({
|
|
|
225
220
|
}, []);
|
|
226
221
|
|
|
227
222
|
return (
|
|
228
|
-
<div className={classes.root}
|
|
223
|
+
<div className={classes.root} {...addDataAttributes(data)}>
|
|
229
224
|
{isSearchEnabled && (
|
|
230
225
|
<div className={classes.dropdownInput}>
|
|
231
226
|
<SearchInput
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import cloneDeep from 'lodash-es/cloneDeep';
|
|
1
2
|
import { colors, createThemedStyles, ITweakStyles } from '../../../../theme';
|
|
2
3
|
import { IButtonStyles } from '../../../Button';
|
|
3
4
|
import { IDatePickerStyles } from '../../../DatePicker';
|
|
@@ -31,7 +32,7 @@ export const useStyles = createThemedStyles('FilterWithDates', {
|
|
|
31
32
|
datepicker: {},
|
|
32
33
|
});
|
|
33
34
|
|
|
34
|
-
export const clearButtonStyles = innerTextButtonStyles;
|
|
35
|
+
export const clearButtonStyles = cloneDeep(innerTextButtonStyles);
|
|
35
36
|
|
|
36
37
|
export const backButtonStyles = innerTextButtonStyles;
|
|
37
38
|
|
|
@@ -7,7 +7,7 @@ import { Icon } from '../../../Icon';
|
|
|
7
7
|
import { SearchInput } from '../../../SearchInput';
|
|
8
8
|
import { getLocale } from '../../helpers';
|
|
9
9
|
import { IFilterLocaleKey, IFiltersPaneSearchPayload, IPartialFilterLocale } from '../../types';
|
|
10
|
-
import { FilterSelect
|
|
10
|
+
import { FilterSelect } from '../FilterSelect';
|
|
11
11
|
import {
|
|
12
12
|
useStyles,
|
|
13
13
|
IFiltersPaneSearchStyles,
|
|
@@ -26,7 +26,6 @@ export interface IFiltersPaneSearchProps<Value> extends ICommonProps<IFiltersPan
|
|
|
26
26
|
getValueView?: (value: Value) => ReactNode;
|
|
27
27
|
getValueId?: (value: Value) => string;
|
|
28
28
|
getValueString?: (value: Value) => string;
|
|
29
|
-
selectWidth?: IFilterSelectProps<Value>['width'];
|
|
30
29
|
hasClearSelectButton?: boolean;
|
|
31
30
|
isDisabled?: boolean;
|
|
32
31
|
isSelectSearchEnabled?: boolean;
|
|
@@ -43,7 +42,6 @@ export function FiltersPaneSearch<Value>({
|
|
|
43
42
|
getValueId,
|
|
44
43
|
getValueView,
|
|
45
44
|
getValueString,
|
|
46
|
-
selectWidth = '100%',
|
|
47
45
|
hasClearSelectButton,
|
|
48
46
|
isDisabled = false,
|
|
49
47
|
isSelectSearchEnabled = true,
|
|
@@ -155,7 +153,6 @@ export function FiltersPaneSearch<Value>({
|
|
|
155
153
|
localeKey={localeKey}
|
|
156
154
|
locale={translates}
|
|
157
155
|
onChange={handleFieldsChange}
|
|
158
|
-
width={selectWidth}
|
|
159
156
|
isSearchEnabled={isSelectSearchEnabled}
|
|
160
157
|
hasClearButton={hasClearSelectButton}
|
|
161
158
|
testId={testId !== undefined ? `${testId}-dropdown` : undefined}
|
|
@@ -152,5 +152,5 @@ export type ISelectLocaleKey = keyof typeof SelectLocales;
|
|
|
152
152
|
|
|
153
153
|
export type IFilterWithDateDatePickerProps = Omit<
|
|
154
154
|
IDatePickerProps,
|
|
155
|
-
'onChange' | 'value' | 'locale' | 'months' | 'selectedDate' | 'tweakStyles'
|
|
155
|
+
'onChange' | 'value' | 'locale' | 'months' | 'selectedDate' | 'tweakStyles'
|
|
156
156
|
>;
|
|
@@ -315,18 +315,20 @@ const tweak: IFlexibleTableStyles = {
|
|
|
315
315
|
};
|
|
316
316
|
|
|
317
317
|
const Template: ComponentStory<typeof FlexibleTable<ITableContent>> = (args) => (
|
|
318
|
-
<
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
item
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
318
|
+
<div style={{ width: 500, overflow: 'auto' }}>
|
|
319
|
+
<FlexibleTable<ITableContent>
|
|
320
|
+
{...args}
|
|
321
|
+
uniqueField="contractCode"
|
|
322
|
+
expandableRowComponent={(item, _, close) =>
|
|
323
|
+
item.contractCode === 'OB_UT_M119' ? (
|
|
324
|
+
<div onClick={close}>всем привет :) {item.contractCode}</div>
|
|
325
|
+
) : null
|
|
326
|
+
}
|
|
327
|
+
tweakStyles={tweak}
|
|
328
|
+
content={content}
|
|
329
|
+
config={config}
|
|
330
|
+
/>
|
|
331
|
+
</div>
|
|
330
332
|
);
|
|
331
333
|
|
|
332
334
|
export const Default = Template.bind({});
|
|
@@ -334,6 +336,7 @@ export const Default = Template.bind({});
|
|
|
334
336
|
Default.args = {
|
|
335
337
|
isHorizontallyScrollable: true,
|
|
336
338
|
isFirstColumnSticky: true,
|
|
339
|
+
isLoading: false,
|
|
337
340
|
};
|
|
338
341
|
|
|
339
342
|
Default.parameters = {
|
|
@@ -22,16 +22,6 @@ export const useStyles = createThemedStyles('FlexibleTable', {
|
|
|
22
22
|
maxHeight: '100%',
|
|
23
23
|
},
|
|
24
24
|
|
|
25
|
-
horizontallyScrolled: {
|
|
26
|
-
'& $cellSticky': {
|
|
27
|
-
boxShadow: '4px 0 4px rgba(0, 0, 0, 0.05)',
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
'& $headerSticky::before': {
|
|
31
|
-
boxShadow: '4px 0 4px rgba(0, 0, 0, 0.05)',
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
|
|
35
25
|
loader: {
|
|
36
26
|
position: 'sticky',
|
|
37
27
|
left: 0,
|
|
@@ -75,6 +65,10 @@ export const useStyles = createThemedStyles('FlexibleTable', {
|
|
|
75
65
|
pointerEvents: 'none',
|
|
76
66
|
zIndex: 1,
|
|
77
67
|
transition: ['box-shadow', '0.25s', 'ease-in-out'],
|
|
68
|
+
|
|
69
|
+
'[data-scrolled] &': {
|
|
70
|
+
boxShadow: '4px 0 4px rgba(0, 0, 0, 0.05)',
|
|
71
|
+
},
|
|
78
72
|
},
|
|
79
73
|
|
|
80
74
|
'&::after': {
|
|
@@ -96,6 +90,11 @@ export const useStyles = createThemedStyles('FlexibleTable', {
|
|
|
96
90
|
headerSecond: {
|
|
97
91
|
paddingLeft: STICKY_SHADOW_PADDING,
|
|
98
92
|
},
|
|
93
|
+
|
|
94
|
+
skeleton: {
|
|
95
|
+
height: 21,
|
|
96
|
+
padding: [14, 7],
|
|
97
|
+
},
|
|
99
98
|
});
|
|
100
99
|
|
|
101
100
|
export type IFlexibleTableStyles = ITweakStyles<
|
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
import { ReactNode, RefObject, useCallback, useEffect,
|
|
1
|
+
import { ReactNode, RefObject, useCallback, useEffect, useMemo, useRef } from 'react';
|
|
2
2
|
import clsx from 'clsx';
|
|
3
|
+
import {
|
|
4
|
+
addDataTestId,
|
|
5
|
+
indexMap,
|
|
6
|
+
isArrayNotEmpty,
|
|
7
|
+
isEmpty,
|
|
8
|
+
isNotEmpty,
|
|
9
|
+
} from '@true-engineering/true-react-platform-helpers';
|
|
3
10
|
import { addDataAttributes } from '../../helpers';
|
|
4
|
-
import { useTweakStyles } from '../../hooks';
|
|
11
|
+
import { useMergedRefs, useTweakStyles } from '../../hooks';
|
|
5
12
|
import { ICommonProps } from '../../types';
|
|
13
|
+
import { Skeleton } from '../Skeleton';
|
|
6
14
|
import { ThemedPreloader } from '../ThemedPreloader';
|
|
7
15
|
import { FlexibleTableRow } from './components';
|
|
16
|
+
import { hasHorizontalScrollBar } from './helpers';
|
|
8
17
|
import { IFlexibleTableConfigType, IInfinityScrollConfig, ITitleComponent } from './types';
|
|
9
18
|
import { useStyles, IFlexibleTableStyles } from './FlexibleTable.styles';
|
|
10
19
|
|
|
@@ -15,12 +24,16 @@ export interface IFlexibleTableProps<Values extends Record<string, any>>
|
|
|
15
24
|
headerContent?: Partial<Record<keyof Values, any>>;
|
|
16
25
|
enabledColumns?: Array<keyof Values>;
|
|
17
26
|
activeRows?: number[];
|
|
18
|
-
config
|
|
27
|
+
config: IFlexibleTableConfigType<Values>;
|
|
19
28
|
isHorizontallyScrollable?: boolean;
|
|
20
29
|
isFirstColumnSticky?: boolean;
|
|
21
30
|
infinityScrollConfig?: IInfinityScrollConfig;
|
|
31
|
+
/**
|
|
32
|
+
* @default Индекс строки
|
|
33
|
+
*/
|
|
22
34
|
uniqueField?: keyof Values;
|
|
23
35
|
onHeadClick?: (column: keyof Values) => void;
|
|
36
|
+
isLoading?: boolean;
|
|
24
37
|
// TODO: Заменить string на Generic Values[uniqueField]
|
|
25
38
|
onRowClick?: (id: string) => void;
|
|
26
39
|
onRowHover?: (id?: string) => void;
|
|
@@ -42,6 +55,7 @@ export function FlexibleTable<Values extends Record<string, any>>({
|
|
|
42
55
|
isFirstColumnSticky,
|
|
43
56
|
infinityScrollConfig,
|
|
44
57
|
uniqueField,
|
|
58
|
+
isLoading,
|
|
45
59
|
onHeadClick,
|
|
46
60
|
onRowHover,
|
|
47
61
|
onRowClick,
|
|
@@ -59,11 +73,44 @@ export function FlexibleTable<Values extends Record<string, any>>({
|
|
|
59
73
|
currentComponentName: 'FlexibleTable',
|
|
60
74
|
});
|
|
61
75
|
|
|
62
|
-
const [isHorizontallyScrolled, setIsHorizontallyScrolled] = useState(false);
|
|
63
|
-
|
|
64
76
|
const observer = useRef<IntersectionObserver>();
|
|
65
77
|
const scrollRef = useRef<HTMLDivElement>(null);
|
|
66
78
|
|
|
79
|
+
const showedColumns = useMemo(
|
|
80
|
+
() => enabledColumns ?? Object.keys(config),
|
|
81
|
+
[enabledColumns, config],
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const getDataScrollAttributeSetter = useCallback(
|
|
85
|
+
(key: string, setter: (el: HTMLDivElement) => boolean) => (el?: HTMLDivElement) => {
|
|
86
|
+
if (isHorizontallyScrollable && isNotEmpty(el) && setter(el)) {
|
|
87
|
+
el.dataset[key] = 'true';
|
|
88
|
+
} else {
|
|
89
|
+
el?.removeAttribute(`data-${key}`);
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
[isHorizontallyScrollable],
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// Когда таблица имеет скроллбар - добавляем аттрибут scrollable
|
|
96
|
+
const setHasScrollBarAttribute = useCallback(
|
|
97
|
+
getDataScrollAttributeSetter('scrollable', hasHorizontalScrollBar),
|
|
98
|
+
[getDataScrollAttributeSetter],
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// Когда таблица проскроллена - добавляем аттрибут scrolled
|
|
102
|
+
const setIsScrolledAttribute = useCallback(
|
|
103
|
+
getDataScrollAttributeSetter('scrolled', (el) => el.scrollLeft > 0),
|
|
104
|
+
[getDataScrollAttributeSetter],
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const ref = useMergedRefs([
|
|
108
|
+
refForScroll,
|
|
109
|
+
scrollRef,
|
|
110
|
+
setHasScrollBarAttribute,
|
|
111
|
+
setIsScrolledAttribute,
|
|
112
|
+
]);
|
|
113
|
+
|
|
67
114
|
const initIntersectionObserver = useCallback(
|
|
68
115
|
(node: HTMLDivElement) => {
|
|
69
116
|
if (infinityScrollConfig) {
|
|
@@ -97,29 +144,34 @@ export function FlexibleTable<Values extends Record<string, any>>({
|
|
|
97
144
|
);
|
|
98
145
|
|
|
99
146
|
useEffect(() => {
|
|
100
|
-
const scrollContainer =
|
|
101
|
-
if (scrollContainer
|
|
147
|
+
const scrollContainer = scrollRef.current;
|
|
148
|
+
if (isEmpty(scrollContainer) || !isHorizontallyScrollable) {
|
|
102
149
|
return;
|
|
103
150
|
}
|
|
104
|
-
|
|
105
|
-
|
|
151
|
+
|
|
152
|
+
const scrollHandler = () => {
|
|
153
|
+
setIsScrolledAttribute(scrollContainer);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const resizeHandler = () => {
|
|
157
|
+
setHasScrollBarAttribute(scrollContainer);
|
|
106
158
|
};
|
|
159
|
+
|
|
107
160
|
scrollContainer.addEventListener('scroll', scrollHandler);
|
|
108
|
-
|
|
109
|
-
|
|
161
|
+
window.addEventListener('resize', resizeHandler);
|
|
162
|
+
|
|
163
|
+
return () => {
|
|
164
|
+
scrollContainer.removeEventListener('scroll', scrollHandler);
|
|
165
|
+
window.removeEventListener('resize', resizeHandler);
|
|
166
|
+
};
|
|
167
|
+
}, [scrollRef, setIsScrolledAttribute, setHasScrollBarAttribute]);
|
|
110
168
|
|
|
111
169
|
return (
|
|
112
|
-
<div
|
|
113
|
-
|
|
114
|
-
className={clsx(
|
|
115
|
-
isHorizontallyScrollable && classes.scroll,
|
|
116
|
-
isHorizontallyScrolled && classes.horizontallyScrolled,
|
|
117
|
-
)}
|
|
118
|
-
>
|
|
119
|
-
<table className={classes.root} data-testid={testId} {...addDataAttributes(data)}>
|
|
170
|
+
<div ref={ref} className={clsx({ [classes.scroll]: isHorizontallyScrollable })}>
|
|
171
|
+
<table className={classes.root} {...addDataTestId(testId)} {...addDataAttributes(data)}>
|
|
120
172
|
<thead>
|
|
121
173
|
<tr className={classes.headerRow}>
|
|
122
|
-
{
|
|
174
|
+
{showedColumns.map((key, i) => {
|
|
123
175
|
const itemConfig = config?.[key];
|
|
124
176
|
|
|
125
177
|
let titleContent = itemConfig?.title ?? '';
|
|
@@ -131,11 +183,10 @@ export function FlexibleTable<Values extends Record<string, any>>({
|
|
|
131
183
|
|
|
132
184
|
return (
|
|
133
185
|
<th
|
|
134
|
-
className={clsx(
|
|
135
|
-
classes.
|
|
136
|
-
isFirstColumnSticky &&
|
|
137
|
-
|
|
138
|
-
)}
|
|
186
|
+
className={clsx(classes.header, {
|
|
187
|
+
[classes.headerSticky]: isFirstColumnSticky && i === 0,
|
|
188
|
+
[classes.headerSecond]: isFirstColumnSticky && i === 1,
|
|
189
|
+
})}
|
|
139
190
|
style={{
|
|
140
191
|
minWidth: itemConfig?.minWidth,
|
|
141
192
|
width: itemConfig?.width,
|
|
@@ -152,42 +203,55 @@ export function FlexibleTable<Values extends Record<string, any>>({
|
|
|
152
203
|
</tr>
|
|
153
204
|
</thead>
|
|
154
205
|
<tbody>
|
|
155
|
-
{
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
206
|
+
{isLoading ? (
|
|
207
|
+
indexMap(6, (i) => (
|
|
208
|
+
<tr key={i}>
|
|
209
|
+
{showedColumns.map((_, j) => (
|
|
210
|
+
<td key={j} className={classes.skeleton}>
|
|
211
|
+
<Skeleton />
|
|
212
|
+
</td>
|
|
213
|
+
))}
|
|
163
214
|
</tr>
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
215
|
+
))
|
|
216
|
+
) : (
|
|
217
|
+
<>
|
|
218
|
+
{!isArrayNotEmpty(content) &&
|
|
219
|
+
nothingFoundContent !== undefined &&
|
|
220
|
+
!infinityScrollConfig?.isLoading &&
|
|
221
|
+
(infinityScrollConfig?.isLastPage === undefined ||
|
|
222
|
+
infinityScrollConfig.isLastPage) && (
|
|
223
|
+
<tr>
|
|
224
|
+
<td colSpan={showedColumns.length}>{nothingFoundContent}</td>
|
|
225
|
+
</tr>
|
|
226
|
+
)}
|
|
227
|
+
|
|
228
|
+
{content.map((item, i) => (
|
|
229
|
+
<FlexibleTableRow
|
|
230
|
+
item={item}
|
|
231
|
+
uniqueField={uniqueField}
|
|
232
|
+
isActive={activeRows?.includes(i) ?? false}
|
|
233
|
+
isFirstColumnSticky={isFirstColumnSticky}
|
|
234
|
+
onRowClick={onRowClick}
|
|
235
|
+
onRowHover={onRowHover}
|
|
236
|
+
enabledColumns={enabledColumns}
|
|
237
|
+
config={config}
|
|
238
|
+
key={isNotEmpty(uniqueField) ? item[uniqueField] : i}
|
|
239
|
+
rowAttributes={rowAttributes}
|
|
240
|
+
tweakStyles={tweakTableRowStyles}
|
|
241
|
+
expandableRowComponent={expandableRowComponent}
|
|
242
|
+
/>
|
|
243
|
+
))}
|
|
244
|
+
|
|
245
|
+
{infinityScrollConfig !== undefined && !infinityScrollConfig.isLastPage && (
|
|
246
|
+
<tr>
|
|
247
|
+
<td colSpan={showedColumns.length}>
|
|
248
|
+
<div ref={initIntersectionObserver} className={classes.loader}>
|
|
249
|
+
<ThemedPreloader type="dots" />
|
|
250
|
+
</div>
|
|
251
|
+
</td>
|
|
252
|
+
</tr>
|
|
253
|
+
)}
|
|
254
|
+
</>
|
|
191
255
|
)}
|
|
192
256
|
</tbody>
|
|
193
257
|
</table>
|