Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(component): create basic pagination component #188

Merged
merged 12 commits into from
Sep 16, 2019
119 changes: 119 additions & 0 deletions packages/big-design/src/components/Pagination/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { ArrowDropDownIcon, ChevronLeftIcon, ChevronRightIcon } from '@bigcommerce/big-design-icons';
import React, { useEffect, useState } from 'react';

import { MarginProps } from '../../mixins';
import { Dropdown } from '../Dropdown';
import { Flex } from '../Flex';

import { StyledButton } from './styled';

export interface PaginationProps extends MarginProps {
currentPage: number;
itemsPerPage: number;
itemsPerPageOptions: number[];
totalItems: number;
onPageChange(page: number): void;
onItemsPerPageChange(range: number): void;
}

export const Pagination: React.FC<PaginationProps> = props => {
jordan-massingill marked this conversation as resolved.
Show resolved Hide resolved
const { itemsPerPage, currentPage, totalItems, itemsPerPageOptions, onPageChange, onItemsPerPageChange } = props;
jordan-massingill marked this conversation as resolved.
Show resolved Hide resolved

const [maxPages, setMaxPages] = useState(Math.ceil(totalItems / itemsPerPage));
const [itemRange, setItemRange] = useState({ start: 0, end: 0 });

const handlePageOutOfBounds = () => {
if (currentPage < 1 || isNaN(currentPage) || currentPage === undefined) {
jordan-massingill marked this conversation as resolved.
Show resolved Hide resolved
onPageChange(1);
} else if (currentPage > maxPages) {
onPageChange(maxPages);
}
};

const handlePerPageOutOfBounds = () => {
if (itemsPerPage < 1 || isNaN(itemsPerPage) || itemsPerPage === undefined) {
jordan-massingill marked this conversation as resolved.
Show resolved Hide resolved
onItemsPerPageChange(itemsPerPageOptions[0]);
}
};

const calculateRange = () => {
let firstItemInRange = itemsPerPage * (currentPage - 1) + 1;
let lastItemInRange = itemsPerPage * currentPage;

firstItemInRange = Math.min(firstItemInRange, totalItems);
lastItemInRange = Math.min(lastItemInRange, totalItems);

if (lastItemInRange === 0 || isNaN(lastItemInRange) || isNaN(firstItemInRange)) {
jordan-massingill marked this conversation as resolved.
Show resolved Hide resolved
firstItemInRange = 0;
lastItemInRange = 0;
}

setItemRange({ start: firstItemInRange, end: lastItemInRange });
};

useEffect(() => {
handlePageOutOfBounds();

handlePerPageOutOfBounds();

calculateRange();

setMaxPages(Math.ceil(totalItems / itemsPerPage));
}, [currentPage, itemsPerPage, totalItems]);

const handlePageIncrease = () => {
onPageChange(currentPage + 1);
};

const handlePageDecrease = () => {
onPageChange(currentPage - 1);
};

const handleRangeChange = (range: number) => {
onItemsPerPageChange(range);
};

const showRanges = () => {
return itemRange.start === itemRange.end
? `${itemRange.start} of ${totalItems}`
: `${itemRange.start} - ${itemRange.end} of ${totalItems}`;
};

return (
<Flex flexDirection="row" justifyContent="center" role="navigation" aria-label="pagination">
jordan-massingill marked this conversation as resolved.
Show resolved Hide resolved
<Dropdown
onItemClick={handleRangeChange}
trigger={
<StyledButton variant="subtle" iconRight={<ArrowDropDownIcon size="xxLarge" />}>
{showRanges()}
</StyledButton>
}
>
{itemsPerPageOptions.map((range, key) => (
<Dropdown.Item key={key} value={range}>
{range} per page
</Dropdown.Item>
))}
</Dropdown>
<Flex.Item>
<StyledButton
className="bd-pagination-prev"
variant="subtle"
disabled={currentPage <= 1}
onClick={handlePageDecrease}
>
<ChevronLeftIcon title="Previous page" />
</StyledButton>

<StyledButton
className="bd-pagination-next"
variant="subtle"
disabled={currentPage >= maxPages}
onClick={handlePageIncrease}
>
<ChevronRightIcon title="Next page" />
</StyledButton>
</Flex.Item>
</Flex>
);
};
Loading