@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.
Files changed (58) hide show
  1. package/dist/components/Checkbox/Checkbox.d.ts +1 -1
  2. package/dist/components/FiltersPane/components/FilterSelect/FilterSelect.d.ts +1 -5
  3. package/dist/components/FiltersPane/components/FiltersPaneSearch/FiltersPaneSearch.d.ts +1 -3
  4. package/dist/components/FlexibleTable/FlexibleTable.d.ts +6 -2
  5. package/dist/components/FlexibleTable/FlexibleTable.styles.d.ts +1 -1
  6. package/dist/components/FlexibleTable/components/FlexibleTableCell/FlexibleTableCell.d.ts +1 -1
  7. package/dist/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.d.ts +1 -1
  8. package/dist/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.styles.d.ts +1 -1
  9. package/dist/components/FlexibleTable/helpers.d.ts +3 -0
  10. package/dist/components/FlexibleTable/types.d.ts +1 -1
  11. package/dist/components/MultiSelectList/MultiSelectList.d.ts +1 -2
  12. package/dist/components/Skeleton/Skeleton.d.ts +7 -0
  13. package/dist/components/Skeleton/Skeleton.styles.d.ts +3 -0
  14. package/dist/components/Skeleton/index.d.ts +2 -0
  15. package/dist/components/Switch/Switch.d.ts +1 -1
  16. package/dist/components/index.d.ts +1 -0
  17. package/dist/helpers/phone.d.ts +1 -1
  18. package/dist/hooks/index.d.ts +1 -0
  19. package/dist/hooks/use-merged-refs.d.ts +2 -0
  20. package/dist/theme/types.d.ts +2 -1
  21. package/dist/true-react-common-ui-kit.js +1100 -757
  22. package/dist/true-react-common-ui-kit.js.map +1 -1
  23. package/dist/true-react-common-ui-kit.umd.cjs +1099 -756
  24. package/dist/true-react-common-ui-kit.umd.cjs.map +1 -1
  25. package/package.json +1 -1
  26. package/src/components/Checkbox/Checkbox.tsx +1 -1
  27. package/src/components/DateInput/DateInput.tsx +1 -9
  28. package/src/components/DatePicker/DatePicker.stories.tsx +1 -0
  29. package/src/components/DatePicker/DatePicker.tsx +1 -3
  30. package/src/components/FiltersPane/FiltersPane.stories.tsx +0 -8
  31. package/src/components/FiltersPane/components/FilterInterval/FilterInterval.styles.ts +2 -1
  32. package/src/components/FiltersPane/components/FilterSelect/FilterSelect.styles.ts +3 -1
  33. package/src/components/FiltersPane/components/FilterSelect/FilterSelect.tsx +1 -6
  34. package/src/components/FiltersPane/components/FilterWithDates/FilterWithDates.styles.ts +2 -1
  35. package/src/components/FiltersPane/components/FiltersPaneSearch/FiltersPaneSearch.styles.ts +1 -0
  36. package/src/components/FiltersPane/components/FiltersPaneSearch/FiltersPaneSearch.tsx +1 -4
  37. package/src/components/FiltersPane/types.ts +1 -1
  38. package/src/components/FlexibleTable/FlexibleTable.stories.tsx +15 -12
  39. package/src/components/FlexibleTable/FlexibleTable.styles.ts +9 -10
  40. package/src/components/FlexibleTable/FlexibleTable.tsx +124 -60
  41. package/src/components/FlexibleTable/components/FlexibleTableCell/FlexibleTableCell.styles.ts +4 -0
  42. package/src/components/FlexibleTable/components/FlexibleTableCell/FlexibleTableCell.tsx +25 -27
  43. package/src/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.styles.ts +4 -0
  44. package/src/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.tsx +12 -11
  45. package/src/components/FlexibleTable/helpers.ts +15 -0
  46. package/src/components/FlexibleTable/types.ts +1 -1
  47. package/src/components/MultiSelectList/MultiSelectList.styles.ts +4 -0
  48. package/src/components/MultiSelectList/MultiSelectList.tsx +1 -3
  49. package/src/components/Skeleton/Skeleton.stories.tsx +19 -0
  50. package/src/components/Skeleton/Skeleton.styles.ts +46 -0
  51. package/src/components/Skeleton/Skeleton.tsx +12 -0
  52. package/src/components/Skeleton/index.ts +2 -0
  53. package/src/components/Switch/Switch.tsx +2 -2
  54. package/src/components/index.ts +1 -0
  55. package/src/helpers/phone.ts +1 -1
  56. package/src/hooks/index.ts +1 -0
  57. package/src/hooks/use-merged-refs.ts +4 -0
  58. package/src/theme/types.ts +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@true-engineering/true-react-common-ui-kit",
