@true-engineering/true-react-common-ui-kit 3.0.0-alpha.9 → 3.0.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.
Files changed (120) hide show
  1. package/README.md +26 -0
  2. package/dist/components/Button/Button.d.ts +2 -1
  3. package/dist/components/Button/Button.styles.d.ts +1 -1
  4. package/dist/components/Checkbox/Checkbox.styles.d.ts +1 -1
  5. package/dist/components/DatePicker/components/DatePickerHeader/DatePickerHeader.styles.d.ts +1 -1
  6. package/dist/components/Description/Description.styles.d.ts +1 -1
  7. package/dist/components/FiltersPane/FiltersPane.styles.d.ts +1 -1
  8. package/dist/components/FiltersPane/components/Filter/Filter.d.ts +1 -1
  9. package/dist/components/FiltersPane/components/FilterInterval/FilterInterval.styles.d.ts +2 -2
  10. package/dist/components/FiltersPane/components/FilterSelect/FilterSelect.styles.d.ts +3 -3
  11. package/dist/components/FiltersPane/components/FilterValueView/FilterValueView.d.ts +1 -1
  12. package/dist/components/FiltersPane/components/FilterWithDates/FilterWithDates.styles.d.ts +4 -4
  13. package/dist/components/FiltersPane/components/FilterWrapper/FilterWrapper.styles.d.ts +1 -1
  14. package/dist/components/FiltersPane/constants.d.ts +1 -1
  15. package/dist/components/FiltersPane/types.d.ts +1 -1
  16. package/dist/components/FlexibleTable/FlexibleTable.d.ts +3 -2
  17. package/dist/components/FlexibleTable/FlexibleTable.styles.d.ts +2 -2
  18. package/dist/components/FlexibleTable/components/FlexibleTableCell/FlexibleTableCell.d.ts +2 -3
  19. package/dist/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.d.ts +1 -1
  20. package/dist/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.styles.d.ts +1 -1
  21. package/dist/components/FlexibleTable/helpers.d.ts +3 -0
  22. package/dist/components/FlexibleTable/types.d.ts +1 -1
  23. package/dist/components/Icon/helpers.d.ts +1 -1
  24. package/dist/components/Icon/icons-list.d.ts +1 -1
  25. package/dist/components/Icon/index.d.ts +1 -0
  26. package/dist/{helpers/snippets.d.ts → components/Icon/snippet.d.ts} +1 -1
  27. package/dist/components/Icon/types.d.ts +2 -1
  28. package/dist/components/Input/Input.d.ts +2 -1
  29. package/dist/components/Input/Input.styles.d.ts +1 -1
  30. package/dist/components/List/List.d.ts +3 -3
  31. package/dist/components/List/components/ListItem/ListItem.d.ts +15 -0
  32. package/dist/components/List/components/ListItem/ListItem.styles.d.ts +3 -0
  33. package/dist/components/List/components/ListItem/index.d.ts +2 -0
  34. package/dist/components/List/components/index.d.ts +1 -0
  35. package/dist/components/List/index.d.ts +1 -1
  36. package/dist/components/Modal/Modal.styles.d.ts +1 -1
  37. package/dist/components/MoreMenu/MoreMenu.styles.d.ts +1 -1
  38. package/dist/components/MultiSelect/MultiSelect.styles.d.ts +1 -1
  39. package/dist/components/MultiSelect/components/MultiSelectInput/MultiSelectInput.d.ts +2 -1
  40. package/dist/components/MultiSelect/components/MultiSelectInput/MultiSelectInput.styles.d.ts +1 -1
  41. package/dist/components/MultiSelectList/MultiSelectList.styles.d.ts +1 -1
  42. package/dist/components/Notification/Notification.styles.d.ts +1 -1
  43. package/dist/components/NumberInput/helpers.d.ts +1 -1
  44. package/dist/components/PhoneInput/PhoneInput.styles.d.ts +1 -1
  45. package/dist/components/PhoneInput/index.d.ts +0 -1
  46. package/dist/components/ScrollIntoViewIfNeeded/ScrollIntoViewIfNeeded.d.ts +5 -5
  47. package/dist/components/Select/Select.d.ts +2 -1
  48. package/dist/components/Select/Select.styles.d.ts +7 -7
  49. package/dist/components/Select/components/SelectList/SelectList.styles.d.ts +1 -1
  50. package/dist/components/Skeleton/Skeleton.d.ts +7 -0
  51. package/dist/components/Skeleton/Skeleton.styles.d.ts +3 -0
  52. package/dist/components/Skeleton/index.d.ts +2 -0
  53. package/dist/components/Switch/Switch.styles.d.ts +1 -1
  54. package/dist/components/TextArea/TextArea.styles.d.ts +1 -1
  55. package/dist/components/ThemedPreloader/ThemedPreloader.styles.d.ts +1 -1
  56. package/dist/components/Toaster/Toaster.styles.d.ts +1 -1
  57. package/dist/components/Tooltip/Tooltip.styles.d.ts +1 -1
  58. package/dist/components/index.d.ts +1 -0
  59. package/dist/constants/index.d.ts +1 -0
  60. package/dist/constants/phone-info.d.ts +2 -0
  61. package/dist/helpers/phone.d.ts +1 -1
  62. package/dist/hooks/index.d.ts +1 -0
  63. package/dist/hooks/use-merged-refs.d.ts +2 -0
  64. package/dist/index.d.ts +1 -1
  65. package/dist/theme/types.d.ts +3 -1
  66. package/dist/true-react-common-ui-kit.js +24269 -24084
  67. package/dist/true-react-common-ui-kit.js.map +1 -1
  68. package/dist/true-react-common-ui-kit.umd.cjs +24268 -24083
  69. package/dist/true-react-common-ui-kit.umd.cjs.map +1 -1
  70. package/dist/types.d.ts +0 -3
  71. package/package.json +2 -1
  72. package/src/components/Button/Button.tsx +2 -2
  73. package/src/components/FiltersPane/components/Filter/Filter.tsx +1 -1
  74. package/src/components/FiltersPane/components/FilterValueView/FilterValueView.tsx +1 -1
  75. package/src/components/FiltersPane/components/FilterWrapper/FilterWrapper.styles.ts +1 -1
  76. package/src/components/FiltersPane/constants.ts +1 -1
  77. package/src/components/FiltersPane/types.ts +1 -1
  78. package/src/components/FlexibleTable/FlexibleTable.stories.tsx +1 -0
  79. package/src/components/FlexibleTable/FlexibleTable.styles.ts +6 -1
  80. package/src/components/FlexibleTable/FlexibleTable.tsx +118 -55
  81. package/src/components/FlexibleTable/components/FlexibleTableCell/FlexibleTableCell.tsx +26 -30
  82. package/src/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.styles.ts +4 -0
  83. package/src/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.tsx +13 -12
  84. package/src/components/FlexibleTable/helpers.ts +15 -0
  85. package/src/components/FlexibleTable/types.ts +1 -1
  86. package/src/components/Icon/{helpers.ts → helpers.tsx} +1 -1
  87. package/src/components/Icon/icons-list.ts +30 -0
  88. package/src/components/Icon/index.ts +1 -0
  89. package/src/{helpers/snippets.tsx → components/Icon/snippet.tsx} +2 -2
  90. package/src/components/Icon/types.ts +3 -1
  91. package/src/components/Input/Input.tsx +2 -3
  92. package/src/components/List/List.stories.tsx +11 -4
  93. package/src/components/List/List.tsx +9 -32
  94. package/src/components/List/components/ListItem/ListItem.styles.ts +47 -0
  95. package/src/components/List/components/ListItem/ListItem.tsx +57 -0
  96. package/src/components/List/components/ListItem/index.ts +2 -0
  97. package/src/components/List/components/index.ts +1 -0
  98. package/src/components/List/index.ts +1 -1
  99. package/src/components/MultiSelect/components/MultiSelectInput/MultiSelectInput.tsx +3 -2
  100. package/src/components/NumberInput/helpers.ts +1 -1
  101. package/src/components/PhoneInput/PhoneInput.stories.tsx +1 -1
  102. package/src/components/PhoneInput/components/PhoneInputCountryList/PhoneInputCountryList.tsx +1 -1
  103. package/src/components/PhoneInput/index.ts +0 -1
  104. package/src/components/Select/Select.tsx +2 -2
  105. package/src/components/Skeleton/Skeleton.stories.tsx +19 -0
  106. package/src/components/Skeleton/Skeleton.styles.ts +46 -0
  107. package/src/components/Skeleton/Skeleton.tsx +12 -0
  108. package/src/components/Skeleton/index.ts +2 -0
  109. package/src/components/index.ts +1 -0
  110. package/src/constants/index.ts +1 -0
  111. package/src/{components/PhoneInput → constants}/phone-info.ts +1 -1
  112. package/src/helpers/phone.ts +2 -1
  113. package/src/hooks/index.ts +1 -0
  114. package/src/hooks/use-merged-refs.ts +4 -0
  115. package/src/index.ts +1 -1
  116. package/src/theme/types.ts +4 -0
  117. package/src/types.ts +0 -4
  118. package/dist/components/List/types.d.ts +0 -12
  119. package/dist/components/PhoneInput/phone-info.d.ts +0 -2
  120. package/src/components/List/types.ts +0 -13
