@true-engineering/true-react-common-ui-kit 4.0.0-alpha24 → 4.0.0-alpha26
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/FlexibleTable/FlexibleTable.d.ts +1 -1
- package/dist/components/FlexibleTable/types.d.ts +12 -4
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/use-intersection-ref.d.ts +8 -0
- package/dist/true-react-common-ui-kit.js +195 -114
- package/dist/true-react-common-ui-kit.js.map +1 -1
- package/dist/true-react-common-ui-kit.umd.cjs +195 -114
- package/dist/true-react-common-ui-kit.umd.cjs.map +1 -1
- package/package.json +1 -1
- package/src/components/FlexibleTable/FlexibleTable.tsx +27 -51
- package/src/components/FlexibleTable/types.ts +12 -4
- package/src/components/MultiSelectList/MultiSelectList.tsx +15 -11
- package/src/components/WithPopup/WithPopup.tsx +3 -5
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-intersection-ref.ts +30 -0
package/package.json
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { ReactNode, RefObject, useCallback, useEffect, useMemo, useRef } from 'react';
|
|
2
2
|
import clsx from 'clsx';
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
addDataAttributes,
|
|
5
5
|
applyAction,
|
|
6
|
+
getTestId,
|
|
6
7
|
indexMap,
|
|
7
8
|
isArrayNotEmpty,
|
|
8
9
|
isEmpty,
|
|
9
10
|
isNotEmpty,
|
|
11
|
+
isReactNodeNotEmpty,
|
|
10
12
|
} from '@true-engineering/true-react-platform-helpers';
|
|
11
|
-
import {
|
|
12
|
-
import { useMergedRefs, useTweakStyles } from '../../hooks';
|
|
13
|
+
import { useIntersectionRef, useMergedRefs, useTweakStyles } from '../../hooks';
|
|
13
14
|
import { ICommonProps } from '../../types';
|
|
14
15
|
import { ThemedPreloader } from '../ThemedPreloader';
|
|
15
16
|
import { FlexibleTableRow, IFlexibleTableRowProps } from './components';
|
|
@@ -81,7 +82,7 @@ export function FlexibleTable<
|
|
|
81
82
|
isFirstColumnSticky = false,
|
|
82
83
|
isHorizontallyScrollable = false,
|
|
83
84
|
isRowFocusable = false,
|
|
84
|
-
infinityScrollConfig,
|
|
85
|
+
infinityScrollConfig: infinityScrollConfigDeprecated,
|
|
85
86
|
renderMode = 'table',
|
|
86
87
|
refForScroll,
|
|
87
88
|
nothingFoundContent,
|
|
@@ -101,13 +102,10 @@ export function FlexibleTable<
|
|
|
101
102
|
currentComponentName: 'FlexibleTable',
|
|
102
103
|
});
|
|
103
104
|
|
|
104
|
-
const observer = useRef<IntersectionObserver>();
|
|
105
105
|
const scrollRef = useRef<HTMLDivElement>(null);
|
|
106
106
|
|
|
107
107
|
const columns = useMemo(() => enabledColumns ?? Object.keys(config), [enabledColumns, config]);
|
|
108
108
|
|
|
109
|
-
const hasInfiniteScroll = isNotEmpty(infinityScrollConfig);
|
|
110
|
-
|
|
111
109
|
const getTableRowProps = (
|
|
112
110
|
item: Row,
|
|
113
111
|
index: number,
|
|
@@ -148,12 +146,6 @@ export function FlexibleTable<
|
|
|
148
146
|
[getDataScrollAttributeSetter],
|
|
149
147
|
);
|
|
150
148
|
|
|
151
|
-
const shouldShowNothingFound =
|
|
152
|
-
!isArrayNotEmpty(content) &&
|
|
153
|
-
nothingFoundContent !== undefined &&
|
|
154
|
-
!infinityScrollConfig?.isLoading &&
|
|
155
|
-
(infinityScrollConfig?.isLastPage === undefined || infinityScrollConfig.isLastPage);
|
|
156
|
-
|
|
157
149
|
const ref = useMergedRefs([
|
|
158
150
|
refForScroll,
|
|
159
151
|
scrollRef,
|
|
@@ -161,37 +153,25 @@ export function FlexibleTable<
|
|
|
161
153
|
setIsScrolledAttribute,
|
|
162
154
|
]);
|
|
163
155
|
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
observer.current = new IntersectionObserver((entries) => {
|
|
179
|
-
if (entries[0].isIntersecting) {
|
|
180
|
-
infinityScrollConfig.onInfinityScroll(infinityScrollConfig.activePage + 1);
|
|
181
|
-
}
|
|
182
|
-
});
|
|
156
|
+
const infinityScrollConfig = infinityScrollConfigDeprecated && {
|
|
157
|
+
isLoading: infinityScrollConfigDeprecated.isLoading,
|
|
158
|
+
onInfinityScroll: () =>
|
|
159
|
+
infinityScrollConfigDeprecated.onInfinityScroll(
|
|
160
|
+
(infinityScrollConfigDeprecated.activePage ?? 0) + 1,
|
|
161
|
+
),
|
|
162
|
+
isEnabled:
|
|
163
|
+
infinityScrollConfigDeprecated.isEnabled ?? !infinityScrollConfigDeprecated.isLastPage,
|
|
164
|
+
};
|
|
165
|
+
const intersectionRef = useIntersectionRef({
|
|
166
|
+
isDisabled: !infinityScrollConfig?.isEnabled || infinityScrollConfig.isLoading,
|
|
167
|
+
onIntersection: infinityScrollConfig?.onInfinityScroll,
|
|
168
|
+
});
|
|
183
169
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
hasInfiniteScroll,
|
|
190
|
-
infinityScrollConfig?.activePage,
|
|
191
|
-
infinityScrollConfig?.totalPages,
|
|
192
|
-
infinityScrollConfig?.onInfinityScroll,
|
|
193
|
-
],
|
|
194
|
-
);
|
|
170
|
+
const shouldShowNothingFound =
|
|
171
|
+
isReactNodeNotEmpty(nothingFoundContent) &&
|
|
172
|
+
!isArrayNotEmpty(content) &&
|
|
173
|
+
!infinityScrollConfig?.isLoading &&
|
|
174
|
+
!infinityScrollConfig?.isEnabled;
|
|
195
175
|
|
|
196
176
|
useEffect(() => {
|
|
197
177
|
const scrollContainer = scrollRef.current;
|
|
@@ -222,11 +202,7 @@ export function FlexibleTable<
|
|
|
222
202
|
|
|
223
203
|
return (
|
|
224
204
|
<div ref={ref} className={clsx({ [classes.scroll]: isHorizontallyScrollable })}>
|
|
225
|
-
<Table.Root
|
|
226
|
-
className={classes.root}
|
|
227
|
-
{...addDataTestId(testId)}
|
|
228
|
-
{...addDataAttributes({ ...data, isLoading })}
|
|
229
|
-
>
|
|
205
|
+
<Table.Root className={classes.root} {...addDataAttributes({ ...data, isLoading }, testId)}>
|
|
230
206
|
{shouldRenderHeader && (
|
|
231
207
|
<Table.Head className={classes.head}>
|
|
232
208
|
<Table.Row className={classes.headerRow}>
|
|
@@ -291,11 +267,11 @@ export function FlexibleTable<
|
|
|
291
267
|
/>
|
|
292
268
|
))}
|
|
293
269
|
|
|
294
|
-
{
|
|
270
|
+
{infinityScrollConfig?.isEnabled && (
|
|
295
271
|
<Table.Row className={classes.loaderRow}>
|
|
296
272
|
<Table.Cell className={classes.loaderCell} colSpan={columns.length}>
|
|
297
|
-
<div ref={
|
|
298
|
-
<ThemedPreloader type="dots" />
|
|
273
|
+
<div ref={intersectionRef} className={classes.loader}>
|
|
274
|
+
<ThemedPreloader type="dots" testId={getTestId(testId, 'loader')} />
|
|
299
275
|
</div>
|
|
300
276
|
</Table.Cell>
|
|
301
277
|
</Table.Row>
|
|
@@ -64,11 +64,19 @@ export type IFlexibleTableConfigType<
|
|
|
64
64
|
};
|
|
65
65
|
|
|
66
66
|
export interface IInfinityScrollConfig {
|
|
67
|
-
|
|
68
|
-
totalPages: number;
|
|
67
|
+
isEnabled?: boolean;
|
|
69
68
|
isLoading: boolean;
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
onInfinityScroll: {
|
|
70
|
+
(_: never): void;
|
|
71
|
+
/** @deprecated use activePage directly */
|
|
72
|
+
(skip: number): void; // eslint-disable-line @typescript-eslint/unified-signatures
|
|
73
|
+
};
|
|
74
|
+
/** @deprecated use activePage in onInfinityScroll */
|
|
75
|
+
activePage?: number;
|
|
76
|
+
/** @deprecated use isEnabled */
|
|
77
|
+
isLastPage?: boolean;
|
|
78
|
+
/** @deprecated use isEnabled */
|
|
79
|
+
totalPages?: number;
|
|
72
80
|
}
|
|
73
81
|
|
|
74
82
|
export interface INestedComponent<T extends PropertyKey = string> {
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { useEffect, useState, useMemo, useRef, useCallback, ReactNode } from 'react';
|
|
2
2
|
import clsx from 'clsx';
|
|
3
3
|
import { debounce } from 'ts-debounce';
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import {
|
|
5
|
+
addDataAttributes,
|
|
6
|
+
addDataTestId,
|
|
7
|
+
getTestId,
|
|
8
|
+
isArrayNotEmpty,
|
|
9
|
+
isNotEmpty,
|
|
10
|
+
} from '@true-engineering/true-react-platform-helpers';
|
|
6
11
|
import { useIsMounted, useTweakStyles } from '../../hooks';
|
|
7
12
|
import { ICommonProps } from '../../types';
|
|
8
13
|
import { Button } from '../Button';
|
|
@@ -330,7 +335,7 @@ export function MultiSelectList<Value extends IMultiSelectListValues<Option>, Op
|
|
|
330
335
|
!isLoading && (isArrayNotEmpty(allOptions) || isArrayNotEmpty(chosenValues));
|
|
331
336
|
|
|
332
337
|
return (
|
|
333
|
-
<div className={classes.root} {...addDataAttributes(data)}>
|
|
338
|
+
<div className={classes.root} {...addDataAttributes(data, testId)}>
|
|
334
339
|
{isSearchEnabled && (
|
|
335
340
|
<div className={classes.dropdownInput}>
|
|
336
341
|
<SearchInput
|
|
@@ -339,16 +344,13 @@ export function MultiSelectList<Value extends IMultiSelectListValues<Option>, Op
|
|
|
339
344
|
onChange={handleOnChange}
|
|
340
345
|
tweakStyles={tweakSearchInputStyles}
|
|
341
346
|
maxLength={searchMaxLength}
|
|
342
|
-
testId={testId
|
|
347
|
+
testId={getTestId(testId, 'search')}
|
|
343
348
|
shouldFocusOnMount
|
|
344
349
|
/>
|
|
345
350
|
</div>
|
|
346
351
|
)}
|
|
347
352
|
{shouldShowOptionsList && (
|
|
348
|
-
<div
|
|
349
|
-
className={classes.list}
|
|
350
|
-
data-testid={testId !== undefined ? `${testId}-list` : undefined}
|
|
351
|
-
>
|
|
353
|
+
<div className={classes.list} {...addDataTestId(testId, 'list')}>
|
|
352
354
|
{/* Выбранные */}
|
|
353
355
|
{hasSelectedOptionsGroup && (
|
|
354
356
|
<>
|
|
@@ -379,8 +381,9 @@ export function MultiSelectList<Value extends IMultiSelectListValues<Option>, Op
|
|
|
379
381
|
value={val}
|
|
380
382
|
tweakStyles={tweakCheckboxStyles}
|
|
381
383
|
labelPosition={checkboxPosition === 'left' ? 'right' : 'left'}
|
|
384
|
+
data={{ id }}
|
|
382
385
|
>
|
|
383
|
-
<div className={classes.option}
|
|
386
|
+
<div className={classes.option} {...addDataAttributes({ option: id })}>
|
|
384
387
|
{view}
|
|
385
388
|
</div>
|
|
386
389
|
</Checkbox>
|
|
@@ -417,8 +420,9 @@ export function MultiSelectList<Value extends IMultiSelectListValues<Option>, Op
|
|
|
417
420
|
value={val}
|
|
418
421
|
tweakStyles={tweakCheckboxStyles}
|
|
419
422
|
labelPosition={checkboxPosition === 'left' ? 'right' : 'left'}
|
|
423
|
+
data={{ id }}
|
|
420
424
|
>
|
|
421
|
-
<div className={classes.option}
|
|
425
|
+
<div className={classes.option} {...addDataAttributes({ option: id })}>
|
|
422
426
|
{view}
|
|
423
427
|
</div>
|
|
424
428
|
</Checkbox>
|
|
@@ -449,7 +453,7 @@ export function MultiSelectList<Value extends IMultiSelectListValues<Option>, Op
|
|
|
449
453
|
onClick={handleClear}
|
|
450
454
|
size="s"
|
|
451
455
|
view="text"
|
|
452
|
-
testId={testId
|
|
456
|
+
testId={getTestId(testId, 'clear-button')}
|
|
453
457
|
tweakStyles={tweakClearButtonStyles}
|
|
454
458
|
>
|
|
455
459
|
{translates.clear}
|
|
@@ -121,10 +121,8 @@ export const WithPopup: FC<IWithPopupProps> = ({
|
|
|
121
121
|
|
|
122
122
|
const handleToggle = (isActive: boolean, event?: IWithPopupToggleEvent) => {
|
|
123
123
|
event?.stopPropagation();
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
setIsOpen(isActive);
|
|
127
|
-
}
|
|
124
|
+
onToggle?.(isActive, event);
|
|
125
|
+
setIsOpen(isActive);
|
|
128
126
|
};
|
|
129
127
|
|
|
130
128
|
const handleClose = (event?: IWithPopupToggleEvent) => {
|
|
@@ -132,7 +130,7 @@ export const WithPopup: FC<IWithPopupProps> = ({
|
|
|
132
130
|
};
|
|
133
131
|
|
|
134
132
|
const { refs, floatingStyles, context } = useFloating({
|
|
135
|
-
open: isOpen,
|
|
133
|
+
open: isOpen && !isDisabled,
|
|
136
134
|
middleware: [
|
|
137
135
|
offset(popupOffset),
|
|
138
136
|
canBeFlipped && flip({ fallbackAxisSideDirection: 'start' }),
|
package/src/hooks/index.ts
CHANGED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useRef, useMemo, RefCallback } from 'react';
|
|
2
|
+
import { isNotEmpty } from '@true-engineering/true-react-platform-helpers';
|
|
3
|
+
|
|
4
|
+
export interface IInsertionRefOptions {
|
|
5
|
+
/** @default false */
|
|
6
|
+
isDisabled?: boolean;
|
|
7
|
+
onIntersection?: VoidFunction;
|
|
8
|
+
onIntersectionEnd?: VoidFunction;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const useIntersectionRef = (options?: IInsertionRefOptions): RefCallback<Element> => {
|
|
12
|
+
const optionsRef = useRef(options);
|
|
13
|
+
optionsRef.current = options;
|
|
14
|
+
|
|
15
|
+
return useMemo(() => {
|
|
16
|
+
const observer = new IntersectionObserver(([{ isIntersecting }]) => {
|
|
17
|
+
const { current } = optionsRef;
|
|
18
|
+
if (current?.isDisabled) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (isIntersecting) {
|
|
22
|
+
current?.onIntersection?.();
|
|
23
|
+
} else {
|
|
24
|
+
current?.onIntersectionEnd?.();
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return (node) => (isNotEmpty(node) ? observer.observe(node) : observer.disconnect());
|
|
29
|
+
}, []);
|
|
30
|
+
};
|