From 2c5e8b1f23b0a2e97b0e1c8913e51314bec4498f Mon Sep 17 00:00:00 2001 From: klond90 Date: Fri, 26 Jul 2024 17:31:25 +0400 Subject: [PATCH] pagination component --- src/components/index.ts | 1 + .../pagination/Pagination.stories.mdx | 56 +++++++ src/components/pagination/Pagination.tsx | 142 ++++++++++++++++++ src/components/pagination/index.ts | 2 + src/components/pagination/types.ts | 21 +++ 5 files changed, 222 insertions(+) create mode 100644 src/components/pagination/Pagination.stories.mdx create mode 100644 src/components/pagination/Pagination.tsx create mode 100644 src/components/pagination/index.ts create mode 100644 src/components/pagination/types.ts diff --git a/src/components/index.ts b/src/components/index.ts index 698b9596..70128252 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -33,3 +33,4 @@ export * from "./toggle"; export * from "./button-icon"; export * from "./copy-button"; export * from "./menu"; +export * from "./pagination"; diff --git a/src/components/pagination/Pagination.stories.mdx b/src/components/pagination/Pagination.stories.mdx new file mode 100644 index 00000000..e3853775 --- /dev/null +++ b/src/components/pagination/Pagination.stories.mdx @@ -0,0 +1,56 @@ +import { Canvas, Meta, Story, ArgsTable, Source } from "@storybook/blocks"; +import { Pagination } from "./Pagination"; +import { useState } from "react"; +import { BUTTON_SIZE } from "../button/types"; + + + +export const Template = ({ ...args }) => { + const [page, setPage] = useState(args.currentPage); + return ( + { + setPage(p); + }} + /> + ); +}; + +# Pagination + + + + {Template.bind({})} + + + {Template.bind({})} + + + {Template.bind({})} + + `/page/${p}` }} + > + {Template.bind({})} + + + + + +### Usage: + +To use, import the component `Pagination` from `@nilfoundation/ui-kit`. + + + `} +/> diff --git a/src/components/pagination/Pagination.tsx b/src/components/pagination/Pagination.tsx new file mode 100644 index 00000000..c822a435 --- /dev/null +++ b/src/components/pagination/Pagination.tsx @@ -0,0 +1,142 @@ +import { useMemo } from "react"; +import { BUTTON_KIND, BUTTON_SIZE, Button, ButtonProps } from "../button"; +import { ChevronLeftIcon, ChevronRightIcon } from "../icons"; +import { PageElementProps, PaginationProps } from "./types"; +import { useStyletron } from "baseui"; + +export const PageElement = ({ + page, + linkMapper, + pageHandler, + label, + disabled, + active, + buttonSize, +}: PageElementProps) => { + const buttonProps: ButtonProps = { + disabled, + kind: active ? BUTTON_KIND.primary : BUTTON_KIND.secondary, + size: buttonSize, + }; + if (pageHandler) { + buttonProps.onClick = (e) => { + e.preventDefault(); + pageHandler(page); + }; + } + if (linkMapper && !disabled) { + return ( +
  • + { + e.preventDefault(); + if (pageHandler) pageHandler(page); + }} + aria-label={`${active ? "Current Page, " : ""}Page ${page}`} + aria-current={active} + > + + +
  • + ); + } + return ( + + ); +}; + +export const Pagination = ({ + currentPage, + totalPages, + visiblePages = 3, + pageHandler, + linkMapper, + buttonSize = BUTTON_SIZE.default, +}: PaginationProps) => { + const [css] = useStyletron(); + + const pages = useMemo(() => { + const pages = [1]; + for ( + let i = Math.max(currentPage - visiblePages, 2); + i <= Math.min(totalPages - 1, currentPage + visiblePages); + i++ + ) { + pages.push(i); + } + pages.push(totalPages); + return pages; + }, [currentPage, totalPages, visiblePages]); + + const elements = useMemo(() => { + const elements = []; + let prevPage = 1; + for (const page of pages) { + if (page - prevPage > 1) { + elements.push( + + ); + } + elements.push( +
  • + +
  • + ); + prevPage = page; + } + return elements; + }, [pages, pageHandler, linkMapper, currentPage, buttonSize]); + + return ( + + ); +}; diff --git a/src/components/pagination/index.ts b/src/components/pagination/index.ts new file mode 100644 index 00000000..36ecc098 --- /dev/null +++ b/src/components/pagination/index.ts @@ -0,0 +1,2 @@ +export * from "./Pagination"; +export * from "./types"; diff --git a/src/components/pagination/types.ts b/src/components/pagination/types.ts new file mode 100644 index 00000000..164284b8 --- /dev/null +++ b/src/components/pagination/types.ts @@ -0,0 +1,21 @@ +import { ReactNode } from "react"; +import { BUTTON_SIZE } from "../button"; + +export type PaginationProps = { + currentPage: number; + totalPages: number; + visiblePages?: number; + linkMapper?: (page: number) => string; + pageHandler?: (page: number) => void; + buttonSize?: BUTTON_SIZE; +}; + +export type PageElementProps = { + page: number; + label?: ReactNode; + linkMapper?: (page: number) => string; + pageHandler?: (page: number) => void; + disabled?: boolean; + active?: boolean; + buttonSize: BUTTON_SIZE; +};