Skip to content

Commit

Permalink
feat(pagination): ✨ initiate pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
navin-moorthy committed Sep 9, 2020
1 parent 4826556 commit 36e4513
Show file tree
Hide file tree
Showing 8 changed files with 305 additions and 0 deletions.
29 changes: 29 additions & 0 deletions src/pagination/Pagination.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { BoxHTMLProps, BoxOptions, useBox } from "reakit";
import { createComponent, createHook } from "reakit-system";

import { PAGINATION_KEYS } from "./__keys";
import { PaginationStateReturn } from "./PaginationState";

export type PaginationOptions = BoxOptions & PaginationStateReturn;

export type PaginationHTMLProps = BoxHTMLProps;

export type PaginationProps = PaginationOptions & PaginationHTMLProps;

export const usePagination = createHook<PaginationOptions, PaginationHTMLProps>(
{
name: "Pagination",
compose: useBox,
keys: PAGINATION_KEYS,

useProps(_, htmlProps) {
return { "aria-label": "pagination navigation", ...htmlProps };
},
},
);

export const Pagination = createComponent({
as: "nav",
memo: true,
useHook: usePagination,
});
52 changes: 52 additions & 0 deletions src/pagination/PaginationItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { callAllHandlers } from "@chakra-ui/utils";
import React from "react";
import { createComponent, createHook } from "reakit-system";
import { ButtonHTMLProps, ButtonOptions, useButton } from "reakit";

import { PAGINATION_KEYS } from "./__keys";
import { PaginationStateReturn } from "./PaginationState";

export type PaginationItemOptions = ButtonOptions &
PaginationStateReturn & {
page: number;
};

export type PaginationItemHTMLProps = ButtonHTMLProps;

export type PaginationItemProps = PaginationItemOptions &
PaginationItemHTMLProps;

export const usePaginationItem = createHook<
PaginationItemOptions,
PaginationItemHTMLProps
>({
name: "PaginationItem",
compose: useButton,
keys: PAGINATION_KEYS,

useProps(
{ currentPage, page, goTo },
{ onClick: htmlOnClick, ...htmlProps },
) {
const isCurrent = currentPage === page;

const onClick = React.useCallback(() => {
if (!isCurrent) {
goTo?.(page);
}
}, [goTo, isCurrent, page]);

return {
"aria-label": isCurrent ? `Page ${page}` : `Go to Page ${page}`,
"aria-current": isCurrent ? true : undefined,
onClick: callAllHandlers(onClick, htmlOnClick),
...htmlProps,
};
},
});

export const PaginationItem = createComponent({
as: "button",
memo: true,
useHook: usePaginationItem,
});
41 changes: 41 additions & 0 deletions src/pagination/PaginationNext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from "react";
import { callAllHandlers } from "@chakra-ui/utils";
import { createComponent, createHook } from "reakit-system";
import { ButtonHTMLProps, ButtonOptions, useButton } from "reakit";

import { PAGINATION_KEYS } from "./__keys";
import { PaginationStateReturn } from "./PaginationState";

export type PaginationNextOptions = ButtonOptions & PaginationStateReturn;

export type PaginationNextHTMLProps = ButtonHTMLProps;

export type PaginationNextProps = PaginationNextOptions &
PaginationNextHTMLProps;

export const usePaginationNext = createHook<
PaginationNextOptions,
PaginationNextHTMLProps
>({
name: "PaginationNext",
compose: useButton,
keys: PAGINATION_KEYS,

useOptions(options, htmlProps) {
return { disabled: htmlProps.disabled || options.isAtMax, ...options };
},

useProps({ next }, { onClick: htmlOnClick, ...htmlProps }) {
return {
"aria-label": "Next Page",
onClick: callAllHandlers(htmlOnClick, next),
...htmlProps,
};
},
});

export const PaginationNext = createComponent({
as: "button",
memo: true,
useHook: usePaginationNext,
});
41 changes: 41 additions & 0 deletions src/pagination/PaginationPrev.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { callAllHandlers } from "@chakra-ui/utils";
import React from "react";
import { createComponent, createHook } from "reakit-system";
import { ButtonHTMLProps, ButtonOptions, useButton } from "reakit";

import { PAGINATION_KEYS } from "./__keys";
import { PaginationStateReturn } from "./PaginationState";

export type PaginationPrevOptions = ButtonOptions & PaginationStateReturn;

