From 8e0b23a078a82cc86332581f781048ba07751714 Mon Sep 17 00:00:00 2001 From: Ye-Chan Kang Date: Tue, 16 Apr 2024 19:14:46 +0900 Subject: [PATCH 1/3] feat(common): pagination, pagination item (#20) --- src/components/Pagination/Pagination.tsx | 13 +++++++ .../Pagination/PaginationContent.tsx | 15 ++++++++ src/components/Pagination/PaginationItem.tsx | 32 +++++++++++++++++ src/components/Pagination/index.tsx | 4 +++ src/components/index.ts | 1 + src/stories/common/Pagination.stories.tsx | 36 +++++++++++++++++++ 6 files changed, 101 insertions(+) create mode 100644 src/components/Pagination/Pagination.tsx create mode 100644 src/components/Pagination/PaginationContent.tsx create mode 100644 src/components/Pagination/PaginationItem.tsx create mode 100644 src/components/Pagination/index.tsx create mode 100644 src/stories/common/Pagination.stories.tsx diff --git a/src/components/Pagination/Pagination.tsx b/src/components/Pagination/Pagination.tsx new file mode 100644 index 0000000..1d89690 --- /dev/null +++ b/src/components/Pagination/Pagination.tsx @@ -0,0 +1,13 @@ +import styled from '@emotion/styled'; + +interface PaginationProps extends React.PropsWithChildren {} + +export const Pagination = ({ children }: PaginationProps) => { + return ( + + {children} + + ); +}; + +const PaginationNav = styled.nav``; diff --git a/src/components/Pagination/PaginationContent.tsx b/src/components/Pagination/PaginationContent.tsx new file mode 100644 index 0000000..9751968 --- /dev/null +++ b/src/components/Pagination/PaginationContent.tsx @@ -0,0 +1,15 @@ +import styled from '@emotion/styled'; + +interface PaginationProps extends React.PropsWithChildren {} + +export const PaginationContent = ({ children }: PaginationProps) => { + return {children}; +}; +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..478e5ba --- /dev/null +++ b/src/components/Pagination/PaginationItem.tsx @@ -0,0 +1,32 @@ +import { css } from '@emotion/react'; +import styled from '@emotion/styled'; + +interface PaginationProps extends React.PropsWithChildren { + active?: boolean; +} + +export const PaginationItem = ({ active = false, children }: PaginationProps) => { + return {children}; +}; + +const PaginationListItem = styled.li` + width: 2.5rem; + height: 2.5rem; + display: inline-flex; + align-items: center; + justify-content: center; + cursor: pointer; + border-radius: 0.25rem; + font-weight: 500; + ${({ active }) => + active + ? css` + background-color: var(--primary); + color: var(--background); + ` + : css` + &:hover { + background-color: var(--gray-200); + } + `} +`; diff --git a/src/components/Pagination/index.tsx b/src/components/Pagination/index.tsx new file mode 100644 index 0000000..c4e069b --- /dev/null +++ b/src/components/Pagination/index.tsx @@ -0,0 +1,4 @@ +/* eslint-disable react-refresh/only-export-components */ +export * from './Pagination'; +export * from './PaginationContent'; +export * from './PaginationItem'; 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..7799806 --- /dev/null +++ b/src/stories/common/Pagination.stories.tsx @@ -0,0 +1,36 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import * as React from 'react'; + +import { Pagination, PaginationContent, PaginationItem } 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 + + + ); + }, + ], +}; From 8aa133766176357d6f28685476b026c75348e359 Mon Sep 17 00:00:00 2001 From: Ye-Chan Kang Date: Sat, 20 Apr 2024 19:47:25 +0900 Subject: [PATCH 2/3] feat(common): autopagination --- src/components/Pagination/AutoPagination.tsx | 31 ++++++ src/components/Pagination/Pagination.tsx | 15 ++- .../Pagination/PaginationContent.tsx | 15 ++- src/components/Pagination/PaginationItem.tsx | 102 +++++++++++++++--- src/components/Pagination/index.tsx | 1 + src/stories/common/Pagination.stories.tsx | 29 ++++- 6 files changed, 166 insertions(+), 27 deletions(-) create mode 100644 src/components/Pagination/AutoPagination.tsx diff --git a/src/components/Pagination/AutoPagination.tsx b/src/components/Pagination/AutoPagination.tsx new file mode 100644 index 0000000..1abf547 --- /dev/null +++ b/src/components/Pagination/AutoPagination.tsx @@ -0,0 +1,31 @@ +import { Pagination } from './Pagination'; +import { PaginationContent } from './PaginationContent'; +import { PaginationItem, PaginationNext, PaginationPrevious } from './PaginationItem'; + +interface AutoPaginationProps { + pageNo: number; + pageSize: number; + pageGap?: number; +} + +export const AutoPagination = ({ pageNo, pageSize, pageGap = 10 }: AutoPaginationProps) => { + const getPages = () => { + const start = Math.floor(pageNo / (pageGap + 1)) * pageGap; + const end = Math.min(start + pageGap, pageSize); + return Array.from({ length: end - start }, (_, index) => index + start + 1); + }; + + return ( + + + + {getPages().map(page => ( + + {page} + + ))} + + + + ); +}; diff --git a/src/components/Pagination/Pagination.tsx b/src/components/Pagination/Pagination.tsx index 1d89690..d66df0b 100644 --- a/src/components/Pagination/Pagination.tsx +++ b/src/components/Pagination/Pagination.tsx @@ -1,13 +1,12 @@ -import styled from '@emotion/styled'; +import * as React from 'react'; -interface PaginationProps extends React.PropsWithChildren {} +interface PaginationProps extends React.ComponentPropsWithoutRef<'nav'> {} -export const Pagination = ({ children }: PaginationProps) => { +export const Pagination = React.forwardRef(({ children, ...props }, ref) => { return ( - + ); -}; - -const PaginationNav = styled.nav``; +}); +Pagination.displayName = 'Pagination'; diff --git a/src/components/Pagination/PaginationContent.tsx b/src/components/Pagination/PaginationContent.tsx index 9751968..cee05fa 100644 --- a/src/components/Pagination/PaginationContent.tsx +++ b/src/components/Pagination/PaginationContent.tsx @@ -1,10 +1,17 @@ import styled from '@emotion/styled'; +import * as React from 'react'; -interface PaginationProps extends React.PropsWithChildren {} +interface PaginationProps extends React.ComponentPropsWithoutRef<'ul'> {} + +export const PaginationContent = React.forwardRef(({ children, ...props }, ref) => { + return ( + + {children} + + ); +}); +PaginationContent.displayName = 'PaginationContent'; -export const PaginationContent = ({ children }: PaginationProps) => { - return {children}; -}; const PaginationList = styled.ul` list-style: none; margin: 0px; diff --git a/src/components/Pagination/PaginationItem.tsx b/src/components/Pagination/PaginationItem.tsx index 478e5ba..7e3685a 100644 --- a/src/components/Pagination/PaginationItem.tsx +++ b/src/components/Pagination/PaginationItem.tsx @@ -1,32 +1,106 @@ import { css } from '@emotion/react'; import styled from '@emotion/styled'; +import * as React from 'react'; -interface PaginationProps extends React.PropsWithChildren { +import { composeEventHandlers } from '@/libs/event'; + +interface PaginationProps extends React.ComponentPropsWithoutRef<'li'> { active?: boolean; + disabled?: boolean; } -export const PaginationItem = ({ active = false, children }: PaginationProps) => { - return {children}; +export const PaginationItem = React.forwardRef( + ({ active = false, disabled = false, onClick, children, ...props }, ref) => { + const handleOnclick = (e: React.MouseEvent) => { + if (disabled) e.preventDefault(); + }; + + return ( + + {children} + + ); + }, +); +PaginationItem.displayName = 'PaginationItem'; + +export const PaginationPrevious = ({ active = false, disabled = false, children, ...props }: PaginationProps) => { + return ( + + + + + {children} + + ); +}; + +export const PaginationNext = ({ active = false, disabled = false, children, ...props }: PaginationProps) => { + return ( + + {children} + + + + + ); }; const PaginationListItem = styled.li` - width: 2.5rem; - height: 2.5rem; + min-width: 2.5rem; + min-height: 2.5rem; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; border-radius: 0.25rem; font-weight: 500; - ${({ active }) => - active + line-height: 2.5rem; + + ${({ active, disabled }) => + disabled ? css` - background-color: var(--primary); - color: var(--background); + cursor: default; + border-color: transparent; + opacity: 0.5; ` - : css` - &:hover { - background-color: var(--gray-200); - } - `} + : active + ? css` + background-color: var(--primary); + color: var(--background); + ` + : css` + &:hover { + background-color: var(--gray-200); + } + `} `; diff --git a/src/components/Pagination/index.tsx b/src/components/Pagination/index.tsx index c4e069b..7696e62 100644 --- a/src/components/Pagination/index.tsx +++ b/src/components/Pagination/index.tsx @@ -2,3 +2,4 @@ export * from './Pagination'; export * from './PaginationContent'; export * from './PaginationItem'; +export * from './AutoPagination'; diff --git a/src/stories/common/Pagination.stories.tsx b/src/stories/common/Pagination.stories.tsx index 7799806..53a29a9 100644 --- a/src/stories/common/Pagination.stories.tsx +++ b/src/stories/common/Pagination.stories.tsx @@ -1,7 +1,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import * as React from 'react'; -import { Pagination, PaginationContent, PaginationItem } from '../../'; +import { AutoPagination, Pagination, PaginationContent, PaginationItem, PaginationPrevious } from '../../'; const meta = { title: 'common/Pagination', @@ -34,3 +34,30 @@ export const Default: Story = { }, ], }; + +export const Disabled: Story = { + args: {}, + decorators: [ + () => { + return ( + + + + 1 + 2 + 3 + + + ); + }, + ], +}; + +export const Auto: Story = { + args: {}, + decorators: [ + () => { + return ; + }, + ], +}; From ffca2e6a05def0620dc8dbd21d55e63b317d085b Mon Sep 17 00:00:00 2001 From: Ye-Chan Kang Date: Sun, 21 Apr 2024 01:22:22 +0900 Subject: [PATCH 3/3] feat: paginationlink --- src/components/Pagination/AutoPagination.tsx | 29 +++-- src/components/Pagination/PaginationItem.tsx | 106 ++----------------- src/components/Pagination/PaginationLink.tsx | 106 +++++++++++++++++++ src/components/Pagination/index.tsx | 1 + src/stories/common/Pagination.stories.tsx | 27 +++-- 5 files changed, 150 insertions(+), 119 deletions(-) create mode 100644 src/components/Pagination/PaginationLink.tsx diff --git a/src/components/Pagination/AutoPagination.tsx b/src/components/Pagination/AutoPagination.tsx index 1abf547..3006000 100644 --- a/src/components/Pagination/AutoPagination.tsx +++ b/src/components/Pagination/AutoPagination.tsx @@ -1,6 +1,9 @@ +import React from 'react'; + import { Pagination } from './Pagination'; import { PaginationContent } from './PaginationContent'; -import { PaginationItem, PaginationNext, PaginationPrevious } from './PaginationItem'; +import { PaginationItem } from './PaginationItem'; +import { PaginationLink, PaginationNext, PaginationPrevious } from './PaginationLink'; interface AutoPaginationProps { pageNo: number; @@ -9,22 +12,26 @@ interface AutoPaginationProps { } export const AutoPagination = ({ pageNo, pageSize, pageGap = 10 }: AutoPaginationProps) => { - const getPages = () => { - const start = Math.floor(pageNo / (pageGap + 1)) * pageGap; - const end = Math.min(start + pageGap, pageSize); - return Array.from({ length: end - start }, (_, index) => index + start + 1); - }; + 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 ( - - {getPages().map(page => ( - - {page} + + + + {pages.map(page => ( + + + {page} + ))} - + + = pageSize} href={`?page=${end + 1}`} /> + ); diff --git a/src/components/Pagination/PaginationItem.tsx b/src/components/Pagination/PaginationItem.tsx index 7e3685a..aff0a58 100644 --- a/src/components/Pagination/PaginationItem.tsx +++ b/src/components/Pagination/PaginationItem.tsx @@ -1,106 +1,12 @@ -import { css } from '@emotion/react'; -import styled from '@emotion/styled'; import * as React from 'react'; -import { composeEventHandlers } from '@/libs/event'; +interface PaginationProps extends React.ComponentPropsWithoutRef<'li'> {} -interface PaginationProps extends React.ComponentPropsWithoutRef<'li'> { - active?: boolean; - disabled?: boolean; -} - -export const PaginationItem = React.forwardRef( - ({ active = false, disabled = false, onClick, children, ...props }, ref) => { - const handleOnclick = (e: React.MouseEvent) => { - if (disabled) e.preventDefault(); - }; - - return ( - - {children} - - ); - }, -); -PaginationItem.displayName = 'PaginationItem'; - -export const PaginationPrevious = ({ active = false, disabled = false, children, ...props }: PaginationProps) => { +export const PaginationItem = React.forwardRef(({ children, ...props }, ref) => { return ( - - - - +
  • {children} - +
  • ); -}; - -export const PaginationNext = ({ active = false, disabled = false, children, ...props }: PaginationProps) => { - return ( - - {children} - - - - - ); -}; - -const PaginationListItem = styled.li` - min-width: 2.5rem; - min-height: 2.5rem; - display: inline-flex; - align-items: center; - justify-content: center; - cursor: pointer; - border-radius: 0.25rem; - font-weight: 500; - line-height: 2.5rem; - - ${({ 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); - } - `} -`; +}); +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 index 7696e62..f4525c0 100644 --- a/src/components/Pagination/index.tsx +++ b/src/components/Pagination/index.tsx @@ -2,4 +2,5 @@ export * from './Pagination'; export * from './PaginationContent'; export * from './PaginationItem'; +export * from './PaginationLink'; export * from './AutoPagination'; diff --git a/src/stories/common/Pagination.stories.tsx b/src/stories/common/Pagination.stories.tsx index 53a29a9..2b102c2 100644 --- a/src/stories/common/Pagination.stories.tsx +++ b/src/stories/common/Pagination.stories.tsx @@ -1,7 +1,14 @@ import type { Meta, StoryObj } from '@storybook/react'; import * as React from 'react'; -import { AutoPagination, Pagination, PaginationContent, PaginationItem, PaginationPrevious } from '../../'; +import { + AutoPagination, + Pagination, + PaginationContent, + PaginationItem, + PaginationLink, + PaginationPrevious, +} from '../../'; const meta = { title: 'common/Pagination', @@ -25,9 +32,11 @@ export const Default: Story = { return ( - 1 - 2 - 3 + {[1, 2, 3].map(page => ( + + {page} + + ))} ); @@ -43,9 +52,11 @@ export const Disabled: Story = { - 1 - 2 - 3 + {[1, 2, 3].map(page => ( + + {page} + + ))} ); @@ -57,7 +68,7 @@ export const Auto: Story = { args: {}, decorators: [ () => { - return ; + return ; }, ], };