Skip to content

Commit

Permalink
Merge pull request #17 from tago-io/fix/pagination-list-limit
Browse files Browse the repository at this point in the history
Fix: Added breaks and limits for pagination on resource lists
  • Loading branch information
matheuslbenachio authored Aug 11, 2022
2 parents 46d6cc6 + f59dfdc commit 472282e
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,15 @@ function VariablesTable(props: IVariablesTableProps) {
const [selectedVariables, setSelectedVariables] = useState<any>({});
const [hasLocation, setHasLocation] = useState(false);
const [hasMetadata, setHasMetadata] = useState(false);
const [page, setPage] = useState(0);
const [page, setPage] = useState(1);
const match = useRouteMatch<{ id: string }>();
const { id } = match.params;

/**
* Called by the table to retrieve data for a page.
*/
const onGetData = async (_page: number, amount: number, filter: IFilter) => {
const result = await getDeviceData(id, page, amount, filter, currentEndDate);
const result = await getDeviceData(id, page - 1, amount, filter, currentEndDate);
setAmountOfRecords(result.length);
setHasLocation(result.some((x) => x.location));
setHasMetadata(result.some((x) => x.metadata));
Expand All @@ -104,10 +104,10 @@ function VariablesTable(props: IVariablesTableProps) {
const refresh = useCallback(() => {
onReloadDataAmount();
setCurrentEndDate(new Date().toISOString());
if (page === 0) {
if (page === 1) {
onReloadTable();
} else {
setPage(0);
setPage(1);
}
}, [onReloadDataAmount, onReloadTable, page]);

Expand Down
4 changes: 2 additions & 2 deletions packages/tcore-console/src/Components/ListPage/ListPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ interface IListPageProps<T> {
* List page for a certain type of resource.
*/
function ListPage<T extends { id?: string }>(props: IListPageProps<T>) {
const [page, setPage] = useState(0);
const [page, setPage] = useState(1);
const [infinitePages, setInfinitePages] = useState(false);
const [amountOfRecords, setAmountOfRecords] = useState(0);
const tagValues = useRef<ITag[]>([]);
Expand Down Expand Up @@ -89,7 +89,7 @@ function ListPage<T extends { id?: string }>(props: IListPageProps<T>) {
const usingFilter = Object.keys(filterWithTags).some(
(x) => filterWithTags[x] !== undefined && filterWithTags[x] !== ""
);
const data = await onGetData(pg + 1, idealAmountOfRows, filterWithTags);
const data = await onGetData(pg, idealAmountOfRows, filterWithTags);

setAmountOfRecords(data.length);
setInfinitePages(usingFilter);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Number of page buttons to display in narrow containers
const MIN_PAGES = 3;
// Number of page buttons to display in medium containers
const MID_PAGES = 5;
// Number of page buttons to display in wide containers
const MAX_PAGES = 7;

// Minimum width of the page buttons
const BUTTON_WIDTH = 30;
// Padding around the pagination buttons to give space around the edges
const PAGINATION_PADDING = 30;

/**
* Get the amount of pages fitting in a container according to its width.
*
* @param paginationWidth Width of the container from the ref.
*/
const getPageAmount = (paginationWidth: number): number => {
// Return max when ref is still undefined as most tables should be wide enough
if (!paginationWidth) {
return MAX_PAGES;
}

const usedWidth = paginationWidth;
const availableWidth = usedWidth - PAGINATION_PADDING - 2 * BUTTON_WIDTH;
const maxPageAmount = Math.floor(availableWidth / BUTTON_WIDTH);
const pageAmount = Math.max(Math.min(maxPageAmount, MAX_PAGES), MIN_PAGES);

if (pageAmount === MAX_PAGES || pageAmount === MIN_PAGES) {
return pageAmount;
} else {
return MID_PAGES;
}
};

/**
* Gets the array of page numbers and the separators according to the range
* being displayed.
*
* @param amountOfPages Amount of pages.
* @param paginationWidth Width of the container from the ref.
* @param page Current page.
*/
const getPageList = (
amountOfPages: number,
paginationWidth: number,
page: number
): Array<"..." | number> => {
// we need to round them out because floating numbers can break the Array() call:
let amountOfPagesRounded = Math.round(amountOfPages) || 1;

if (isNaN(amountOfPagesRounded) || amountOfPagesRounded === Infinity) {
// extreme cases we will use at least one page
amountOfPagesRounded = 1;
}

const pageNumbers = Array.from(Array(amountOfPagesRounded).keys()).map((pageIdx) => pageIdx + 1);

const maxPages = getPageAmount(paginationWidth);

// Get the pages with ellipsis separating the current page from the boundary
// pages and sibling pages only amount of pages is bigger than space available
if (amountOfPagesRounded > maxPages) {
const lowerBound = maxPages - 2;
const upperBound = amountOfPagesRounded - (maxPages - 3);

const pageInLowerBound = page <= lowerBound;
const pageInUpperBound = page >= upperBound;

// Sibling pages are the page buttons around the current page
const siblings = maxPages === MAX_PAGES ? 1 : 0;

if (pageInLowerBound) {
return [...pageNumbers.slice(0, lowerBound), "...", amountOfPagesRounded];
} else if (pageInUpperBound) {
return [1, "...", ...pageNumbers.slice(upperBound - 1, amountOfPagesRounded)];
} else if (maxPages === MIN_PAGES) {
return ["...", page, "..."];
} else {
return [
1,
"...",
...pageNumbers.slice(page - (1 + siblings), page + siblings),
"...",
amountOfPagesRounded,
];
}
}

return pageNumbers;
};

export { MIN_PAGES, MID_PAGES, MAX_PAGES, getPageAmount, getPageList };
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,14 @@ export const Button = styled.button<{ selected?: boolean }>`
background: rgba(0, 0, 0, 0.25);
`}
`;

export const PaginationSeparator = styled.div`
border: 1px solid transparent;
padding: 0;
border-radius: 5px;
min-width: 30px;
width: initial;
display: flex;
align-items: center;
justify-content: center;
`;
51 changes: 37 additions & 14 deletions packages/tcore-console/src/Components/Pagination/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { useRef } from "react";
import { Tooltip } from "../..";
import Icon from "../Icon/Icon";
import { EIcon } from "../Icon/Icon.types";
import { getPageList } from "./Pagination.logic";
import * as Style from "./Pagination.style";

/**
Expand Down Expand Up @@ -51,7 +53,7 @@ interface IPaginationProps {
*/
function Pagination(props: IPaginationProps) {
const { page, pageAmount, amountOfRecords, idealAmountOfRows, infinitePages, onChange } = props;
const array = new Array(pageAmount || 1).fill("").map((_, i) => i);
const containerRef = useRef<HTMLDivElement>(null);

/**
* Goes back one page.
Expand All @@ -70,13 +72,17 @@ function Pagination(props: IPaginationProps) {
/**
* Renders a single item.
*/
const renderItem = (value: number) => {
const renderItem = (value: number | string, index: number) => {
const selected = page === value;
return (
<Style.Button key={value} onClick={() => onChange(value)} selected={selected}>
{value + 1}
</Style.Button>
);
if (typeof value === "string") {
return <Style.PaginationSeparator key={`separator-${index}`}>...</Style.PaginationSeparator>;
} else {
return (
<Style.Button key={value} onClick={() => onChange(value)} selected={selected}>
{value}
</Style.Button>
);
}
};

/**
Expand All @@ -89,8 +95,29 @@ function Pagination(props: IPaginationProps) {
);
};

/**
* Renders the 'finite' pages in this component. Finite pages are the ones
* that have a start and end, different from the infinite pages which
* just show two arrows.
*/
const renderFinitePages = () => {
if (!containerRef?.current?.clientWidth) {
return null;
}

const array = getPageList(pageAmount, containerRef?.current?.clientWidth, page);
const nextDisabled = !pageAmount || page === pageAmount;
return (
<>
{renderArrow(EIcon["chevron-left"], page === 1, goBack)}
{array.map(renderItem)}
{renderArrow(EIcon["chevron-right"], nextDisabled, goForward)}
</>
);
};

return (
<Style.Container>
<Style.Container ref={containerRef}>
{props.showConfigButton && (
<Tooltip text="Configure table">
<div className="config-button" onClick={props.onConfigButtonClick}>
Expand All @@ -101,19 +128,15 @@ function Pagination(props: IPaginationProps) {

{infinitePages ? (
<>
{renderArrow(EIcon["chevron-left"], page === 0, goBack)}
{renderArrow(EIcon["chevron-left"], page === 1, goBack)}
{renderArrow(
EIcon["chevron-right"],
(amountOfRecords || 0) < (idealAmountOfRows || 0),
goForward
)}
</>
) : pageAmount > 0 ? (
<>
{renderArrow(EIcon["chevron-left"], page === 0, goBack)}
{array.map(renderItem)}
{renderArrow(EIcon["chevron-right"], page === pageAmount - 1, goForward)}
</>
renderFinitePages()
) : null}

{props.showRefreshButton && (
Expand Down

0 comments on commit 472282e

Please sign in to comment.