diff --git a/src/components/Pagination/AutoPagination.tsx b/src/components/Pagination/AutoPagination.tsx new file mode 100644 index 0000000..3006000 --- /dev/null +++ b/src/components/Pagination/AutoPagination.tsx @@ -0,0 +1,38 @@ +import React from 'react'; + +import { Pagination } from './Pagination'; +import { PaginationContent } from './PaginationContent'; +import { PaginationItem } from './PaginationItem'; +import { PaginationLink, PaginationNext, PaginationPrevious } from './PaginationLink'; + +interface AutoPaginationProps { + pageNo: number; + pageSize: number; + pageGap?: number; +} + +export const AutoPagination = ({ pageNo, pageSize, pageGap = 10 }: AutoPaginationProps) => { + const start = Math.floor(pageNo / (pageGap + 1)) * pageGap; + const end = Math.min(start + pageGap, pageSize); + const pages = Array.from({ length: end - start }, (_, index) => index + start + 1); + + return ( + + + + + + {pages.map(page => ( + + + {page} + + + ))} + + = pageSize} href={`?page=${end + 1}`} /> + + + + ); +}; diff --git a/src/components/Pagination/Pagination.tsx b/src/components/Pagination/Pagination.tsx new file mode 100644 index 0000000..d66df0b --- /dev/null +++ b/src/components/Pagination/Pagination.tsx @@ -0,0 +1,12 @@ +import * as React from 'react'; + +interface PaginationProps extends React.ComponentPropsWithoutRef<'nav'> {} + +export const Pagination = React.forwardRef(({ children, ...props }, ref) => { + return ( + + ); +}); +Pagination.displayName = 'Pagination'; diff --git a/src/components/Pagination/PaginationContent.tsx b/src/components/Pagination/PaginationContent.tsx new file mode 100644 index 0000000..cee05fa --- /dev/null +++ b/src/components/Pagination/PaginationContent.tsx @@ -0,0 +1,22 @@ +import styled from '@emotion/styled'; +import * as React from 'react'; + +interface PaginationProps extends React.ComponentPropsWithoutRef<'ul'> {} + +export const PaginationContent = React.forwardRef(({ children, ...props }, ref) => { + return ( + + {children} + + ); +}); +PaginationContent.displayName = 'PaginationContent'; + +const PaginationList = styled.ul` + list-style: none; + margin: 0px; + padding: 0px; + display: flex; + flex-direction: row; + gap: 0.5rem; +`; diff --git a/src/components/Pagination/PaginationItem.tsx b/src/components/Pagination/PaginationItem.tsx new file mode 100644 index 0000000..aff0a58 --- /dev/null +++ b/src/components/Pagination/PaginationItem.tsx @@ -0,0 +1,12 @@ +import * as React from 'react'; + +interface PaginationProps extends React.ComponentPropsWithoutRef<'li'> {} + +export const PaginationItem = React.forwardRef(({ children, ...props }, ref) => { + return ( +
  • + {children} +
  • + ); +}); +PaginationItem.displayName = 'PaginationItem'; diff --git a/src/components/Pagination/PaginationLink.tsx b/src/components/Pagination/PaginationLink.tsx new file mode 100644 index 0000000..6b81e40 --- /dev/null +++ b/src/components/Pagination/PaginationLink.tsx @@ -0,0 +1,106 @@ +import { css } from '@emotion/react'; +import styled from '@emotion/styled'; +import * as React from 'react'; + +import { composeEventHandlers } from '@/libs/event'; + +interface PaginationLinkProps extends React.ComponentPropsWithoutRef<'a'> { + active?: boolean; + disabled?: boolean; +} + +export const PaginationLink = React.forwardRef( + ({ active = false, disabled = false, onClick, ...props }, ref) => { + const handleOnclick = (e: React.MouseEvent) => { + if (disabled) e.preventDefault(); + }; + + return ( + <> + {disabled ? ( + + ) : ( + + )} + + ); + }, +); +PaginationLink.displayName = 'PaginationLink'; + +export const PaginationPrevious = ({ active = false, disabled = false, children, ...props }: PaginationLinkProps) => { + return ( + + + + + {children} + + ); +}; + +export const PaginationNext = ({ active = false, disabled = false, children, ...props }: PaginationLinkProps) => { + return ( + + {children} + + + + + ); +}; + +const Link = styled.a` + min-width: 2.5rem; + min-height: 2.5rem; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 0.25rem; + font-weight: 500; + line-height: 2.5rem; + cursor: pointer; + text-decoration: none; + color: inherit; + ${({ active, disabled }) => + disabled + ? css` + cursor: default; + border-color: transparent; + opacity: 0.5; + ` + : active + ? css` + background-color: var(--primary); + color: var(--background); + ` + : css` + &:hover { + background-color: var(--gray-200); + } + `} +`; + +const DisabledLink = Link.withComponent('span'); diff --git a/src/components/Pagination/index.tsx b/src/components/Pagination/index.tsx new file mode 100644 index 0000000..f4525c0 --- /dev/null +++ b/src/components/Pagination/index.tsx @@ -0,0 +1,6 @@ +/* eslint-disable react-refresh/only-export-components */ +export * from './Pagination'; +export * from './PaginationContent'; +export * from './PaginationItem'; +export * from './PaginationLink'; +export * from './AutoPagination'; diff --git a/src/components/index.ts b/src/components/index.ts index c210f15..ecbae11 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -7,6 +7,7 @@ export * from './Dropdown'; export * from './HoverCard'; export * from './Input'; export * from './Label'; +export * from './Pagination'; export * from './Select'; export * from './Switch'; export * from './Textarea'; diff --git a/src/stories/common/Pagination.stories.tsx b/src/stories/common/Pagination.stories.tsx new file mode 100644 index 0000000..2b102c2 --- /dev/null +++ b/src/stories/common/Pagination.stories.tsx @@ -0,0 +1,74 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import * as React from 'react'; + +import { + AutoPagination, + Pagination, + PaginationContent, + PaginationItem, + PaginationLink, + PaginationPrevious, +} from '../../'; + +const meta = { + title: 'common/Pagination', + component: Pagination, + tags: ['autodocs'], + args: {}, + parameters: { + layout: 'centered', + componentSubtitle: 'Base Pagination', + }, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: {}, + decorators: [ + () => { + return ( + + + {[1, 2, 3].map(page => ( + + {page} + + ))} + + + ); + }, + ], +}; + +export const Disabled: Story = { + args: {}, + decorators: [ + () => { + return ( + + + + {[1, 2, 3].map(page => ( + + {page} + + ))} + + + ); + }, + ], +}; + +export const Auto: Story = { + args: {}, + decorators: [ + () => { + return ; + }, + ], +};