package/dist/types.d.ts CHANGED
@@ -1,6 +1,4 @@
1
- import { ReactElement } from 'react';
2
1
  import { Modifier, Placement } from 'react-overlays/usePopper';
3
- import type { IIconType } from './components';
4
2
  export interface IDataAttributes {
5
3
  [key: string]: unknown;
6
4
  }
@@ -19,4 +17,3 @@ export interface IDropdownWithPopperOptions {
19
17
  placement?: Placement;
20
18
  flipOptions?: Record<string, any>;
21
19
  }
22
- export declare type IIcon = IIconType | ReactElement;
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.9",
3
+ "version": "3.0.0",
4
4
  "description": "True Engineering React UI Kit with theming support",
5
5
  "author": "True Engineering (https://trueengineering.ru)",
6
6
  "keywords": [
@@ -83,6 +83,7 @@
83
83
  "eslint": "8.36.0",
84
84
  "eslint-config-prettier": "8.7.0",
85
85
  "eslint-plugin-react": "7.32.2",
86
+ "eslint-plugin-import": "2.29.0",
86
87
  "prettier": "2.8.3",
87
88
  "rollup-plugin-swc3": "0.3.0",
88
89
  "typescript": "4.9.3",
@@ -2,9 +2,9 @@ import { ReactNode, ButtonHTMLAttributes, MouseEvent, forwardRef } from 'react';
2
2
  import clsx from 'clsx';
3
3
  import { addDataTestId, isReactNodeNotEmpty } from '@true-engineering/true-react-platform-helpers';
4
4
  import { addDataAttributes } from '../../helpers';
5
- import { renderIcon } from '../../helpers/snippets';
6
5
  import { useTweakStyles } from '../../hooks';
7
- import { ICommonProps, IIcon } from '../../types';
6
+ import { ICommonProps } from '../../types';
7
+ import { renderIcon, IIcon } from '../Icon';
8
8
  import { ThemedPreloader, IThemedPreloaderProps } from '../ThemedPreloader';
9
9
  import { IButtonSize, IButtonView } from './types';
10
10
  import { useStyles, IButtonStyles, getPreloaderStyles } from './Button.styles';
@@ -9,7 +9,7 @@ import { FilterMultiSelect } from '../FilterMultiSelect';
9
9
  import { FilterSelect } from '../FilterSelect';
10
10
  import { FilterWithDates } from '../FilterWithDates';
11
11
  import { FilterWithPeriod } from '../FilterWithPeriod';
12
- import { IFilterWrapperProps } from '../FilterWrapper';
12
+ import type { IFilterWrapperProps } from '../FilterWrapper';
13
13
 
14
14
  export interface IFilterProps<Values, Key extends keyof Values>
15
15
  extends IFilterWrapperProps<Values, Key> {
@@ -5,7 +5,7 @@ import { IMultiSelectListValues } from '../../../MultiSelectList';
5
5
  import { DEFAULT_DATE_FORMAT } from '../../constants';
6
6
  import { defaultConvertFunction, getLocale } from '../../helpers';
7
7
  import { IDateRangeConfigItem, IFilterWithDatesValue, IPeriod } from '../../types';
8
- import { IFilterWrapperProps } from '../FilterWrapper';
8
+ import type { IFilterWrapperProps } from '../FilterWrapper';
9
9
  import { IFilterValueViewStyles, useStyles } from './FilterValueView.styles';
10
10
 
11
11
  export function FilterValueView<Values, Key extends keyof Values>({
@@ -1,5 +1,5 @@
1
1
  import { colors, createThemedStyles, ITweakStyles } from '../../../../theme';
2
- import { IFilterValueViewStyles } from '../FilterValueView';
2
+ import type { IFilterValueViewStyles } from '../FilterValueView';
3
3
  import { FILTER_HEIGHT } from '../../FiltersPane.styles';
4
4
 
5
5
  export const useStyles = createThemedStyles('FilterWrapper', {
@@ -1,5 +1,5 @@
1
1
  import { endOfMonth, endOfYear, startOfMonth, startOfYear, sub } from 'date-fns';
2
- import {
2
+ import type {
3
3
  IFilterLocale,
4
4
  IFilterLocaleKey,
5
5
  IPeriodGetter,
@@ -1,7 +1,7 @@
1
1
  import { FC, ReactNode } from 'react';
2
2
  import { IDatePickerProps } from '../DatePicker';
3
3
  import { IMultiSelectListValues } from '../MultiSelectList';
4
- import {
4
+ import type {
5
5
  IFilterIntervalProps,
6
6
  IFilterMultiSelectProps,
7
7
  IFilterSelectProps,
@@ -336,6 +336,7 @@ export const Default = Template.bind({});
336
336
  Default.args = {
337
337
  isHorizontallyScrollable: true,
338
338
  isFirstColumnSticky: true,
339
+ isLoading: false,
339
340
  };
340
341
 
341
342
  Default.parameters = {
@@ -1,6 +1,6 @@
1
1
  import { rgba } from '../../helpers';
2
2
  import { colors, ITweakStyles, createThemedStyles } from '../../theme';
3
- import { IFlexibleTableRowStyles } from './components';
3
+ import type { IFlexibleTableRowStyles } from './components';
4
4
 
5
5
  export const STICKY_SHADOW_PADDING = 12;
6
6
 
@@ -90,6 +90,11 @@ export const useStyles = createThemedStyles('FlexibleTable', {
90
90
  headerSecond: {
91
91
  paddingLeft: STICKY_SHADOW_PADDING,
92
92
  },
93
+
94
+ skeleton: {
95
+ height: 21,
96
+ padding: [14, 7],
97
+ },
93
98
  });
94
99
 
95
100
  export type IFlexibleTableStyles = ITweakStyles<
@@ -1,11 +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 { addDataTestId, isNotEmpty } from '@true-engineering/true-react-platform-helpers';
3
+ import {
4
+ addDataTestId,
5
+ indexMap,
6
+ isArrayNotEmpty,
7
+ isEmpty,
8
+ isNotEmpty,
9
+ } from '@true-engineering/true-react-platform-helpers';
4
10
  import { addDataAttributes } from '../../helpers';
5
- import { useTweakStyles } from '../../hooks';
11
+ import { useMergedRefs, useTweakStyles } from '../../hooks';
6
12
  import { ICommonProps } from '../../types';
13
+ import { Skeleton } from '../Skeleton';
7
14
  import { ThemedPreloader } from '../ThemedPreloader';
8
15
  import { FlexibleTableRow } from './components';
16
+ import { hasHorizontalScrollBar } from './helpers';
9
17
  import { IFlexibleTableConfigType, IInfinityScrollConfig, ITitleComponent } from './types';
10
18
  import { useStyles, IFlexibleTableStyles } from './FlexibleTable.styles';
11
19
 
@@ -16,7 +24,7 @@ export interface IFlexibleTableProps<Values extends Record<string, any>>
16
24
  headerContent?: Partial<Record<keyof Values, any>>;
17
25
  enabledColumns?: Array<keyof Values>;
18
26
  activeRows?: number[];
19
- config?: IFlexibleTableConfigType<Values>;
27
+ config: IFlexibleTableConfigType<Values>;
20
28
  isHorizontallyScrollable?: boolean;
21
29
  isFirstColumnSticky?: boolean;
22
30
  infinityScrollConfig?: IInfinityScrollConfig;
@@ -25,6 +33,7 @@ export interface IFlexibleTableProps<Values extends Record<string, any>>
25
33
  */
26
34
  uniqueField?: keyof Values;
27
35
  onHeadClick?: (column: keyof Values) => void;
36
+ isLoading?: boolean;
28
37
  // TODO: Заменить string на Generic Values[uniqueField]
29
38
  onRowClick?: (id: string) => void;
30
39
  onRowHover?: (id?: string) => void;
@@ -46,6 +55,7 @@ export function FlexibleTable<Values extends Record<string, any>>({
46
55
  isFirstColumnSticky,
47
56
  infinityScrollConfig,
48
57
  uniqueField,
58
+ isLoading,
49
59
  onHeadClick,
50
60
  onRowHover,
51
61
  onRowClick,
@@ -63,11 +73,44 @@ export function FlexibleTable<Values extends Record<string, any>>({
63
73
  currentComponentName: 'FlexibleTable',
64
74
  });
65
75
 
66
- const [isHorizontallyScrolled, setIsHorizontallyScrolled] = useState(false);
67
-
68
76
  const observer = useRef<IntersectionObserver>();
69
77
  const scrollRef = useRef<HTMLDivElement>(null);
70
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
+
71
114
  const initIntersectionObserver = useCallback(
72
115
  (node: HTMLDivElement) => {
73
116
  if (infinityScrollConfig) {
@@ -101,27 +144,34 @@ export function FlexibleTable<Values extends Record<string, any>>({
101
144
  );
102
145
 
103
146
  useEffect(() => {
104
- const scrollContainer = (refForScroll ?? scrollRef).current;
105
- if (scrollContainer === null || !isHorizontallyScrollable || !isFirstColumnSticky) {
147
+ const scrollContainer = scrollRef.current;
148
+ if (isEmpty(scrollContainer) || !isHorizontallyScrollable) {
106
149
  return;
107
150
  }
108
- const scrollHandler = (e: Event) => {
109
- setIsHorizontallyScrolled((e.target as HTMLDivElement).scrollLeft > 0);
151
+
152
+ const scrollHandler = () => {
153
+ setIsScrolledAttribute(scrollContainer);
154
+ };
155
+
156
+ const resizeHandler = () => {
157
+ setHasScrollBarAttribute(scrollContainer);
110
158
  };
159
+
111
160
  scrollContainer.addEventListener('scroll', scrollHandler);
112
- return () => scrollContainer.removeEventListener('scroll', scrollHandler);
113
- }, [scrollRef]);
161
+ window.addEventListener('resize', resizeHandler);
162
+
163
+ return () => {
164
+ scrollContainer.removeEventListener('scroll', scrollHandler);
165
+ window.removeEventListener('resize', resizeHandler);
166
+ };
167
+ }, [scrollRef, setIsScrolledAttribute, setHasScrollBarAttribute]);
114
168
 
115
169
  return (
116
- <div
117
- ref={refForScroll ?? scrollRef}
118
- className={clsx({ [classes.scroll]: isHorizontallyScrollable })}
119
- data-scrolled={isHorizontallyScrolled ? true : undefined}
120
- >
170
+ <div ref={ref} className={clsx({ [classes.scroll]: isHorizontallyScrollable })}>
121
171
  <table className={classes.root} {...addDataTestId(testId)} {...addDataAttributes(data)}>
122
172
  <thead>
123
173
  <tr className={classes.headerRow}>
124
- {(enabledColumns || Object.keys(content[0])).map((key, idx) => {
174
+ {showedColumns.map((key, i) => {
125
175
  const itemConfig = config?.[key];
126
176
 
127
177
  let titleContent = itemConfig?.title ?? '';
@@ -134,8 +184,8 @@ export function FlexibleTable<Values extends Record<string, any>>({
134
184
  return (
135
185
  <th
136
186
  className={clsx(classes.header, {
137
- [classes.headerSticky]: isFirstColumnSticky && idx === 0,
138
- [classes.headerSecond]: isFirstColumnSticky && idx === 1,
187
+ [classes.headerSticky]: isFirstColumnSticky && i === 0,
188
+ [classes.headerSecond]: isFirstColumnSticky && i === 1,
139
189
  })}
140
190
  style={{
141
191
  minWidth: itemConfig?.minWidth,
@@ -153,42 +203,55 @@ export function FlexibleTable<Values extends Record<string, any>>({
153
203
  </tr>
154
204
  </thead>
155
205
  <tbody>
156
- {content.length === 0 &&
157
- nothingFoundContent !== undefined &&
158
- !infinityScrollConfig?.isLoading &&
159
- (infinityScrollConfig?.isLastPage === undefined || infinityScrollConfig.isLastPage) && (
160
- <tr>
161
- <td colSpan={(enabledColumns || Object.keys(content[0])).length}>
162
- {nothingFoundContent}
163
- </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
+ ))}
164
214
  </tr>
165
- )}
166
-
167
- {content.map((item, i) => (
168
- <FlexibleTableRow
169
- item={item}
170
- uniqueField={uniqueField}
171
- isActive={activeRows?.includes(i) ?? false}
172
- isFirstColumnSticky={isFirstColumnSticky}
173
- onRowClick={onRowClick}
174
- onRowHover={onRowHover}
175
- enabledColumns={enabledColumns}
176
- config={config}
177
- key={isNotEmpty(uniqueField) ? item[uniqueField] : i}
178
- rowAttributes={rowAttributes}
179
- tweakStyles={tweakTableRowStyles}
180
- expandableRowComponent={expandableRowComponent}
181
- />
182
- ))}
183
-
184
- {infinityScrollConfig !== undefined && !infinityScrollConfig.isLastPage && (
185
- <tr>
186
- <td colSpan={(enabledColumns || Object.keys(content[0])).length}>
187
- <div ref={initIntersectionObserver} className={classes.loader}>
188
- <ThemedPreloader type="dots" />
189
- </div>
190
- </td>
191
- </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
+ </>
192
255
  )}
193
256
  </tbody>
194
257
  </table>
@@ -1,8 +1,8 @@
1
1
  import { ReactNode } from 'react';
2
2
  import clsx from 'clsx';
3
- import { format } from 'date-fns';
3
+ import { isNotEmpty } from '@true-engineering/true-react-platform-helpers';
4
4
  import type { ICommonProps } from '../../../../types';
5
- import { DEFAULT_DATE_FORMAT } from '../../constants';
5
+ import { formatCellContent } from '../../helpers';
6
6
  import type { IFlexibleTableConfigType } from '../../types';
7
7
  import { useStyles, IFlexibleTableCellStyles } from './FlexibleTableCell.styles';
8
8
 
@@ -10,7 +10,7 @@ export interface IFlexibleTableCellProps<Values extends Record<string, any>>
10
10
  extends Pick<ICommonProps<IFlexibleTableCellStyles>, 'tweakStyles'> {
11
11
  item: Values;
12
12
  columnName: keyof Values;
13
- config?: IFlexibleTableConfigType<Values>;
13
+ config: IFlexibleTableConfigType<Values>;
14
14
  isFocusedRow?: boolean;
15
15
  isSecond?: boolean;
16
16
  isSticky?: boolean;
@@ -19,7 +19,7 @@ export interface IFlexibleTableCellProps<Values extends Record<string, any>>
19
19
  onSetNestedComponent(component?: ReactNode): void;
20
20
  }
21
21
 
22
- function FlexibleTableCell<Values extends Record<string, any>>({
22
+ export function FlexibleTableCell<Values extends Record<string, any>>({
23
23
  item,
24
24
  columnName,
25
25
  config,
@@ -33,41 +33,37 @@ function FlexibleTableCell<Values extends Record<string, any>>({
33
33
  }: IFlexibleTableCellProps<Values>): JSX.Element {
34
34
  const classes = useStyles({ theme: tweakStyles });
35
35
 
36
- const itemConfig = config?.[columnName];
37
- const value = item[columnName];
38
- let content = null;
36
+ const { component, cellAlign, position, right, left, cellVerticalAlign } =
37
+ config[columnName] ?? {};
39
38
 
40
- if (itemConfig?.component) {
41
- const ValueComponent = itemConfig?.component;
42
- content = ValueComponent({
43
- value,
44
- row: item,
45
- isFocusedRow,
46
- isNestedComponentExpanded,
47
- isRowNestedComponentExpanded,
48
- onSetNestedComponent,
49
- });
50
- } else if (typeof value === 'string' || typeof value === 'number') {
51
- content = value;
52
- } else if ((value as any) instanceof Date) {
53
- content = format(value, itemConfig?.dateFormat || DEFAULT_DATE_FORMAT);
54
- }
39
+ const value = item[columnName];
55
40
 
56
41
  return (
57
42
  <td
58
43
  key={columnName as string}
59
44
  className={clsx(classes.root, { [classes.sticky]: isSticky, [classes.second]: isSecond })}
60
45
  style={{
61
- textAlign: itemConfig?.cellAlign,
62
- position: isSticky ? 'sticky' : itemConfig?.position,
63
- right: itemConfig?.right,
64
- left: isSticky ? 0 : itemConfig?.left,
65
- verticalAlign: itemConfig?.cellVerticalAlign,
46
+ textAlign: cellAlign,
47
+ position: isSticky ? 'sticky' : position,
48
+ right,
49
+ left: isSticky ? 0 : left,
50
+ verticalAlign: cellVerticalAlign,
66
51
  }}
67
52
  >
68
- {content}
53
+ {isNotEmpty(value) && (
54
+ <>
55
+ {isNotEmpty(component)
56
+ ? component({
57
+ value,
58
+ row: item,
59
+ isFocusedRow,
60
+ isNestedComponentExpanded,
61
+ isRowNestedComponentExpanded,
62
+ onSetNestedComponent,
63
+ })
64
+ : formatCellContent(value, config[columnName])}
65
+ </>
66
+ )}
69
67
  </td>
70
68
  );
71
69
  }
72
-
73
- export default FlexibleTableCell;
@@ -11,6 +11,10 @@ export const useStyles = createThemedStyles('FlexibleTableRow', {
11
11
  editable: {
12
12
  cursor: 'pointer',
13
13
  },
14
+
15
+ clickable: {
16
+ cursor: 'pointer',
17
+ },
14
18
  });
15
19
 
16
20
  export type IFlexibleTableRowStyles = ITweakStyles<
@@ -1,10 +1,11 @@
1
1
  import { ReactNode, useState, memo } from 'react';
2
2
  import clsx from 'clsx';
3
+ import { isNotEmpty } from '@true-engineering/true-react-platform-helpers';
3
4
  import { addDataAttributes } from '../../../../helpers';
4
5
  import { useTweakStyles } from '../../../../hooks';
5
6
  import { ICommonProps, IDataAttributes } from '../../../../types';
6
7
  import { IFlexibleTableConfigType, INestedComponent } from '../../types';
7
- import FlexibleTableCell from '../FlexibleTableCell/FlexibleTableCell';
8
+ import { FlexibleTableCell } from '../FlexibleTableCell';
8
9
  import { useStyles, IFlexibleTableRowStyles } from './FlexibleTableRow.styles';
9
10
 
10
11
  // TODO: Заменить Record<string, any> на Record<string, unknown>
@@ -14,7 +15,7 @@ export interface IFlexibleTableRowProps<Values extends Record<string, any>>
14
15
  uniqueField?: keyof Values;
15
16
  isFirstColumnSticky?: boolean;
16
17
  isActive: boolean;
17
- config?: IFlexibleTableConfigType<Values>;
18
+ config: IFlexibleTableConfigType<Values>;
18
19
  enabledColumns?: Array<keyof Values>;
19
20
  rowAttributes?: Array<keyof Values>;
20
21
  expandableRowComponent?(item: Values, isOpen: boolean, close: () => void): ReactNode;
@@ -81,7 +82,7 @@ function FlexibleTableRowInner<Values extends Record<string, any>>({
81
82
  onRowClick?.(item[uniqueField]);
82
83
  }
83
84
 
84
- if (expandableRowComponent !== undefined) {
85
+ if (isNotEmpty(expandableRowComponent)) {
85
86
  const newNestedComponent = expandableRowComponent(item, true, closeNestedComponent);
86
87
 
87
88
  if (!nestedComponent.isOpen && newNestedComponent !== null) {
@@ -96,16 +97,16 @@ function FlexibleTableRowInner<Values extends Record<string, any>>({
96
97
  }
97
98
  };
98
99
 
99
- const items = enabledColumns ?? Object.keys(item);
100
+ const items = enabledColumns ?? Object.keys(config);
100
101
 
101
102
  return (
102
103
  <>
103
104
  <tr
104
- className={clsx(
105
- classes.root,
106
- isActive && classes.active,
107
- (onRowClick !== undefined || onRowHover !== undefined) && classes.editable,
108
- )}
105
+ className={clsx(classes.root, {
106
+ [classes.active]: isActive,
107
+ [classes.editable]: isNotEmpty(onRowClick) || isNotEmpty(onRowHover),
108
+ [classes.clickable]: isNotEmpty(onRowClick) || isNotEmpty(expandableRowComponent),
109
+ })}
109
110
  onMouseEnter={(e) => {
110
111
  if (uniqueField !== undefined && onRowHover !== undefined) {
111
112
  e.stopPropagation();
@@ -120,11 +121,11 @@ function FlexibleTableRowInner<Values extends Record<string, any>>({
120
121
  isExpandableComponentActive: nestedComponent.isOpen ? true : undefined,
121
122
  })}
122
123
  >
123
- {items.map((key, idx) => (
124
+ {items.map((key, i) => (
124
125
  <FlexibleTableCell
125
126
  columnName={key}
126
- isSticky={isFirstColumnSticky && idx === 0}
127
- isSecond={isFirstColumnSticky && idx === 1}
127
+ isSticky={isFirstColumnSticky && i === 0}
128
+ isSecond={isFirstColumnSticky && i === 1}
128
129
  key={key as string}
129
130
  item={item}
130
131
  config={config}
@@ -0,0 +1,15 @@
1
+ import { format } from 'date-fns';
2
+ import { isNotEmpty } from '@true-engineering/true-react-platform-helpers';
3
+ import { DEFAULT_DATE_FORMAT } from './constants';
4
+ import { IFlexibleTableConfigType } from './types';
5
+
6
+ export const hasHorizontalScrollBar = (el: HTMLElement | null | undefined): boolean =>
7
+ isNotEmpty(el) && el.scrollWidth !== el.clientWidth;
8
+
9
+ export const formatCellContent = <Values>(
10
+ value: unknown,
11
+ config?: IFlexibleTableConfigType<Values>[keyof Values],
12
+ ): string =>
13
+ value instanceof Date
14
+ ? format(value as Date, config?.dateFormat ?? DEFAULT_DATE_FORMAT)
15
+ : String(value);
@@ -23,7 +23,7 @@ export type IFlexibleTableConfigType<Values> = {
23
23
  [Key in keyof Values]?: {
24
24
  title?: ReactNode;
25
25
  titleComponent?: ITitleComponent<unknown>;
26
- component?: IValueComponent<Values, Values[Key]>;
26
+ component?: IValueComponent<Values, NonNullable<Values[Key]>>;
27
27
  dateFormat?: string;
28
28
  minWidth?: string | number;
29
29
  width?: string | number;
@@ -1,5 +1,5 @@
1
1
  import { complexIcons } from './complexIcons';
2
- import { IComplexIcon, IIconType, ISvgIcon } from './types';
2
+ import type { IComplexIcon, IIconType, ISvgIcon } from './types';
3
3
 
4
4
  export const isComplexIcon = (type: IIconType): type is IComplexIcon => type in complexIcons;
5
5
 
@@ -370,6 +370,26 @@ export const iconsList = checkIcons({
370
370
  },
371
371
  ],
372
372
  },
373
+ folder: {
374
+ viewBox: '0 0 20 20',
375
+ paths: [
376
+ {
377
+ d: 'M16.9 5H10.009L8.716 3.795C8.25211 3.34264 7.714 3 7.069 3H3.1C1.942 3 1 3.942 1 5.1V15.9C1 17.058 1.942 18 3.1 18H16.9C18.058 18 19 17.058 19 15.9V7.1C19 5.942 18.058 5 16.9 5Z',
378
+ },
379
+ ],
380
+ },
381
+ 'folder-add': {
382
+ viewBox: '0 0 20 20',
383
+ paths: [
384
+ {
385
+ d: 'M16.9 5H10.009C10.009 5 9.19158 4.23037 8.716 3.795C8.24042 3.35963 7.714 3 7.069 3H3.1C1.942 3 1 3.942 1 5.1V15.9C1 17.058 1.942 18 3.1 18H16.9C18.058 18 19 17.058 19 15.9V7.1C19 5.942 18.058 5 16.9 5Z',
386
+ },
387
+ {
388
+ d: 'M13 13H11V15C11 15.5523 10.5523 16 10 16C9.44772 16 9 15.5523 9 15V13H7C6.44772 13 6 12.5523 6 12C6 11.4477 6.44772 11 7 11H9V9C9 8.44772 9.44772 8 10 8C10.5523 8 11 8.44772 11 9V11H13C13.5523 11 14 11.4477 14 12C14 12.5523 13.5523 13 13 13Z',
389
+ fill: 'white',
390
+ },
391
+ ],
392
+ },
373
393
  food: {
374
394
  viewBox: '0 0 28 28',
375
395
  paths: [
@@ -495,6 +515,16 @@ export const iconsList = checkIcons({
495
515
  },
496
516
  ],
497
517
  },
518
+ presentation: {
519
+ viewBox: '0 0 20 20',
520
+ paths: [
521
+ { d: 'M8 10H12V11H8V10Z' },
522
+ { d: 'M14 8H6V9H14V8Z' },
523
+ {
524
+ d: 'M4 2C2.89543 2 2 2.89543 2 4V13C2 14.1046 2.89543 15 4 15H7.6906L5.76795 18.3301L7.5 19.3301L10 15L12.5004 19.3301L14.2324 18.3301L12.3098 15H16C17.1046 15 18 14.1046 18 13V4C18 2.89543 17.1046 2 16 2H4ZM16 6H4V13H16V6Z',
525
+ },
526
+ ],
527
+ },
498
528
  print: {
499
529
  paths: [
500
530
  {
@@ -2,3 +2,4 @@ export * from './Icon';
2
2
  export * from './types';
3
3
  export { iconsList } from './icons-list';
4
4
  export type { IIconStyles } from './Icon.styles';
5
+ export * from './snippet';
@@ -1,6 +1,6 @@
1
1
  import { ReactNode } from 'react';
2
- import { Icon } from '../components';
3
- import { IIcon } from '../types';
2
+ import { Icon } from './Icon';
3
+ import { IIcon } from './types';
4
4
 
5
5
  export const renderIcon = (icon: IIcon): ReactNode =>
6
6
  typeof icon === 'string' ? <Icon type={icon} /> : icon;
@@ -1,4 +1,4 @@
1
- import { SVGAttributes } from 'react';
1
+ import { ReactElement, SVGAttributes } from 'react';
2
2
  import { complexIcons } from './complexIcons';
3
3
  import { iconsList } from './icons-list';
4
4
 
@@ -14,3 +14,5 @@ export type ICommonIcon = keyof typeof iconsList;
14
14
  export type IComplexIcon = keyof typeof complexIcons;
15
15
 
16
16
  export type IIconType = ICommonIcon | IComplexIcon;
17
+
18
+ export type IIcon = IIconType | ReactElement;
@@ -19,10 +19,9 @@ import {
19
19
  isStringNotEmpty,
20
20
  } from '@true-engineering/true-react-platform-helpers';
21
21
  import { addDataAttributes } from '../../helpers';
22
- import { renderIcon } from '../../helpers/snippets';
23
22
  import { useTweakStyles } from '../../hooks';
24
- import { ICommonProps, IIcon } from '../../types';
25
- import { Icon } from '../Icon';
23
+ import { ICommonProps } from '../../types';
24
+ import { renderIcon, Icon, IIcon } from '../Icon';
26
25
  import { ThemedPreloader } from '../ThemedPreloader';
27
26
  import { DEFAULT_SIZE } from './constants';
28
27
  import { IReactInputMaskProps } from './types';