@true-engineering/true-react-common-ui-kit 3.0.0-alpha.16 → 3.0.0-alpha.18

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/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.16",
3
+ "version": "3.0.0-alpha.18",
4
4
  "description": "True Engineering React UI Kit with theming support",
5
5
  "author": "True Engineering (https://trueengineering.ru)",
6
6
  "keywords": [
@@ -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 = {
@@ -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,9 +1,16 @@
1
- import { ReactNode, RefObject, useCallback, useEffect, useRef, useState } from 'react';
1
+ import { ReactNode, RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
2
  import clsx from 'clsx';
3
- import { addDataTestId, isEmpty, 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
11
  import { 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';
9
16
  import { hasHorizontalScrollBar } from './helpers';
@@ -26,6 +33,7 @@ export interface IFlexibleTableProps<Values extends Record<string, any>>
26
33
  */
27
34
  uniqueField?: keyof Values;
28
35
  onHeadClick?: (column: keyof Values) => void;
36
+ isLoading?: boolean;
29
37
  // TODO: Заменить string на Generic Values[uniqueField]
30
38
  onRowClick?: (id: string) => void;
31
39
  onRowHover?: (id?: string) => void;
@@ -47,6 +55,7 @@ export function FlexibleTable<Values extends Record<string, any>>({
47
55
  isFirstColumnSticky,
48
56
  infinityScrollConfig,
49
57
  uniqueField,
58
+ isLoading,
50
59
  onHeadClick,
51
60
  onRowHover,
52
61
  onRowClick,
@@ -71,6 +80,11 @@ export function FlexibleTable<Values extends Record<string, any>>({
71
80
  const scrollRef = useRef<HTMLDivElement>(null);
72
81
  const ref = refForScroll ?? scrollRef;
73
82
 
83
+ const showedColumns = useMemo(
84
+ () => enabledColumns ?? Object.keys(config),
85
+ [enabledColumns, config],
86
+ );
87
+
74
88
  const initIntersectionObserver = useCallback(
75
89
  (node: HTMLDivElement) => {
76
90
  if (infinityScrollConfig) {
@@ -140,7 +154,7 @@ export function FlexibleTable<Values extends Record<string, any>>({
140
154
  <table className={classes.root} {...addDataTestId(testId)} {...addDataAttributes(data)}>
141
155
  <thead>
142
156
  <tr className={classes.headerRow}>
143
- {(enabledColumns || Object.keys(content[0])).map((key, idx) => {
157
+ {showedColumns.map((key, i) => {
144
158
  const itemConfig = config?.[key];
145
159
 
146
160
  let titleContent = itemConfig?.title ?? '';
@@ -153,8 +167,8 @@ export function FlexibleTable<Values extends Record<string, any>>({
153
167
  return (
154
168
  <th
155
169
  className={clsx(classes.header, {
156
- [classes.headerSticky]: isFirstColumnSticky && idx === 0,
157
- [classes.headerSecond]: isFirstColumnSticky && idx === 1,
170
+ [classes.headerSticky]: isFirstColumnSticky && i === 0,
171
+ [classes.headerSecond]: isFirstColumnSticky && i === 1,
158
172
  })}
159
173
  style={{
160
174
  minWidth: itemConfig?.minWidth,
@@ -172,42 +186,54 @@ export function FlexibleTable<Values extends Record<string, any>>({
172
186
  </tr>
173
187
  </thead>
174
188
  <tbody>
175
- {content.length === 0 &&
189
+ {!isArrayNotEmpty(content) &&
176
190
  nothingFoundContent !== undefined &&
177
191
  !infinityScrollConfig?.isLoading &&
178
192
  (infinityScrollConfig?.isLastPage === undefined || infinityScrollConfig.isLastPage) && (
179
193
  <tr>
180
- <td colSpan={(enabledColumns || Object.keys(content[0])).length}>
181
- {nothingFoundContent}
182
- </td>
194
+ <td colSpan={showedColumns.length}>{nothingFoundContent}</td>
183
195
  </tr>
184
196
  )}
185
197
 
186
- {content.map((item, i) => (
187
- <FlexibleTableRow
188
- item={item}
189
- uniqueField={uniqueField}
190
- isActive={activeRows?.includes(i) ?? false}
191
- isFirstColumnSticky={isFirstColumnSticky}
192
- onRowClick={onRowClick}
193
- onRowHover={onRowHover}
194
- enabledColumns={enabledColumns}
195
- config={config}
196
- key={isNotEmpty(uniqueField) ? item[uniqueField] : i}
197
- rowAttributes={rowAttributes}
198
- tweakStyles={tweakTableRowStyles}
199
- expandableRowComponent={expandableRowComponent}
200
- />
201
- ))}
202
-
203
- {infinityScrollConfig !== undefined && !infinityScrollConfig.isLastPage && (
204
- <tr>
205
- <td colSpan={(enabledColumns || Object.keys(content[0])).length}>
206
- <div ref={initIntersectionObserver} className={classes.loader}>
207
- <ThemedPreloader type="dots" />
208
- </div>
209
- </td>
210
- </tr>
198
+ {isLoading ? (
199
+ indexMap(6, (i) => (
200
+ <tr key={i}>
201
+ {showedColumns.map((_, j) => (
202
+ <td key={j} className={classes.skeleton}>
203
+ <Skeleton />
204
+ </td>
205
+ ))}
206
+ </tr>
207
+ ))
208
+ ) : (
209
+ <>
210
+ {content.map((item, i) => (
211
+ <FlexibleTableRow
212
+ item={item}
213
+ uniqueField={uniqueField}
214
+ isActive={activeRows?.includes(i) ?? false}
215
+ isFirstColumnSticky={isFirstColumnSticky}
216
+ onRowClick={onRowClick}
217
+ onRowHover={onRowHover}
218
+ enabledColumns={enabledColumns}
219
+ config={config}
220
+ key={isNotEmpty(uniqueField) ? item[uniqueField] : i}
221
+ rowAttributes={rowAttributes}
222
+ tweakStyles={tweakTableRowStyles}
223
+ expandableRowComponent={expandableRowComponent}
224
+ />
225
+ ))}
226
+
227
+ {infinityScrollConfig !== undefined && !infinityScrollConfig.isLastPage && (
228
+ <tr>
229
+ <td colSpan={showedColumns.length}>
230
+ <div ref={initIntersectionObserver} className={classes.loader}>
231
+ <ThemedPreloader type="dots" />
232
+ </div>
233
+ </td>
234
+ </tr>
235
+ )}
236
+ </>
211
237
  )}
212
238
  </tbody>
213
239
  </table>
@@ -97,7 +97,7 @@ function FlexibleTableRowInner<Values extends Record<string, any>>({
97
97
  }
98
98
  };
99
99
 
100
- const items = enabledColumns ?? Object.keys(item);
100
+ const items = enabledColumns ?? Object.keys(config);
101
101
 
102
102
  return (
103
103
  <>
@@ -121,11 +121,11 @@ function FlexibleTableRowInner<Values extends Record<string, any>>({
121
121
  isExpandableComponentActive: nestedComponent.isOpen ? true : undefined,
122
122
  })}
123
123
  >
124
- {items.map((key, idx) => (
124
+ {items.map((key, i) => (
125
125
  <FlexibleTableCell
126
126
  columnName={key}
127
- isSticky={isFirstColumnSticky && idx === 0}
128
- isSecond={isFirstColumnSticky && idx === 1}
127
+ isSticky={isFirstColumnSticky && i === 0}
128
+ isSecond={isFirstColumnSticky && i === 1}
129
129
  key={key as string}
130
130
  item={item}
131
131
  config={config}
@@ -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<NonNullable<Values>, Values[Key]>;
26
+ component?: IValueComponent<Values, NonNullable<Values[Key]>>;
27
27
  dateFormat?: string;
28
28
  minWidth?: string | number;
29
29
  width?: string | number;
@@ -0,0 +1,19 @@
1
+ import { ComponentStory } from '@storybook/react';
2
+ import { Skeleton } from './Skeleton';
3
+
4
+ export default {
5
+ title: 'Skeleton',
6
+ component: Skeleton,
7
+ };
8
+
9
+ const Template: ComponentStory<typeof Skeleton> = (args) => (
10
+ <div style={{ padding: 32, backgroundColor: '#fff', width: 200 }}>
11
+ <Skeleton {...args} />
12
+ </div>
13
+ );
14
+
15
+ export const Default = Template.bind({});
16
+
17
+ Default.args = {
18
+ height: '20px',
19
+ };
@@ -0,0 +1,46 @@
1
+ import { rgba } from '../../helpers';
2
+ import { colors, createThemedStyles, ITweakStyles } from '../../theme';
3
+
4
+ export const useStyles = createThemedStyles('Skeleton', {
5
+ root: {
6
+ display: 'flex',
7
+ width: '100%',
8
+ height: '100%',
9
+ backgroundColor: rgba(colors.GREY_DISABLED, 0.8),
10
+ position: 'relative',
11
+ borderRadius: 4,
12
+ overflow: 'hidden',
13
+ '-webkit-mask-image': '-webkit-radial-gradient(white, black)',
14
+
15
+ '&::after': {
16
+ content: '""',
17
+ animation: '$skeleton 1.6s linear 0.5s infinite',
18
+ background: `linear-gradient(90deg, transparent, ${rgba(
19
+ colors.GREY_BACKGROUND,
20
+ 0.65,
21
+ )}, transparent)`,
22
+ position: 'absolute',
23
+ transform: 'translateX(-100%)',
24
+ bottom: 0,
25
+ left: 0,
26
+ right: 0,
27
+ top: 0,
28
+ },
29
+ },
30
+
31
+ '@keyframes skeleton': {
32
+ '0%': {
33
+ transform: 'translateX(-100%)',
34
+ },
35
+
36
+ '50%': {
37
+ transform: 'translateX(100%)',
38
+ },
39
+
40
+ '100%': {
41
+ transform: 'translateX(100%)',
42
+ },
43
+ },
44
+ });
45
+
46
+ export type ISkeletonStyles = ITweakStyles<typeof useStyles>;
@@ -0,0 +1,12 @@
1
+ import { CSSProperties, FC } from 'react';
2
+ import { ICommonProps } from '../../types';
3
+ import { ISkeletonStyles, useStyles } from './Skeleton.styles';
4
+
5
+ export interface ISkeletonProps extends Pick<ICommonProps<ISkeletonStyles>, 'tweakStyles'> {
6
+ height?: CSSProperties['height'];
7
+ }
8
+
9
+ export const Skeleton: FC<ISkeletonProps> = ({ height, tweakStyles }) => {
10
+ const classes = useStyles({ theme: tweakStyles });
11
+ return <div className={classes.root} style={{ height }} />;
12
+ };
@@ -0,0 +1,2 @@
1
+ export * from './Skeleton';
2
+ export type { ISkeletonStyles } from './Skeleton.styles';
@@ -34,3 +34,4 @@ export * from './ThemedPreloader';
34
34
  export * from './Toaster';
35
35
  export * from './Tooltip';
36
36
  export * from './ScrollIntoViewIfNeeded';
37
+ export * from './Skeleton';
@@ -52,6 +52,7 @@ import type {
52
52
  IFlexibleTableCellStyles,
53
53
  IFlexibleTableRowStyles,
54
54
  IFilterValueViewStyles,
55
+ ISkeletonStyles,
55
56
  } from '../components';
56
57
 
57
58
  export type IStyles<C extends string, P> = Styles<C, P, Partial<Styles<C, P>>>;
@@ -108,6 +109,7 @@ export interface IComponentStyles {
108
109
  SearchInput: ISearchInputStyles;
109
110
  Select: ISelectStyles;
110
111
  SelectList: ISelectListStyles;
112
+ Skeleton: ISkeletonStyles;
111
113
  Switch: ISwitchStyles;
112
114
  TextArea: ITextAreaStyles;
113
115
  TextWithInfo: ITextWithInfoStyles;