History pagination (#58)

Adds pagination for the history page. fixes #32
This commit is contained in:
mrdrogdrog 2020-05-24 15:27:16 +02:00 committed by GitHub
parent 70ab02431c
commit 11f01094b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 185 additions and 37 deletions

View file

@ -1,10 +1,14 @@
import React, {Fragment} from 'react' import React from 'react'
import {HistoryEntriesProps} from "../history-content/history-content"; import {HistoryEntriesProps} from "../history-content/history-content";
import {HistoryCard} from "./history-card"; import {HistoryCard} from "./history-card";
import {Pager} from '../../../../pagination/pager';
import {Row} from 'react-bootstrap';
export const HistoryCardList: React.FC<HistoryEntriesProps> = ({entries, onPinClick, pageIndex, onLastPageIndexChange}) => {
export const HistoryCardList: React.FC<HistoryEntriesProps> = ({entries, onPinClick}) => {
return ( return (
<Fragment> <Row className="justify-content-center">
<Pager numberOfElementsPerPage={6} pageIndex={pageIndex} onLastPageIndexChange={onLastPageIndexChange}>
{ {
entries.map((entry) => ( entries.map((entry) => (
<HistoryCard <HistoryCard
@ -13,6 +17,7 @@ export const HistoryCardList: React.FC<HistoryEntriesProps> = ({entries, onPinCl
onPinClick={onPinClick} onPinClick={onPinClick}
/>)) />))
} }
</Fragment> </Pager>
) </Row>
);
} }

View file

@ -1,10 +1,11 @@
import React from "react"; import React, {Fragment, useState} from "react";
import {HistoryEntry, pinClick} from "../history"; import {HistoryEntry, pinClick} from "../history";
import {HistoryTable} from "../history-table/history-table"; import {HistoryTable} from "../history-table/history-table";
import {Alert} from "react-bootstrap"; import {Alert, Row} from "react-bootstrap";
import {Trans} from "react-i18next"; import {Trans} from "react-i18next";
import {HistoryCardList} from "../history-card/history-card-list"; import {HistoryCardList} from "../history-card/history-card-list";
import {ViewStateEnum} from "../history-toolbar/history-toolbar"; import {ViewStateEnum} from "../history-toolbar/history-toolbar";
import {PagerPagination} from "../../../../pagination/pager-pagination";
export interface HistoryContentProps { export interface HistoryContentProps {
viewState: ViewStateEnum viewState: ViewStateEnum
@ -20,23 +21,43 @@ export interface HistoryEntryProps {
export interface HistoryEntriesProps { export interface HistoryEntriesProps {
entries: HistoryEntry[] entries: HistoryEntry[]
onPinClick: pinClick onPinClick: pinClick
pageIndex: number
onLastPageIndexChange: (lastPageIndex: number) => void
} }
export const HistoryContent: React.FC<HistoryContentProps> = ({viewState, entries, onPinClick}) => { export const HistoryContent: React.FC<HistoryContentProps> = ({viewState, entries, onPinClick}) => {
const [pageIndex, setPageIndex] = useState(0);
const [lastPageIndex, setLastPageIndex] = useState(0);
if (entries.length === 0) { if (entries.length === 0) {
return ( return (
<Row className={"justify-content-center"}>
<Alert variant={"secondary"}> <Alert variant={"secondary"}>
<Trans i18nKey={"noHistory"}/> <Trans i18nKey={"noHistory"}/>
</Alert> </Alert>
</Row>
); );
} }
const mapViewStateToComponent = (viewState: ViewStateEnum) => {
switch (viewState) { switch (viewState) {
default: default:
case ViewStateEnum.card: case ViewStateEnum.card:
return <HistoryCardList entries={entries} onPinClick={onPinClick}/> return <HistoryCardList entries={entries} onPinClick={onPinClick} pageIndex={pageIndex}
onLastPageIndexChange={setLastPageIndex}/>
case ViewStateEnum.table: case ViewStateEnum.table:
return <HistoryTable entries={entries} onPinClick={onPinClick}/>; return <HistoryTable entries={entries} onPinClick={onPinClick} pageIndex={pageIndex}
onLastPageIndexChange={setLastPageIndex}/>
} }
} }
return (
<Fragment>
{mapViewStateToComponent(viewState)}
<Row className="justify-content-center">
<PagerPagination numberOfPageButtonsToShowAfterAndBeforeCurrent={2} lastPageIndex={lastPageIndex}
onPageChange={setPageIndex}/>
</Row>
</Fragment>);
}

View file

@ -3,8 +3,9 @@ import {Table} from "react-bootstrap"
import {HistoryTableRow} from "./history-table-row"; import {HistoryTableRow} from "./history-table-row";
import {HistoryEntriesProps} from "../history-content/history-content"; import {HistoryEntriesProps} from "../history-content/history-content";
import {Trans} from "react-i18next"; import {Trans} from "react-i18next";
import {Pager} from "../../../../pagination/pager";
const HistoryTable: React.FC<HistoryEntriesProps> = ({entries, onPinClick}) => { export const HistoryTable: React.FC<HistoryEntriesProps> = ({entries, onPinClick, pageIndex, onLastPageIndexChange}) => {
return ( return (
<Table striped bordered hover size="sm" variant="dark"> <Table striped bordered hover size="sm" variant="dark">
<thead> <thead>
@ -16,6 +17,7 @@ const HistoryTable: React.FC<HistoryEntriesProps> = ({entries, onPinClick}) => {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<Pager numberOfElementsPerPage={6} pageIndex={pageIndex} onLastPageIndexChange={onLastPageIndexChange}>
{ {
entries.map((entry) => entries.map((entry) =>
<HistoryTableRow <HistoryTableRow
@ -24,9 +26,8 @@ const HistoryTable: React.FC<HistoryEntriesProps> = ({entries, onPinClick}) => {
onPinClick={onPinClick} onPinClick={onPinClick}
/>) />)
} }
</Pager>
</tbody> </tbody>
</Table> </Table>
) )
} }
export {HistoryTable}

View file

@ -57,11 +57,9 @@ export const History: React.FC = () => {
<Row className={"justify-content-center mb-3"}> <Row className={"justify-content-center mb-3"}>
<HistoryToolbar onSettingsChange={setViewState} tags={tags}/> <HistoryToolbar onSettingsChange={setViewState} tags={tags}/>
</Row> </Row>
<div className="d-flex flex-wrap justify-content-center">
<HistoryContent viewState={viewState.viewState} <HistoryContent viewState={viewState.viewState}
entries={entriesToShow} entries={entriesToShow}
onPinClick={pinClick}/> onPinClick={pinClick}/>
</div>
</Fragment> </Fragment>
) )
} }

View file

@ -0,0 +1,17 @@
import React from "react";
export interface PageItemProps {
onClick: (index: number) => void
index: number
}
export const PagerItem: React.FC<PageItemProps> = ({index, onClick}) => {
return (
<li className="page-item">
<span className="page-link" role="button" onClick={() => onClick(index)}>
{index + 1}
</span>
</li>
);
}

View file

@ -0,0 +1,82 @@
import React, {Fragment, useEffect, useState} from "react";
import {Pagination} from "react-bootstrap";
import {PagerItem} from "./pager-item";
export interface PaginationProps {
numberOfPageButtonsToShowAfterAndBeforeCurrent: number
onPageChange: (pageIndex: number) => void
lastPageIndex: number
}
export const PagerPagination: React.FC<PaginationProps> = ({numberOfPageButtonsToShowAfterAndBeforeCurrent, onPageChange, lastPageIndex}) => {
if (numberOfPageButtonsToShowAfterAndBeforeCurrent % 2 !== 0) {
throw new Error("number of pages to show must be even!")
}
const [pageIndex, setPageIndex] = useState(0);
const correctedPageIndex = Math.min(pageIndex, lastPageIndex);
const wantedUpperPageIndex = correctedPageIndex + numberOfPageButtonsToShowAfterAndBeforeCurrent;
const wantedLowerPageIndex = correctedPageIndex - numberOfPageButtonsToShowAfterAndBeforeCurrent;
useEffect(() => {
onPageChange(pageIndex)
}, [onPageChange, pageIndex])
const correctedLowerPageIndex =
Math.min(
Math.max(
Math.min(
wantedLowerPageIndex,
wantedLowerPageIndex + lastPageIndex - wantedUpperPageIndex
),
0
),
lastPageIndex
);
const correctedUpperPageIndex =
Math.max(
Math.min(
Math.max(
wantedUpperPageIndex,
wantedUpperPageIndex - wantedLowerPageIndex
),
lastPageIndex
),
0
);
const paginationItemsBefore = Array.from(new Array(correctedPageIndex - correctedLowerPageIndex)).map((k, index) => {
const itemIndex = correctedLowerPageIndex + index;
return <PagerItem key={itemIndex} index={itemIndex} onClick={setPageIndex}/>
});
const paginationItemsAfter = Array.from(new Array(correctedUpperPageIndex - correctedPageIndex)).map((k, index) => {
const itemIndex = correctedPageIndex + index + 1;
return <PagerItem key={itemIndex} index={itemIndex} onClick={setPageIndex}/>
});
return (
<Pagination>
{
correctedLowerPageIndex > 0 ?
<Fragment>
<PagerItem key={0} index={0} onClick={setPageIndex}/>
<Pagination.Ellipsis disabled/>
</Fragment>
: null
}
{paginationItemsBefore}
<Pagination.Item active>{correctedPageIndex + 1}</Pagination.Item>
{paginationItemsAfter}
{
correctedUpperPageIndex < lastPageIndex ?
<Fragment>
<Pagination.Ellipsis disabled/>
<PagerItem key={lastPageIndex} index={lastPageIndex} onClick={setPageIndex}/>
</Fragment>
: null
}
</Pagination>
);
}

View file

@ -0,0 +1,24 @@
import React, {Fragment, useEffect} from "react";
export interface PagerPageProps {
pageIndex: number
numberOfElementsPerPage: number
onLastPageIndexChange: (lastPageIndex: number) => void
}
export const Pager: React.FC<PagerPageProps> = ({children, numberOfElementsPerPage, pageIndex, onLastPageIndexChange}) => {
useEffect(() => {
const lastPageIndex = Math.ceil(React.Children.count(children) / numberOfElementsPerPage) - 1;
onLastPageIndexChange(lastPageIndex)
}, [children, numberOfElementsPerPage, onLastPageIndexChange])
return <Fragment>
{
React.Children.toArray(children).filter((value, index) => {
const pageOfElement = Math.floor((index) / numberOfElementsPerPage);
return (pageOfElement === pageIndex);
})
}
</Fragment>
}