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 fa13796cc..433bec18a 100644
--- a/src/Components/Containers/DataGrid/UpDataGrid.tsx
+++ b/src/Components/Containers/DataGrid/UpDataGrid.tsx
@@ -1,31 +1,32 @@
import * as React from 'react';
-import $ from 'jquery';
import classnames from 'classnames';
+import $ from 'jquery';
import { media, style } from 'typestyle';
import axios from 'axios';
-import UpPagination, { UpPaginationProps } from './UpPagination';
-import UpDataGridRowHeader from './UpDataGridRowHeader';
import UpDataGridRow, { ActionFactory } from './UpDataGridRow';
+import UpDataGridRowHeader from './UpDataGridRowHeader';
import { ICellFormatter } from './UpDefaultCellFormatter';
+import UpPagination, { UpPaginationProps } from './UpPagination';
import UpLoadingIndicator from '../../Display/LoadingIndicator';
import UpButton from '../../Inputs/Button/UpButton';
-import { IntentType, WithThemeProps } from '../../../Common/theming/types';
import { ActionType } from '../../../Common/actions';
import UpDefaultTheme, { withTheme } from '../../../Common/theming';
+import { IntentType, WithThemeProps } from '../../../Common/theming/types';
import UpDataGridFooter, { UpDataGridFooterProps } from './UpDataGridFooter';
import UpDataGridHeader, { UpDataGridHeaderProps } from './UpDataGridHeader';
-import { UpDataGridProvider } from './UpDataGridContext';
-import { getTestableComponentProps, TestableComponentProps } from '../../../Common/utils/types';
-import { DeviceSmartphones } from '../../../Common/utils/device';
import { IconName } from '../../../Common/theming/icons';
import { isEmpty } from '../../../Common/utils';
import { DetailsData, DetailsType } from './UpDataGridDetails';
+import { DeviceSmartphones } from '../../../Common/utils/device';
+import { getTestableComponentProps, TestableComponentProps } from '../../../Common/utils/types';
+import { InfiniteScrollObserver } from './InfiniteScrollObserver';
+import { UpDataGridProvider } from './UpDataGridContext';
const WrapperDataGridStyle = style(
{
@@ -296,6 +297,9 @@ export interface UpDataGridProps extends TestableComponentProps {
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[],
@@ -325,6 +329,8 @@ export interface UpDataGridState {
rowsSelected?: Array;
lastFetchedDataTime?: Date;
data?: any;
+ currentPage?: number;
+ refershData?: boolean;
}
export type SortDirection = 'ASC' | 'DESC';
@@ -412,6 +418,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);
@@ -673,7 +699,7 @@ class UpDataGrid extends React.Component {
+ this.setState({ columns: columns, currentPage: 1, refershData: true }, () => {
if (this.props.dataSource != undefined) {
this.fetchData();
}
@@ -727,6 +753,7 @@ class UpDataGrid extends React.Component
@@ -861,7 +888,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;
@@ -881,7 +935,7 @@ class UpDataGrid extends React.Component
{rows}
- >
+ )}
Array;
/** Affihage du nombre de résultats */
@@ -320,7 +322,7 @@ class UpPagination extends React.Component
+ ) : (
+
+ 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 795f0d1e1..05dbbd6b5 100644
--- a/src/Components/Containers/DataGrid/index.stories.tsx
+++ b/src/Components/Containers/DataGrid/index.stories.tsx
@@ -4,7 +4,7 @@ import classnames from 'classnames';
import UpDefaultTheme, { UpThemeInterface } from '../../../Common/theming';
import { WithThemeProps } from '../../../Common/theming/types';
-import UpDataGrid, { Action, Row } from './UpDataGrid';
+import UpDataGrid, { Action, Column, Row, SortDirection } from './UpDataGrid';
import { getRootContainer } from '../../../Common/stories';
import { withKnobs } from '@storybook/addon-knobs';
@@ -537,6 +537,172 @@ export const WithAutoClearSelectionOnDataChanged = (): 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 WithExternalSource = (): JSX.Element => (