@astral/ui 4.79.0 → 4.80.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.
- package/components/DataGridInfinite/DataGridInfinite.js +11 -3
- package/components/DataGridInfinite/Row/Row.js +1 -1
- package/components/DataGridInfinite/index.d.ts +1 -1
- package/components/DataGridInfinite/public.d.ts +1 -1
- package/components/DataGridInfinite/styles.d.ts +4 -0
- package/components/DataGridInfinite/styles.js +8 -0
- package/components/DataGridInfinite/types.d.ts +20 -0
- package/components/DataGridInfinite/useLogic/useLogic.d.ts +10 -4
- package/components/DataGridInfinite/useLogic/useLogic.js +52 -3
- package/node/components/DataGridInfinite/DataGridInfinite.js +10 -2
- package/node/components/DataGridInfinite/Row/Row.js +1 -1
- package/node/components/DataGridInfinite/index.d.ts +1 -1
- package/node/components/DataGridInfinite/public.d.ts +1 -1
- package/node/components/DataGridInfinite/styles.d.ts +4 -0
- package/node/components/DataGridInfinite/styles.js +9 -1
- package/node/components/DataGridInfinite/types.d.ts +20 -0
- package/node/components/DataGridInfinite/useLogic/useLogic.d.ts +10 -4
- package/node/components/DataGridInfinite/useLogic/useLogic.js +52 -3
- package/package.json +1 -1
|
@@ -20,7 +20,7 @@ import { ErrorMessage } from './ErrorMessage';
|
|
|
20
20
|
import { List } from './List';
|
|
21
21
|
import { NoData } from './NoData';
|
|
22
22
|
import { Row } from './Row';
|
|
23
|
-
import { Backdrop, DataGridWrapper, DisabledDataGridWrapper, FooterRow, StyledContainer, StyledScrollToTopButton, } from './styles';
|
|
23
|
+
import { Backdrop, DataGridWrapper, DisabledDataGridWrapper, FooterRow, HeaderRow, StyledContainer, StyledScrollToTopButton, } from './styles';
|
|
24
24
|
import { useLogic } from './useLogic';
|
|
25
25
|
const dataGridInfiniteCva = cva(dataGridClassnames.root, {
|
|
26
26
|
variants: {
|
|
@@ -42,7 +42,7 @@ const dataGridInfiniteCva = cva(dataGridClassnames.root, {
|
|
|
42
42
|
});
|
|
43
43
|
export const DataGridInfinite = (props) => {
|
|
44
44
|
const { imagesMap, emptySymbol } = useContext(ConfigContext);
|
|
45
|
-
const { isNoData, isDataGridDisabled, treeRenderConfig, virtuosoProps, headProps, rowProps, scrollToTopButtonProps, isEndReached, isHideHead, containerCssVars, isMobile, mobileSkeletonProps, hasGroupedColumns, gridViewMode, gridColumns, handleSelectRow, mobileDisplayVariant, isSelfContained, } = useLogic(props);
|
|
45
|
+
const { isNoData, isDataGridDisabled, treeRenderConfig, virtuosoProps, headProps, rowProps, scrollToTopButtonProps, isEndReached, isHideHead, containerCssVars, isMobile, mobileSkeletonProps, hasGroupedColumns, gridViewMode, gridColumns, handleSelectRow, mobileDisplayVariant, isSelfContained, isNotRenderHeader, initialTopMostItemIndex, } = useLogic(props);
|
|
46
46
|
const { columns, rows = [], selectedRows = [], sorting, isLoading, isDisabled, keyId, emptyCellValue = emptySymbol, variant = Variant.Tree, subrows, className, isError, endOfScrollMsg, errorMsg, noDataPlaceholder, onRowClick, onSort, onRetry, headPreAddon, activeRowId, isTabularNums, renderRow, } = props;
|
|
47
47
|
const { moreButtonColumnPosition = 1, isVisibleCollapseButton = true } = subrows || {};
|
|
48
48
|
const { isInitialExpanded = false, expandedLevel = EXPANDED_LEVEL_BY_DEFAULT, initialVisibleChildrenCount = INITIAL_OPENED_NESTED_CHILDREN_COUNT_BY_DEFAULT, } = treeRenderConfig || {};
|
|
@@ -58,7 +58,9 @@ export const DataGridInfinite = (props) => {
|
|
|
58
58
|
errorList: [errorMsg || ''],
|
|
59
59
|
imgSrc: imagesMap.defaultErrorImgSrc,
|
|
60
60
|
onRetry,
|
|
61
|
-
}, children: _jsx(Virtuoso, { ...virtuosoProps,
|
|
61
|
+
}, children: _jsx(Virtuoso, { ...virtuosoProps, ...(initialTopMostItemIndex && {
|
|
62
|
+
initialTopMostItemIndex,
|
|
63
|
+
}), style: { height: '100%' }, overscan: OVERSCAN_COUNT, data: rows, components: {
|
|
62
64
|
// biome-ignore lint/suspicious/noTsIgnore: Требует HTMLDivElement, а для элемента списка используем HTMLUListElement (https://github.com/petyosi/react-virtuoso/issues/864)
|
|
63
65
|
// @ts-ignore
|
|
64
66
|
List,
|
|
@@ -70,5 +72,11 @@ export const DataGridInfinite = (props) => {
|
|
|
70
72
|
}
|
|
71
73
|
return (_jsxs(FooterRow, { children: [isLoading && _jsx(Loader, {}), isError && _jsx(ErrorMessage, { onRetry: onRetry }), isEndReached && (_jsx(EndData, { endOfScrollMsg: endOfScrollMsg }))] }));
|
|
72
74
|
},
|
|
75
|
+
Header: () => {
|
|
76
|
+
if (isNotRenderHeader) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
return (_jsxs(HeaderRow, { children: [isLoading && _jsx(Loader, {}), isError && _jsx(ErrorMessage, { onRetry: onRetry })] }));
|
|
80
|
+
},
|
|
73
81
|
} }) }), _jsx(StyledScrollToTopButton, { ...scrollToTopButtonProps, className: dataGridInfiniteClassnames.scrollToTopButton }), isDisabled && _jsx(Backdrop, {})] })] }) }) }));
|
|
74
82
|
};
|
|
@@ -6,5 +6,5 @@ import { useLogic } from './useLogic';
|
|
|
6
6
|
const INITIAL_LEVEL = 0;
|
|
7
7
|
export const Row = ({ children, ...props }) => {
|
|
8
8
|
const { item, memoizedItemProps, onSelectRow, renderRow, flattenColumns, gridColumns, options, keyId, variant, isSelectable, isSelected, nestedChildren, isInitialExpanded, expandedLevel, initialVisibleChildrenCount, moreButtonColumnPosition, isVisibleCollapseButton, emptyCellValue, activeRowId, onRowClick, } = useLogic(props);
|
|
9
|
-
return (_jsx(RowContextProvider, { row: item, itemProps: memoizedItemProps, onSelectRow: onSelectRow, isSelectable: isSelectable, isSelected: isSelected, children: renderRow ? (_jsx(CustomRowWrapper, { index: memoizedItemProps['data-
|
|
9
|
+
return (_jsx(RowContextProvider, { row: item, itemProps: memoizedItemProps, onSelectRow: onSelectRow, isSelectable: isSelectable, isSelected: isSelected, children: renderRow ? (_jsx(CustomRowWrapper, { index: memoizedItemProps['data-index'], renderRow: renderRow, row: item })) : (_jsx(RowComponent, { ...memoizedItemProps, row: item, columns: flattenColumns, gridColumns: gridColumns, options: options, keyId: keyId, variant: variant, isSelectable: isSelectable, isSelected: isSelected, level: INITIAL_LEVEL, nestedChildren: nestedChildren, isInitialExpanded: isInitialExpanded || false, expandedLevel: expandedLevel || 1, initialVisibleChildrenCount: initialVisibleChildrenCount || 2, activeRowId: activeRowId, moreButtonColumnPosition: moreButtonColumnPosition || 1, isVisibleCollapseButton: isVisibleCollapseButton || true, emptyCellValue: emptyCellValue, onRowClick: onRowClick, onSelectRow: onSelectRow })) }));
|
|
10
10
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { DataGridInfinite } from './DataGridInfinite';
|
|
2
|
-
export type { DataGridInfiniteProps } from './types';
|
|
2
|
+
export type { DataGridInfiniteProps, DataGridInfiniteVirtuosoHandle, DataGridInfiniteVirtuosoRef, } from './types';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { DataGridInfinite } from './DataGridInfinite';
|
|
2
|
-
export type { DataGridInfiniteProps } from './types';
|
|
2
|
+
export type { DataGridInfiniteProps, DataGridInfiniteVirtuosoHandle, DataGridInfiniteVirtuosoRef, } from './types';
|
|
@@ -13,6 +13,10 @@ export declare const FooterRow: import("../styled").StyledComponent<{
|
|
|
13
13
|
theme?: import("@emotion/react").Theme | undefined;
|
|
14
14
|
as?: import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements> | undefined;
|
|
15
15
|
}, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLElement>, HTMLElement>, {}>;
|
|
16
|
+
export declare const HeaderRow: import("../styled").StyledComponent<{
|
|
17
|
+
theme?: import("@emotion/react").Theme | undefined;
|
|
18
|
+
as?: import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements> | undefined;
|
|
19
|
+
}, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLElement>, HTMLElement>, {}>;
|
|
16
20
|
export declare const Backdrop: import("../styled").StyledComponent<{
|
|
17
21
|
theme?: import("@emotion/react").Theme | undefined;
|
|
18
22
|
as?: import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements> | undefined;
|
|
@@ -26,6 +26,14 @@ export const FooterRow = styled.footer `
|
|
|
26
26
|
|
|
27
27
|
min-height: 68px;
|
|
28
28
|
`;
|
|
29
|
+
export const HeaderRow = styled.header `
|
|
30
|
+
display: flex;
|
|
31
|
+
flex-shrink: 0;
|
|
32
|
+
align-items: center;
|
|
33
|
+
justify-content: center;
|
|
34
|
+
|
|
35
|
+
min-height: 68px;
|
|
36
|
+
`;
|
|
29
37
|
export const Backdrop = styled.div `
|
|
30
38
|
position: absolute;
|
|
31
39
|
top: 0;
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
+
import { type Ref, type RefObject } from 'react';
|
|
2
|
+
import { type VirtuosoHandle } from 'react-virtuoso';
|
|
1
3
|
import type { DataGridProps, DataGridRow } from '../DataGrid';
|
|
2
4
|
import { type CellValue } from '../DataGrid/types';
|
|
5
|
+
export type DataGridInfiniteVirtuosoHandle = VirtuosoHandle;
|
|
6
|
+
export type DataGridInfiniteVirtuosoRef = RefObject<DataGridInfiniteVirtuosoHandle | null>;
|
|
3
7
|
export type DataGridInfiniteProps<TData extends Record<string, CellValue> = DataGridRow, TSortField extends keyof TData = keyof TData> = Omit<DataGridProps<TData, TSortField>, 'footer' | 'minDisplayRows'> & {
|
|
8
|
+
/**
|
|
9
|
+
* Ref для доступа к методам Virtuoso
|
|
10
|
+
*/
|
|
11
|
+
virtuosoRef?: Ref<DataGridInfiniteVirtuosoHandle>;
|
|
4
12
|
/**
|
|
5
13
|
* Флаг достижения конца списка
|
|
6
14
|
*/
|
|
@@ -17,4 +25,16 @@ export type DataGridInfiniteProps<TData extends Record<string, CellValue> = Data
|
|
|
17
25
|
* Убирает шапку таблицы, когда нет данных
|
|
18
26
|
*/
|
|
19
27
|
isHideHeaderIfNoData?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Обработчик подгрузки данных при скроле до начала списка
|
|
30
|
+
*/
|
|
31
|
+
onStartReached?: () => void;
|
|
32
|
+
/**
|
|
33
|
+
* Флаг достижения начала списка
|
|
34
|
+
*/
|
|
35
|
+
isStartReached?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Индекс первого видимого элемента
|
|
38
|
+
*/
|
|
39
|
+
initialVisibleItemIndex?: number;
|
|
20
40
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { type ChangeEvent } from 'react';
|
|
2
|
-
import type { ListRange
|
|
2
|
+
import type { ListRange } from 'react-virtuoso';
|
|
3
3
|
import { type DataGridRow } from '../../DataGrid';
|
|
4
4
|
import { type CellValue } from '../../DataGrid/types';
|
|
5
|
-
import { type DataGridInfiniteProps } from '../types';
|
|
5
|
+
import { type DataGridInfiniteProps, type DataGridInfiniteVirtuosoHandle } from '../types';
|
|
6
6
|
type UseLogicParams<TData extends Record<string, CellValue> = DataGridRow, TSortField extends keyof TData = keyof TData> = DataGridInfiniteProps<TData, TSortField>;
|
|
7
|
-
export declare const useLogic: <TData extends Record<string, unknown> = DataGridRow, TSortField extends keyof TData = keyof TData>({ keyId, columns, rows, variant, tree, subrows, selectedRows, maxHeight, isLoading, isDisabled, isEndReached, onEndReached, onSelectRow, isHideSelectAll, isHideHeaderIfNoData, gridViewMode: gridViewModeProp, mobileDisplayVariant, }: UseLogicParams<TData, TSortField>) => {
|
|
7
|
+
export declare const useLogic: <TData extends Record<string, unknown> = DataGridRow, TSortField extends keyof TData = keyof TData>({ keyId, columns, rows, variant, tree, subrows, selectedRows, maxHeight, isLoading, isError, isDisabled, isEndReached, onEndReached, onStartReached, isStartReached, onSelectRow, isHideSelectAll, initialVisibleItemIndex, isHideHeaderIfNoData, gridViewMode: gridViewModeProp, mobileDisplayVariant, virtuosoRef, }: UseLogicParams<TData, TSortField>) => {
|
|
8
8
|
containerCssVars: import("react").CSSProperties;
|
|
9
9
|
mobileSkeletonProps: {
|
|
10
10
|
cellsCount: number;
|
|
@@ -24,12 +24,17 @@ export declare const useLogic: <TData extends Record<string, unknown> = DataGrid
|
|
|
24
24
|
hasGroupedColumns: boolean;
|
|
25
25
|
gridViewMode: "standard" | "full";
|
|
26
26
|
gridColumns: string;
|
|
27
|
+
isStartReached: boolean | undefined;
|
|
28
|
+
isNotRenderHeader: boolean | undefined;
|
|
29
|
+
initialTopMostItemIndex: number | undefined;
|
|
27
30
|
virtuosoProps: {
|
|
28
31
|
id: string;
|
|
29
|
-
ref:
|
|
32
|
+
ref: (node: DataGridInfiniteVirtuosoHandle | null) => void;
|
|
30
33
|
endReached: () => void;
|
|
31
34
|
rangeChanged: (range: ListRange) => void;
|
|
32
35
|
totalListHeightChanged: () => void;
|
|
36
|
+
startReached: () => void;
|
|
37
|
+
firstItemIndex: number;
|
|
33
38
|
};
|
|
34
39
|
headProps: {
|
|
35
40
|
rowsCount: number;
|
|
@@ -50,5 +55,6 @@ export declare const useLogic: <TData extends Record<string, unknown> = DataGrid
|
|
|
50
55
|
handleSelectRow: (row: TData) => (event: ChangeEvent<HTMLInputElement>) => void;
|
|
51
56
|
mobileDisplayVariant: "list" | "cards";
|
|
52
57
|
isSelfContained: boolean;
|
|
58
|
+
firstItemIndex: number;
|
|
53
59
|
};
|
|
54
60
|
export {};
|
|
@@ -7,12 +7,45 @@ import { useViewportType } from '../../useViewportType';
|
|
|
7
7
|
import { prop } from '../../utils/prop';
|
|
8
8
|
import { uniqueBy } from '../../utils/uniqueBy';
|
|
9
9
|
import { dataGridInfiniteClassnames, VIRTUOSO_CONTAINER_ID, VIRTUOSO_LIST_ID, } from '../constants';
|
|
10
|
-
export const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selectedRows = [], maxHeight, isLoading, isDisabled, isEndReached, onEndReached, onSelectRow, isHideSelectAll, isHideHeaderIfNoData = false, gridViewMode: gridViewModeProp = 'standard', mobileDisplayVariant = variant === 'subrows' ? 'cards' : 'list', }) => {
|
|
10
|
+
export const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selectedRows = [], maxHeight, isLoading, isError, isDisabled, isEndReached, onEndReached, onStartReached, isStartReached, onSelectRow, isHideSelectAll, initialVisibleItemIndex, isHideHeaderIfNoData = false, gridViewMode: gridViewModeProp = 'standard', mobileDisplayVariant = variant === 'subrows' ? 'cards' : 'list', virtuosoRef, }) => {
|
|
11
11
|
const virtuoso = useRef(null);
|
|
12
|
+
const handleVirtuosoRef = useCallback((node) => {
|
|
13
|
+
virtuoso.current = node;
|
|
14
|
+
if (virtuosoRef) {
|
|
15
|
+
virtuosoRef.current = node;
|
|
16
|
+
}
|
|
17
|
+
}, [virtuosoRef]);
|
|
12
18
|
const scrollToTopButtonRef = useRef(null);
|
|
13
19
|
const { isMobile } = useViewportType();
|
|
14
20
|
// Реф добавлен для избежания замыкания selectedRows в handleSelectRow (иначе в handleSelectRow приходят некорректные значения selectedRows).
|
|
15
21
|
const selectedRowsRef = useRef(selectedRows);
|
|
22
|
+
// Рефы для вычисления firstItemIndex
|
|
23
|
+
// baseIndex - базовый индекс для вычисления firstItemIndex. Делаем очень большим, что бы при подгрузке данных вверх(начало списка) virtuoso ставил им индексы меньше чем этот.
|
|
24
|
+
const baseIndex = useRef(10000);
|
|
25
|
+
const initialLength = useRef(rows.length);
|
|
26
|
+
const firstRowId = useRef(rows[0]?.[keyId]);
|
|
27
|
+
// Вычисляем firstItemIndex для динамической подгрузки данных вверх. Что бы virtuoso понимал что данные сверху еще есть и не надо скролить до верха списка.
|
|
28
|
+
const firstItemIndex = useMemo(() => {
|
|
29
|
+
const currentLength = rows.length;
|
|
30
|
+
const currentFirstId = rows[0]?.[keyId];
|
|
31
|
+
if (currentLength !== initialLength.current) {
|
|
32
|
+
// Если первый элемент изменился и длина увеличилась → подгрузка вверх
|
|
33
|
+
if (currentFirstId !== firstRowId.current &&
|
|
34
|
+
currentLength > initialLength.current) {
|
|
35
|
+
const addedCount = currentLength - initialLength.current;
|
|
36
|
+
baseIndex.current -= addedCount;
|
|
37
|
+
firstRowId.current = currentFirstId;
|
|
38
|
+
}
|
|
39
|
+
else if (currentLength < initialLength.current) {
|
|
40
|
+
baseIndex.current = 10000;
|
|
41
|
+
firstRowId.current = currentFirstId;
|
|
42
|
+
}
|
|
43
|
+
// Если длина увеличилась, но первый элемент не изменился → подгрузка вниз
|
|
44
|
+
// baseIndex не меняем
|
|
45
|
+
initialLength.current = currentLength;
|
|
46
|
+
}
|
|
47
|
+
return baseIndex.current;
|
|
48
|
+
}, [rows.length]);
|
|
16
49
|
useEffect(() => {
|
|
17
50
|
selectedRowsRef.current = selectedRows;
|
|
18
51
|
}, [selectedRows]);
|
|
@@ -43,12 +76,17 @@ export const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selecte
|
|
|
43
76
|
onEndReached();
|
|
44
77
|
}
|
|
45
78
|
}, [isEndReached, onEndReached]);
|
|
79
|
+
const handleStartReach = useCallback(() => {
|
|
80
|
+
if (!isStartReached && onStartReached) {
|
|
81
|
+
onStartReached();
|
|
82
|
+
}
|
|
83
|
+
}, [isStartReached, onStartReached]);
|
|
46
84
|
const uncheckedRowsCount = useMemo(() => {
|
|
47
85
|
return availableRows.filter((row) => !selectedRows.find((selectedRow) => selectedRow[keyId] === row[keyId])).length;
|
|
48
86
|
}, [availableRows, selectedRows, keyId]);
|
|
49
87
|
const handleRangeChanged = useCallback((range) => {
|
|
50
88
|
if (scrollToTopButtonRef.current) {
|
|
51
|
-
scrollToTopButtonRef.current.classList.toggle(dataGridInfiniteClassnames.visibleScrollToTopButton, range.startIndex > 2);
|
|
89
|
+
scrollToTopButtonRef.current.classList.toggle(dataGridInfiniteClassnames.visibleScrollToTopButton, range.startIndex > baseIndex.current + 2);
|
|
52
90
|
}
|
|
53
91
|
}, []);
|
|
54
92
|
const handleSelectAllRows = (event) => {
|
|
@@ -87,6 +125,11 @@ export const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selecte
|
|
|
87
125
|
mobileDisplayVariant,
|
|
88
126
|
});
|
|
89
127
|
const containerCssVars = getDataGridCssVars(maxHeight);
|
|
128
|
+
const foundIndex = rows.findIndex((row) => String(row[keyId]) === String(initialVisibleItemIndex));
|
|
129
|
+
const initialTopMostItemIndex = foundIndex === -1 ? undefined : foundIndex;
|
|
130
|
+
const isNotRenderHeader = (!isLoading && !isError && !isStartReached) ||
|
|
131
|
+
!onStartReached ||
|
|
132
|
+
isStartReached;
|
|
90
133
|
return {
|
|
91
134
|
containerCssVars,
|
|
92
135
|
mobileSkeletonProps,
|
|
@@ -99,12 +142,17 @@ export const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selecte
|
|
|
99
142
|
hasGroupedColumns,
|
|
100
143
|
gridViewMode,
|
|
101
144
|
gridColumns,
|
|
145
|
+
isStartReached,
|
|
146
|
+
isNotRenderHeader,
|
|
147
|
+
initialTopMostItemIndex,
|
|
102
148
|
virtuosoProps: {
|
|
103
149
|
id: VIRTUOSO_CONTAINER_ID,
|
|
104
|
-
ref:
|
|
150
|
+
ref: handleVirtuosoRef,
|
|
105
151
|
endReached: handleEndReach,
|
|
106
152
|
rangeChanged: handleRangeChanged,
|
|
107
153
|
totalListHeightChanged: recalculateVerticalScroll,
|
|
154
|
+
startReached: handleStartReach,
|
|
155
|
+
firstItemIndex,
|
|
108
156
|
},
|
|
109
157
|
headProps: {
|
|
110
158
|
rowsCount: availableRows.length,
|
|
@@ -125,5 +173,6 @@ export const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selecte
|
|
|
125
173
|
handleSelectRow,
|
|
126
174
|
mobileDisplayVariant,
|
|
127
175
|
isSelfContained: mobileDisplayVariant === 'cards',
|
|
176
|
+
firstItemIndex,
|
|
128
177
|
};
|
|
129
178
|
};
|
|
@@ -45,7 +45,7 @@ const dataGridInfiniteCva = (0, cva_1.cva)(constants_2.dataGridClassnames.root,
|
|
|
45
45
|
});
|
|
46
46
|
const DataGridInfinite = (props) => {
|
|
47
47
|
const { imagesMap, emptySymbol } = (0, react_1.useContext)(ConfigProvider_1.ConfigContext);
|
|
48
|
-
const { isNoData, isDataGridDisabled, treeRenderConfig, virtuosoProps, headProps, rowProps, scrollToTopButtonProps, isEndReached, isHideHead, containerCssVars, isMobile, mobileSkeletonProps, hasGroupedColumns, gridViewMode, gridColumns, handleSelectRow, mobileDisplayVariant, isSelfContained, } = (0, useLogic_1.useLogic)(props);
|
|
48
|
+
const { isNoData, isDataGridDisabled, treeRenderConfig, virtuosoProps, headProps, rowProps, scrollToTopButtonProps, isEndReached, isHideHead, containerCssVars, isMobile, mobileSkeletonProps, hasGroupedColumns, gridViewMode, gridColumns, handleSelectRow, mobileDisplayVariant, isSelfContained, isNotRenderHeader, initialTopMostItemIndex, } = (0, useLogic_1.useLogic)(props);
|
|
49
49
|
const { columns, rows = [], selectedRows = [], sorting, isLoading, isDisabled, keyId, emptyCellValue = emptySymbol, variant = enums_1.Variant.Tree, subrows, className, isError, endOfScrollMsg, errorMsg, noDataPlaceholder, onRowClick, onSort, onRetry, headPreAddon, activeRowId, isTabularNums, renderRow, } = props;
|
|
50
50
|
const { moreButtonColumnPosition = 1, isVisibleCollapseButton = true } = subrows || {};
|
|
51
51
|
const { isInitialExpanded = false, expandedLevel = constants_2.EXPANDED_LEVEL_BY_DEFAULT, initialVisibleChildrenCount = constants_2.INITIAL_OPENED_NESTED_CHILDREN_COUNT_BY_DEFAULT, } = treeRenderConfig || {};
|
|
@@ -61,7 +61,9 @@ const DataGridInfinite = (props) => {
|
|
|
61
61
|
errorList: [errorMsg || ''],
|
|
62
62
|
imgSrc: imagesMap.defaultErrorImgSrc,
|
|
63
63
|
onRetry,
|
|
64
|
-
}, children: (0, jsx_runtime_1.jsx)(react_virtuoso_1.Virtuoso, { ...virtuosoProps,
|
|
64
|
+
}, children: (0, jsx_runtime_1.jsx)(react_virtuoso_1.Virtuoso, { ...virtuosoProps, ...(initialTopMostItemIndex && {
|
|
65
|
+
initialTopMostItemIndex,
|
|
66
|
+
}), style: { height: '100%' }, overscan: constants_3.OVERSCAN_COUNT, data: rows, components: {
|
|
65
67
|
// biome-ignore lint/suspicious/noTsIgnore: Требует HTMLDivElement, а для элемента списка используем HTMLUListElement (https://github.com/petyosi/react-virtuoso/issues/864)
|
|
66
68
|
// @ts-ignore
|
|
67
69
|
List: List_1.List,
|
|
@@ -73,6 +75,12 @@ const DataGridInfinite = (props) => {
|
|
|
73
75
|
}
|
|
74
76
|
return ((0, jsx_runtime_1.jsxs)(styles_1.FooterRow, { children: [isLoading && (0, jsx_runtime_1.jsx)(Loader_1.Loader, {}), isError && (0, jsx_runtime_1.jsx)(ErrorMessage_1.ErrorMessage, { onRetry: onRetry }), isEndReached && ((0, jsx_runtime_1.jsx)(EndData_1.EndData, { endOfScrollMsg: endOfScrollMsg }))] }));
|
|
75
77
|
},
|
|
78
|
+
Header: () => {
|
|
79
|
+
if (isNotRenderHeader) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
return ((0, jsx_runtime_1.jsxs)(styles_1.HeaderRow, { children: [isLoading && (0, jsx_runtime_1.jsx)(Loader_1.Loader, {}), isError && (0, jsx_runtime_1.jsx)(ErrorMessage_1.ErrorMessage, { onRetry: onRetry })] }));
|
|
83
|
+
},
|
|
76
84
|
} }) }), (0, jsx_runtime_1.jsx)(styles_1.StyledScrollToTopButton, { ...scrollToTopButtonProps, className: constants_3.dataGridInfiniteClassnames.scrollToTopButton }), isDisabled && (0, jsx_runtime_1.jsx)(styles_1.Backdrop, {})] })] }) }) }));
|
|
77
85
|
};
|
|
78
86
|
exports.DataGridInfinite = DataGridInfinite;
|
|
@@ -9,6 +9,6 @@ const useLogic_1 = require("./useLogic");
|
|
|
9
9
|
const INITIAL_LEVEL = 0;
|
|
10
10
|
const Row = ({ children, ...props }) => {
|
|
11
11
|
const { item, memoizedItemProps, onSelectRow, renderRow, flattenColumns, gridColumns, options, keyId, variant, isSelectable, isSelected, nestedChildren, isInitialExpanded, expandedLevel, initialVisibleChildrenCount, moreButtonColumnPosition, isVisibleCollapseButton, emptyCellValue, activeRowId, onRowClick, } = (0, useLogic_1.useLogic)(props);
|
|
12
|
-
return ((0, jsx_runtime_1.jsx)(RowContext_1.RowContextProvider, { row: item, itemProps: memoizedItemProps, onSelectRow: onSelectRow, isSelectable: isSelectable, isSelected: isSelected, children: renderRow ? ((0, jsx_runtime_1.jsx)(CustomRowWrapper_1.CustomRowWrapper, { index: memoizedItemProps['data-
|
|
12
|
+
return ((0, jsx_runtime_1.jsx)(RowContext_1.RowContextProvider, { row: item, itemProps: memoizedItemProps, onSelectRow: onSelectRow, isSelectable: isSelectable, isSelected: isSelected, children: renderRow ? ((0, jsx_runtime_1.jsx)(CustomRowWrapper_1.CustomRowWrapper, { index: memoizedItemProps['data-index'], renderRow: renderRow, row: item })) : ((0, jsx_runtime_1.jsx)(Row_1.Row, { ...memoizedItemProps, row: item, columns: flattenColumns, gridColumns: gridColumns, options: options, keyId: keyId, variant: variant, isSelectable: isSelectable, isSelected: isSelected, level: INITIAL_LEVEL, nestedChildren: nestedChildren, isInitialExpanded: isInitialExpanded || false, expandedLevel: expandedLevel || 1, initialVisibleChildrenCount: initialVisibleChildrenCount || 2, activeRowId: activeRowId, moreButtonColumnPosition: moreButtonColumnPosition || 1, isVisibleCollapseButton: isVisibleCollapseButton || true, emptyCellValue: emptyCellValue, onRowClick: onRowClick, onSelectRow: onSelectRow })) }));
|
|
13
13
|
};
|
|
14
14
|
exports.Row = Row;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { DataGridInfinite } from './DataGridInfinite';
|
|
2
|
-
export type { DataGridInfiniteProps } from './types';
|
|
2
|
+
export type { DataGridInfiniteProps, DataGridInfiniteVirtuosoHandle, DataGridInfiniteVirtuosoRef, } from './types';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { DataGridInfinite } from './DataGridInfinite';
|
|
2
|
-
export type { DataGridInfiniteProps } from './types';
|
|
2
|
+
export type { DataGridInfiniteProps, DataGridInfiniteVirtuosoHandle, DataGridInfiniteVirtuosoRef, } from './types';
|
|
@@ -13,6 +13,10 @@ export declare const FooterRow: import("@emotion/styled/dist/declarations/src/ty
|
|
|
13
13
|
theme?: import("@emotion/react").Theme | undefined;
|
|
14
14
|
as?: import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements> | undefined;
|
|
15
15
|
}, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLElement>, HTMLElement>, {}>;
|
|
16
|
+
export declare const HeaderRow: import("@emotion/styled/dist/declarations/src/types").StyledComponent<{
|
|
17
|
+
theme?: import("@emotion/react").Theme | undefined;
|
|
18
|
+
as?: import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements> | undefined;
|
|
19
|
+
}, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLElement>, HTMLElement>, {}>;
|
|
16
20
|
export declare const Backdrop: import("@emotion/styled/dist/declarations/src/types").StyledComponent<{
|
|
17
21
|
theme?: import("@emotion/react").Theme | undefined;
|
|
18
22
|
as?: import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements> | undefined;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.StyledContainer = exports.StyledScrollToTopButton = exports.Backdrop = exports.FooterRow = exports.DisabledDataGridWrapper = exports.DataGridWrapper = void 0;
|
|
3
|
+
exports.StyledContainer = exports.StyledScrollToTopButton = exports.Backdrop = exports.HeaderRow = exports.FooterRow = exports.DisabledDataGridWrapper = exports.DataGridWrapper = void 0;
|
|
4
4
|
const styles_1 = require("../DataGrid/styles");
|
|
5
5
|
const ScrollToTopButton_1 = require("../ScrollToTopButton");
|
|
6
6
|
const styled_1 = require("../styled");
|
|
@@ -29,6 +29,14 @@ exports.FooterRow = styled_1.styled.footer `
|
|
|
29
29
|
|
|
30
30
|
min-height: 68px;
|
|
31
31
|
`;
|
|
32
|
+
exports.HeaderRow = styled_1.styled.header `
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-shrink: 0;
|
|
35
|
+
align-items: center;
|
|
36
|
+
justify-content: center;
|
|
37
|
+
|
|
38
|
+
min-height: 68px;
|
|
39
|
+
`;
|
|
32
40
|
exports.Backdrop = styled_1.styled.div `
|
|
33
41
|
position: absolute;
|
|
34
42
|
top: 0;
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
+
import { type Ref, type RefObject } from 'react';
|
|
2
|
+
import { type VirtuosoHandle } from 'react-virtuoso';
|
|
1
3
|
import type { DataGridProps, DataGridRow } from '../DataGrid';
|
|
2
4
|
import { type CellValue } from '../DataGrid/types';
|
|
5
|
+
export type DataGridInfiniteVirtuosoHandle = VirtuosoHandle;
|
|
6
|
+
export type DataGridInfiniteVirtuosoRef = RefObject<DataGridInfiniteVirtuosoHandle | null>;
|
|
3
7
|
export type DataGridInfiniteProps<TData extends Record<string, CellValue> = DataGridRow, TSortField extends keyof TData = keyof TData> = Omit<DataGridProps<TData, TSortField>, 'footer' | 'minDisplayRows'> & {
|
|
8
|
+
/**
|
|
9
|
+
* Ref для доступа к методам Virtuoso
|
|
10
|
+
*/
|
|
11
|
+
virtuosoRef?: Ref<DataGridInfiniteVirtuosoHandle>;
|
|
4
12
|
/**
|
|
5
13
|
* Флаг достижения конца списка
|
|
6
14
|
*/
|
|
@@ -17,4 +25,16 @@ export type DataGridInfiniteProps<TData extends Record<string, CellValue> = Data
|
|
|
17
25
|
* Убирает шапку таблицы, когда нет данных
|
|
18
26
|
*/
|
|
19
27
|
isHideHeaderIfNoData?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Обработчик подгрузки данных при скроле до начала списка
|
|
30
|
+
*/
|
|
31
|
+
onStartReached?: () => void;
|
|
32
|
+
/**
|
|
33
|
+
* Флаг достижения начала списка
|
|
34
|
+
*/
|
|
35
|
+
isStartReached?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Индекс первого видимого элемента
|
|
38
|
+
*/
|
|
39
|
+
initialVisibleItemIndex?: number;
|
|
20
40
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { type ChangeEvent } from 'react';
|
|
2
|
-
import type { ListRange
|
|
2
|
+
import type { ListRange } from 'react-virtuoso';
|
|
3
3
|
import { type DataGridRow } from '../../DataGrid';
|
|
4
4
|
import { type CellValue } from '../../DataGrid/types';
|
|
5
|
-
import { type DataGridInfiniteProps } from '../types';
|
|
5
|
+
import { type DataGridInfiniteProps, type DataGridInfiniteVirtuosoHandle } from '../types';
|
|
6
6
|
type UseLogicParams<TData extends Record<string, CellValue> = DataGridRow, TSortField extends keyof TData = keyof TData> = DataGridInfiniteProps<TData, TSortField>;
|
|
7
|
-
export declare const useLogic: <TData extends Record<string, unknown> = DataGridRow, TSortField extends keyof TData = keyof TData>({ keyId, columns, rows, variant, tree, subrows, selectedRows, maxHeight, isLoading, isDisabled, isEndReached, onEndReached, onSelectRow, isHideSelectAll, isHideHeaderIfNoData, gridViewMode: gridViewModeProp, mobileDisplayVariant, }: UseLogicParams<TData, TSortField>) => {
|
|
7
|
+
export declare const useLogic: <TData extends Record<string, unknown> = DataGridRow, TSortField extends keyof TData = keyof TData>({ keyId, columns, rows, variant, tree, subrows, selectedRows, maxHeight, isLoading, isError, isDisabled, isEndReached, onEndReached, onStartReached, isStartReached, onSelectRow, isHideSelectAll, initialVisibleItemIndex, isHideHeaderIfNoData, gridViewMode: gridViewModeProp, mobileDisplayVariant, virtuosoRef, }: UseLogicParams<TData, TSortField>) => {
|
|
8
8
|
containerCssVars: import("react").CSSProperties;
|
|
9
9
|
mobileSkeletonProps: {
|
|
10
10
|
cellsCount: number;
|
|
@@ -24,12 +24,17 @@ export declare const useLogic: <TData extends Record<string, unknown> = DataGrid
|
|
|
24
24
|
hasGroupedColumns: boolean;
|
|
25
25
|
gridViewMode: "standard" | "full";
|
|
26
26
|
gridColumns: string;
|
|
27
|
+
isStartReached: boolean | undefined;
|
|
28
|
+
isNotRenderHeader: boolean | undefined;
|
|
29
|
+
initialTopMostItemIndex: number | undefined;
|
|
27
30
|
virtuosoProps: {
|
|
28
31
|
id: string;
|
|
29
|
-
ref:
|
|
32
|
+
ref: (node: DataGridInfiniteVirtuosoHandle | null) => void;
|
|
30
33
|
endReached: () => void;
|
|
31
34
|
rangeChanged: (range: ListRange) => void;
|
|
32
35
|
totalListHeightChanged: () => void;
|
|
36
|
+
startReached: () => void;
|
|
37
|
+
firstItemIndex: number;
|
|
33
38
|
};
|
|
34
39
|
headProps: {
|
|
35
40
|
rowsCount: number;
|
|
@@ -50,5 +55,6 @@ export declare const useLogic: <TData extends Record<string, unknown> = DataGrid
|
|
|
50
55
|
handleSelectRow: (row: TData) => (event: ChangeEvent<HTMLInputElement>) => void;
|
|
51
56
|
mobileDisplayVariant: "list" | "cards";
|
|
52
57
|
isSelfContained: boolean;
|
|
58
|
+
firstItemIndex: number;
|
|
53
59
|
};
|
|
54
60
|
export {};
|
|
@@ -10,12 +10,45 @@ const useViewportType_1 = require("../../useViewportType");
|
|
|
10
10
|
const prop_1 = require("../../utils/prop");
|
|
11
11
|
const uniqueBy_1 = require("../../utils/uniqueBy");
|
|
12
12
|
const constants_2 = require("../constants");
|
|
13
|
-
const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selectedRows = [], maxHeight, isLoading, isDisabled, isEndReached, onEndReached, onSelectRow, isHideSelectAll, isHideHeaderIfNoData = false, gridViewMode: gridViewModeProp = 'standard', mobileDisplayVariant = variant === 'subrows' ? 'cards' : 'list', }) => {
|
|
13
|
+
const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selectedRows = [], maxHeight, isLoading, isError, isDisabled, isEndReached, onEndReached, onStartReached, isStartReached, onSelectRow, isHideSelectAll, initialVisibleItemIndex, isHideHeaderIfNoData = false, gridViewMode: gridViewModeProp = 'standard', mobileDisplayVariant = variant === 'subrows' ? 'cards' : 'list', virtuosoRef, }) => {
|
|
14
14
|
const virtuoso = (0, react_1.useRef)(null);
|
|
15
|
+
const handleVirtuosoRef = (0, react_1.useCallback)((node) => {
|
|
16
|
+
virtuoso.current = node;
|
|
17
|
+
if (virtuosoRef) {
|
|
18
|
+
virtuosoRef.current = node;
|
|
19
|
+
}
|
|
20
|
+
}, [virtuosoRef]);
|
|
15
21
|
const scrollToTopButtonRef = (0, react_1.useRef)(null);
|
|
16
22
|
const { isMobile } = (0, useViewportType_1.useViewportType)();
|
|
17
23
|
// Реф добавлен для избежания замыкания selectedRows в handleSelectRow (иначе в handleSelectRow приходят некорректные значения selectedRows).
|
|
18
24
|
const selectedRowsRef = (0, react_1.useRef)(selectedRows);
|
|
25
|
+
// Рефы для вычисления firstItemIndex
|
|
26
|
+
// baseIndex - базовый индекс для вычисления firstItemIndex. Делаем очень большим, что бы при подгрузке данных вверх(начало списка) virtuoso ставил им индексы меньше чем этот.
|
|
27
|
+
const baseIndex = (0, react_1.useRef)(10000);
|
|
28
|
+
const initialLength = (0, react_1.useRef)(rows.length);
|
|
29
|
+
const firstRowId = (0, react_1.useRef)(rows[0]?.[keyId]);
|
|
30
|
+
// Вычисляем firstItemIndex для динамической подгрузки данных вверх. Что бы virtuoso понимал что данные сверху еще есть и не надо скролить до верха списка.
|
|
31
|
+
const firstItemIndex = (0, react_1.useMemo)(() => {
|
|
32
|
+
const currentLength = rows.length;
|
|
33
|
+
const currentFirstId = rows[0]?.[keyId];
|
|
34
|
+
if (currentLength !== initialLength.current) {
|
|
35
|
+
// Если первый элемент изменился и длина увеличилась → подгрузка вверх
|
|
36
|
+
if (currentFirstId !== firstRowId.current &&
|
|
37
|
+
currentLength > initialLength.current) {
|
|
38
|
+
const addedCount = currentLength - initialLength.current;
|
|
39
|
+
baseIndex.current -= addedCount;
|
|
40
|
+
firstRowId.current = currentFirstId;
|
|
41
|
+
}
|
|
42
|
+
else if (currentLength < initialLength.current) {
|
|
43
|
+
baseIndex.current = 10000;
|
|
44
|
+
firstRowId.current = currentFirstId;
|
|
45
|
+
}
|
|
46
|
+
// Если длина увеличилась, но первый элемент не изменился → подгрузка вниз
|
|
47
|
+
// baseIndex не меняем
|
|
48
|
+
initialLength.current = currentLength;
|
|
49
|
+
}
|
|
50
|
+
return baseIndex.current;
|
|
51
|
+
}, [rows.length]);
|
|
19
52
|
(0, react_1.useEffect)(() => {
|
|
20
53
|
selectedRowsRef.current = selectedRows;
|
|
21
54
|
}, [selectedRows]);
|
|
@@ -46,12 +79,17 @@ const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selectedRows =
|
|
|
46
79
|
onEndReached();
|
|
47
80
|
}
|
|
48
81
|
}, [isEndReached, onEndReached]);
|
|
82
|
+
const handleStartReach = (0, react_1.useCallback)(() => {
|
|
83
|
+
if (!isStartReached && onStartReached) {
|
|
84
|
+
onStartReached();
|
|
85
|
+
}
|
|
86
|
+
}, [isStartReached, onStartReached]);
|
|
49
87
|
const uncheckedRowsCount = (0, react_1.useMemo)(() => {
|
|
50
88
|
return availableRows.filter((row) => !selectedRows.find((selectedRow) => selectedRow[keyId] === row[keyId])).length;
|
|
51
89
|
}, [availableRows, selectedRows, keyId]);
|
|
52
90
|
const handleRangeChanged = (0, react_1.useCallback)((range) => {
|
|
53
91
|
if (scrollToTopButtonRef.current) {
|
|
54
|
-
scrollToTopButtonRef.current.classList.toggle(constants_2.dataGridInfiniteClassnames.visibleScrollToTopButton, range.startIndex > 2);
|
|
92
|
+
scrollToTopButtonRef.current.classList.toggle(constants_2.dataGridInfiniteClassnames.visibleScrollToTopButton, range.startIndex > baseIndex.current + 2);
|
|
55
93
|
}
|
|
56
94
|
}, []);
|
|
57
95
|
const handleSelectAllRows = (event) => {
|
|
@@ -90,6 +128,11 @@ const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selectedRows =
|
|
|
90
128
|
mobileDisplayVariant,
|
|
91
129
|
});
|
|
92
130
|
const containerCssVars = (0, utils_1.getDataGridCssVars)(maxHeight);
|
|
131
|
+
const foundIndex = rows.findIndex((row) => String(row[keyId]) === String(initialVisibleItemIndex));
|
|
132
|
+
const initialTopMostItemIndex = foundIndex === -1 ? undefined : foundIndex;
|
|
133
|
+
const isNotRenderHeader = (!isLoading && !isError && !isStartReached) ||
|
|
134
|
+
!onStartReached ||
|
|
135
|
+
isStartReached;
|
|
93
136
|
return {
|
|
94
137
|
containerCssVars,
|
|
95
138
|
mobileSkeletonProps,
|
|
@@ -102,12 +145,17 @@ const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selectedRows =
|
|
|
102
145
|
hasGroupedColumns,
|
|
103
146
|
gridViewMode,
|
|
104
147
|
gridColumns,
|
|
148
|
+
isStartReached,
|
|
149
|
+
isNotRenderHeader,
|
|
150
|
+
initialTopMostItemIndex,
|
|
105
151
|
virtuosoProps: {
|
|
106
152
|
id: constants_2.VIRTUOSO_CONTAINER_ID,
|
|
107
|
-
ref:
|
|
153
|
+
ref: handleVirtuosoRef,
|
|
108
154
|
endReached: handleEndReach,
|
|
109
155
|
rangeChanged: handleRangeChanged,
|
|
110
156
|
totalListHeightChanged: recalculateVerticalScroll,
|
|
157
|
+
startReached: handleStartReach,
|
|
158
|
+
firstItemIndex,
|
|
111
159
|
},
|
|
112
160
|
headProps: {
|
|
113
161
|
rowsCount: availableRows.length,
|
|
@@ -128,6 +176,7 @@ const useLogic = ({ keyId, columns, rows, variant, tree, subrows, selectedRows =
|
|
|
128
176
|
handleSelectRow,
|
|
129
177
|
mobileDisplayVariant,
|
|
130
178
|
isSelfContained: mobileDisplayVariant === 'cards',
|
|
179
|
+
firstItemIndex,
|
|
131
180
|
};
|
|
132
181
|
};
|
|
133
182
|
exports.useLogic = useLogic;
|