diff --git a/src/Components/Containers/DataGrid/InfiniteScrollObserver.tsx b/src/Components/Containers/DataGrid/InfiniteScrollObserver.tsx
new file mode 100644
index 000000000..75357da4f
--- /dev/null
+++ b/src/Components/Containers/DataGrid/InfiniteScrollObserver.tsx
@@ -0,0 +1,69 @@
+import { useCallback, useEffect, useRef, useState } from 'react';
+import { style } from 'typestyle';
+interface IProps {
+ children?: any;
+ upDatagridHeight?: string;
+ borderRadius?: string;
+ borderColor?: string;
+ onScrollStop: (page?: number, take?: number, skip?: number) => void;
+}
+
+export const InfiniteScrollObserver = (props: IProps) => {
+ const { onScrollStop, borderColor, borderRadius, children, upDatagridHeight } = props;
+ const [pageNum, setPageNum] = useState(1);
+ const loader = useRef(null);
+
+ const handleObserver = useCallback(entries => {
+ const target = entries[0];
+ if (target.isIntersecting) {
+ setPageNum(prev => prev + 1);
+ onScrollStop(pageNum);
+ }
+ }, []);
+ useEffect(() => {
+ const option = {
+ root: null,
+ rootMargin: '20px',
+ threshold: 0,
+ };
+ const observer = new IntersectionObserver(handleObserver, option);
+ if (loader.current) observer.observe(loader.current);
+ }, [handleObserver]);
+
+ return (
+
+ );
+};
+
+const scrollableDataGridStyle = (upDatagridHeight = '700px', borderRadius?: string, borderColor?: string): string =>
+ style({
+ border: 'solid 1px',
+ borderColor: borderColor,
+ width: '100%',
+ borderRadius: borderRadius,
+ overflowY: 'auto',
+ maxHeight: upDatagridHeight,
+ position: 'sticky',
+ top: 0,
+ $nest: {
+ table: {
+ borderCollapse: 'collapse',
+ },
+ thead: {
+ position: 'sticky',
+ top: 0,
+ zIndex: 1000,
+ },
+ tbody: {
+ position: 'sticky',
+ top: 0,
+ },
+ th: {
+ position: 'sticky',
+ top: 0,
+ },
+ },
+ });
diff --git a/src/Components/Containers/DataGrid/UpDataGrid.tsx b/src/Components/Containers/DataGrid/UpDataGrid.tsx
index 2fd79e992..163865100 100644
--- a/src/Components/Containers/DataGrid/UpDataGrid.tsx
+++ b/src/Components/Containers/DataGrid/UpDataGrid.tsx
@@ -27,6 +27,7 @@ import { getTestableComponentProps, TestableComponentProps } from '../../../Comm
import { DeviceSmartphones } from '../../../Common/utils/device';
import { IconName } from '../../../Common/theming/icons';
import { isEmpty } from '../../../Common/utils';
+import { InfiniteScrollObserver } from './InfiniteScrollObserver';
const WrapperDataGridStyle = style(
{
@@ -291,6 +292,9 @@ export interface UpDataGridProps extends TestableComponentProps, WithThemeProps
injectRow?: (previous: any, next: any, colum: Column[]) => JSX.Element;
// Event Handler
onSortChange?: (c: Column, dir: SortDirection) => void;
+ onScrollStop?: (page: number, take: number, skip: number) => void;
+ upDatagridHeight?: string;
+ loadOnScroll?: boolean;
onSelectionChange?: (
lastUpdatedRow: Row,
dataSelected: any[],
@@ -317,6 +321,8 @@ export interface UpDataGridState {
total?: number;
isDataFetching?: boolean;
allRowsSelected?: boolean;
+ currentPage?: number;
+ refershData?: boolean;
rowsSelected?: Array;
lastFetchedDataTime?: Date;
data?: any;
@@ -403,6 +409,8 @@ class UpDataGrid extends React.Component {
+ const totalPages = Math.ceil(this.state.total / this.state.take);
+ if (this.state.currentPage < totalPages) {
+ if (this.props.onScrollStop) this.props.onScrollStop(page, take, skip);
+ this.setState(
+ { currentPage: this.state.currentPage + 1, take, skip, isDataFetching: true, refershData: false },
+ () => {
+ if (this.props.dataSource !== undefined) {
+ this.fetchData();
+ }
+ }
+ );
+ }
+ };
+
onPageChange = (page: number, take: number, skip: number): void => {
if (this.props.paginationProps.onPageChange) this.props.paginationProps.onPageChange(page, take, skip);
@@ -664,7 +690,7 @@ class UpDataGrid extends React.Component {
+ this.setState({ columns: columns, currentPage: 1, refershData: true }, () => {
if (this.props.dataSource != undefined) {
this.fetchData();
}
@@ -718,6 +744,7 @@ class UpDataGrid extends React.Component
@@ -851,7 +878,34 @@ class UpDataGrid extends React.Component
- <>
+ {this.props.loadOnScroll ? (
+
+ {
+ this.refTable = r;
+ }}
+ className={classnames('up-data-grid-main', DataGridStyle(this.props))}
+ >
+
+ {rows}
+
+
+ ) : (
{
this.refTable = r;
@@ -871,7 +925,7 @@ class UpDataGrid extends React.Component
{rows}
- >
+ )}
Array;
/** Affihage du nombre de résultats */
@@ -320,7 +322,7 @@ class UpPagination extends React.Component
);
}
- return (
+ const paginationNumbers = !this.props.loadOnScroll ? (
{value}
+ ) : (
+
+ e.preventDefault()} href="#">
+ {value}
+
+
);
+
+ return paginationNumbers;
});
pageNumberNavigation = (
);
diff --git a/src/Components/Containers/DataGrid/index.stories.tsx b/src/Components/Containers/DataGrid/index.stories.tsx
index fd8f606a4..3c7a6589e 100644
--- a/src/Components/Containers/DataGrid/index.stories.tsx
+++ b/src/Components/Containers/DataGrid/index.stories.tsx
@@ -333,6 +333,172 @@ export const WithActions = (): JSX.Element => {
);
};
+export const WithLoadOnScrollEnabled = (): JSX.Element => {
+ const [isFetching, setIsFetching] = React.useState(false);
+ const [currentPage, setPage] = React.useState(1);
+ const [state, setState] = React.useState<{
+ data: Array;
+ total: number;
+ lastFetchedDataTime: Date | undefined;
+ previousFetchedPage: number;
+ }>({
+ data: [],
+ total: 0,
+ lastFetchedDataTime: undefined,
+ previousFetchedPage: 0,
+ });
+
+ const [previousPage, setPreviousPage] = React.useState(0);
+ const [currentAllRowsSelected, setAllRowsSelected] = React.useState>([]);
+ const [isAllRowsSelected, setIsAllRowsSelected] = React.useState(false);
+
+ const fetchData = (): Promise => {
+ setIsFetching(true);
+ setState({ ...state, data: [] });
+ return fetch('https://jsonplaceholder.typicode.com/posts')
+ .then(response => response.json())
+ .then(data => {
+ setState({
+ data: data.slice((currentPage - 1) * 50, (currentPage - 1) * 50 + 50),
+ total: data.length,
+ previousFetchedPage: previousPage,
+ lastFetchedDataTime: new Date(),
+ });
+
+ if (isAllRowsSelected === true && currentPage != previousPage) {
+ const newSelectedData = dataSelectedToRows(data, currentAllRowsSelected, isAllRowsSelected);
+ setAllRowsSelected(newSelectedData);
+ }
+
+ setPreviousPage(currentPage);
+
+ return data;
+ })
+ .then(data => {
+ setIsFetching(false);
+ return data;
+ });
+ };
+
+ React.useEffect(() => {
+ fetchData();
+ }, [currentPage, setPage]);
+
+ const dataSelectedToRows = (data: any[], allRowsSelected: Row[], isAllRowsSelected: boolean): Array => {
+ const newAllRowsSelected: Row[] = _.clone(allRowsSelected);
+ data.forEach(item => {
+ const matchedRow = newAllRowsSelected.find(row => row.value.id == item.id);
+ if (matchedRow == null) {
+ newAllRowsSelected.push({
+ isSelected: isAllRowsSelected,
+ value: item,
+ });
+ } else {
+ matchedRow.isSelected = isAllRowsSelected;
+ }
+ });
+
+ return newAllRowsSelected;
+ };
+
+ const updateCurrentAllRowsSelected = (updatedRow: Row, currentAllRowsSelected: Row[]): Array => {
+ const newSelectedData: Row[] = _.clone(currentAllRowsSelected);
+ const matchedRow = newSelectedData.find(row => row.value.id == updatedRow.value.id);
+ if (matchedRow == null) {
+ newSelectedData.push({ ...updatedRow });
+ } else {
+ matchedRow.isSelected = isAllRowsSelected;
+ }
+ return newSelectedData;
+ };
+
+ const onSelectionChange = (
+ lastUpdatedRow: Row,
+ dataSelected: any[],
+ allRowsSelected?: Row[],
+ isAllRowsSelected?: boolean
+ ): void => {
+ let newSelectedData: Row[] = [];
+
+ if (lastUpdatedRow != null) {
+ newSelectedData = updateCurrentAllRowsSelected(lastUpdatedRow, currentAllRowsSelected);
+ } else if (isAllRowsSelected != null) {
+ newSelectedData = dataSelectedToRows(state.data, currentAllRowsSelected, isAllRowsSelected);
+ }
+
+ if (newSelectedData != null) setAllRowsSelected(newSelectedData.filter(row => row.isSelected));
+
+ setIsAllRowsSelected(isAllRowsSelected === true);
+ };
+
+ return (
+ <>
+ {
+ setAllRowsSelected([]);
+ fetchData();
+ }}
+ >
+ Rafraichir
+
+ {isFetching && }
+ {
+ setPreviousPage(currentPage);
+ setPage(page);
+ },
+ }}
+ onScrollStop={(page: number, take: number, skip: number): void => {
+ setPreviousPage(currentPage);
+ setPage(currentPage + 1);
+ }}
+ onSortChange={(c: Column, dir: SortDirection): void => {
+ setPreviousPage(1);
+ setPage(1);
+ }}
+ isSelectionEnabled={true}
+ isPaginationEnabled={true}
+ loadOnScroll={true}
+ columns={[
+ {
+ label: 'Id',
+ field: 'id',
+ isSortable: true,
+ },
+ {
+ label: 'Titre',
+ field: 'title',
+ isSortable: true,
+ },
+ {
+ label: 'Texte',
+ field: 'body',
+ isSortable: true,
+ },
+ {
+ label: 'Auteur',
+ field: 'userId',
+ isSortable: true,
+ },
+ ]}
+ />
+ >
+ );
+};
+
export const WithSingleActionAndRowClickable = (): JSX.Element => {
const [currentRow, setCurrentRow] = React.useState({});