3
- "version": "3.0.0-alpha.2",
3
+ "version": "3.0.0-alpha.20",
4
4
  "description": "True Engineering React UI Kit with theming support",
5
5
  "author": "True Engineering (https://trueengineering.ru)",
6
6
  "keywords": [
@@ -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?: boolean;
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}
@@ -47,6 +47,7 @@ const Template: ComponentStory<typeof DatePicker> = (args) => {
47
47
  <DatePicker
48
48
  {...args}
49
49
  locale={ru}
50
+ testId="datepicker"
50
51
  months={months}
51
52
  selectedDate={date}
52
53
  startDate={startDate}
@@ -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} {...addDataTestId(testId)} {...addDataAttributes(data)}>
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} style={{ width }} {...addDataAttributes(data)}>
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
 
@@ -104,6 +104,7 @@ export const selectStyles: ISelectStyles = {
104
104
  borderBottomLeftRadius: 6,
105
105
  borderBottomRightRadius: 6,
106
106
  boxShadow: 'none',
107
+ width: '100%',
107
108
  },
108
109
  };
109
110
 
@@ -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, IFilterSelectProps } from '../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' // TODO: rethink
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
- <FlexibleTable<ITableContent>
319
- {...args}
320
- uniqueField="contractCode"
321
- expandableRowComponent={(item, _, close) =>
322
- item.contractCode === 'OB_UT_M119' ? (
323
- <div onClick={close}>всем привет :) {item.contractCode}</div>
324
- ) : null
325
- }
326
- tweakStyles={tweak}
327
- content={content}
328
- config={config}
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, useRef, useState } from 'react';
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?: IFlexibleTableConfigType<Values>;
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 = (refForScroll ?? scrollRef).current;
101
- if (scrollContainer === null || !isHorizontallyScrollable || !isFirstColumnSticky) {
147
+ const scrollContainer = scrollRef.current;
148
+ if (isEmpty(scrollContainer) || !isHorizontallyScrollable) {
102
149
  return;
103
150
  }
104
- const scrollHandler = (e: Event) => {
105
- setIsHorizontallyScrolled((e.target as HTMLDivElement).scrollLeft > 0);
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
- return () => scrollContainer.removeEventListener('scroll', scrollHandler);
109
- }, [scrollRef]);
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
- ref={refForScroll ?? scrollRef}
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
- {(enabledColumns || Object.keys(content[0])).map((key, idx) => {
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.header,
136
- isFirstColumnSticky && idx === 0 && classes.headerSticky,
137
- isFirstColumnSticky && idx === 1 && classes.headerSecond,
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
- {content.length === 0 &&
156
- nothingFoundContent !== undefined &&
157
- !infinityScrollConfig?.isLoading &&
158
- (infinityScrollConfig?.isLastPage === undefined || infinityScrollConfig.isLastPage) && (
159
- <tr>
160
- <td colSpan={(enabledColumns || Object.keys(content[0])).length}>
161
- {nothingFoundContent}
162
- </td>
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
- {content.map((item, idx) => (
167
- <FlexibleTableRow
168
- item={item}
169
- uniqueField={uniqueField}
170
- isActive={activeRows?.includes(idx) ?? false}
171
- isFirstColumnSticky={isFirstColumnSticky}
172
- onRowClick={onRowClick}
173
- onRowHover={onRowHover}
174
- enabledColumns={enabledColumns}
175
- config={config}
176
- key={uniqueField ? item[uniqueField] : idx}
177
- rowAttributes={rowAttributes}
178
- tweakStyles={tweakTableRowStyles}
179
- expandableRowComponent={expandableRowComponent}
180
- />
181
- ))}
182
-
183
- {infinityScrollConfig !== undefined && !infinityScrollConfig.isLastPage && (
184
- <tr>
185
- <td colSpan={(enabledColumns || Object.keys(content[0])).length}>
186
- <div ref={initIntersectionObserver} className={classes.loader}>
187
- <ThemedPreloader type="dots" />
188
- </div>
189
- </td>
190
- </tr>
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>
@@ -18,6 +18,10 @@ export const useStyles = createThemedStyles('FlexibleTableCell', {
18
18
  paddingLeft: 24,
19
19
  paddingRight: 12,
20
20
  ...getTransition(['box-shadow']),
21
+
22
+ '[data-scrolled] &': {
23
+ boxShadow: '4px 0 4px rgba(0, 0, 0, 0.05)',
24
+ },
21
25
  },
22
26
 
23
27
  second: {