export type PaginationPrevHTMLProps = ButtonHTMLProps;

export type PaginationPrevProps = PaginationPrevOptions &
PaginationPrevHTMLProps;

export const usePaginationPrev = createHook<
PaginationPrevOptions,
PaginationPrevHTMLProps
>({
name: "PaginationPrev",
compose: useButton,
keys: PAGINATION_KEYS,

useOptions(options, htmlProps) {
return { disabled: htmlProps.disabled || options.isAtMin, ...options };
},

useProps({ prev }, { onClick: htmlOnClick, ...htmlProps }) {
return {
"aria-label": "Previous Page",
onClick: callAllHandlers(htmlOnClick, prev),
...htmlProps,
};
},
});

export const PaginationPrev = createComponent({
as: "button",
memo: true,
useHook: usePaginationPrev,
});
43 changes: 43 additions & 0 deletions src/pagination/PaginationState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from "react";

export interface UsePaginationProps {
totalItems?: number;
perPage?: number;
defaultPage?: number;
}

export const usePaginationState = (props: UsePaginationProps = {}) => {
const {
totalItems = 50,
perPage = 10,
defaultPage: currentPageProp = 1,
} = props;

const totalPages = Math.ceil(totalItems / perPage);
console.log("%c totalPages", "color: #aa00ff", totalPages);

const [currentPage, setCurrentPage] = React.useState(currentPageProp);

const pages = Array(totalPages)
.fill("")
.map((_, i) => i + 1);

const isAtMax = currentPage >= totalPages;
const isAtMin = currentPage <= 1;

const next = React.useCallback(() => {
setCurrentPage(prevPage => prevPage + 1);
}, []);

const prev = React.useCallback(() => {
setCurrentPage(prevPage => prevPage - 1);
}, []);

const goTo = React.useCallback(page => {
setCurrentPage(page);
}, []);

return { currentPage, isAtMax, isAtMin, next, prev, goTo, pages };
};

export type PaginationStateReturn = ReturnType<typeof usePaginationState>;
13 changes: 13 additions & 0 deletions src/pagination/__keys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const PAGINATION_STATE_KEYS = [
"currentPage",
"isAtMin",
"isAtMax",
"next",
"prev",
"goTo",
"id",
"page",
"pages",
] as const;

export const PAGINATION_KEYS = PAGINATION_STATE_KEYS;
Empty file added src/pagination/index.ts
Empty file.
86 changes: 86 additions & 0 deletions src/pagination/stories/Pagination.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React from "react";

import { Meta } from "@storybook/react";
import { Pagination } from "../Pagination";
import { PaginationPrev } from "../PaginationPrev";
import { PaginationNext } from "../PaginationNext";
import { PaginationItem } from "../PaginationItem";
import { usePaginationState } from "../PaginationState";

export default {
title: "Component/Pagination",
} as Meta;

// export const Default = () => {
// const state = usePaginationState();
// console.log("%c state", "color: #00a3cc", state);

// return (
// <Pagination {...state}>
// <ul>
// <li>
// <PaginationPrev {...state}>{"<"}</PaginationPrev>
// </li>
// <li>
// <PaginationItem page={1} {...state}>
// 1
// </PaginationItem>
// </li>
// <li>
// <PaginationItem page={2} {...state}>
// 2
// </PaginationItem>
// </li>
// <li>
// <PaginationItem page={3} {...state}>
// 3
// </PaginationItem>
// </li>
// <li>
// <PaginationItem page={4} {...state}>
// 4
// </PaginationItem>
// </li>
// <li>
// <PaginationItem page={5} {...state}>
// 5
// </PaginationItem>
// </li>
// <li>
// <PaginationNext {...state}>{">"}</PaginationNext>
// </li>
// </ul>
// </Pagination>
// );
// };

export const Dynamic = () => {
const state = usePaginationState();
console.log("%c state", "color: #00fe600", state);

return (
<Pagination {...state}>
<ul>
<li>
<PaginationPrev {...state}>{"<"}</PaginationPrev>
</li>
{state.pages.map(page => (
<li>
<PaginationItem
page={page}
style={{
fontWeight: state.currentPage === page ? "bold" : undefined,
}}
{...state}
>
{page}
</PaginationItem>
</li>
))}
<li>
<PaginationNext {...state}>{">"}</PaginationNext>
</li>
</ul>
</Pagination>
);
};

0 comments on commit 36e4513

Please sign in to comment.