diff --git a/src/components/Sidebar/ArrowLeft.tsx b/src/components/Sidebar/ArrowLeft.tsx deleted file mode 100644 index cfef1ac..0000000 --- a/src/components/Sidebar/ArrowLeft.tsx +++ /dev/null @@ -1,21 +0,0 @@ -interface ArrowProps { - disabled?: boolean; -} - -export default function ArrowLeft({ disabled = false }: ArrowProps) { - const baseStyle = 'w-4 h-4 fill-current'; - - // disabled 여부에 따른 색상 및 hover - // - 비활성화(disabled=true)일 땐 text-gray-30, 클릭 불가(cursor-default) - // - 활성화(disabled=false)일 땐 text-gray-80, hover:text-gray-60, cursor-pointer - const colorStyle = disabled ? 'text-gray-30 cursor-default' : 'text-gray-50 hover:text-gray-60 cursor-pointer'; - - return ( - - - - ); -} diff --git a/src/components/Sidebar/ArrowRight.tsx b/src/components/Sidebar/ArrowRight.tsx deleted file mode 100644 index cdc7589..0000000 --- a/src/components/Sidebar/ArrowRight.tsx +++ /dev/null @@ -1,17 +0,0 @@ -interface ArrowProps { - disabled?: boolean; -} - -export default function ArrowRight({ disabled = false }: ArrowProps) { - const baseStyle = 'w-4 h-4 fill-current'; - const colorStyle = disabled ? 'text-gray-30 cursor-default' : 'text-gray-50 hover:text-gray-70 cursor-pointer'; - - return ( - - - - ); -} diff --git a/src/components/Sidebar/Sidebar.tsx b/src/components/Sidebar/Sidebar.tsx index 058bece..8161250 100644 --- a/src/components/Sidebar/Sidebar.tsx +++ b/src/components/Sidebar/Sidebar.tsx @@ -1,12 +1,11 @@ 'use client'; -import { useState } from 'react'; import Image from 'next/image'; -import { chunkArray } from '@/utils/chunkArray'; +import { UseChunkPagination } from '@/hooks/useChunkPagination'; +import PaginationControls from '../pagination/PaginationControls'; import SidebarLogo from './SidebarLogo'; import SidebarItemList from './SidebarItemList'; -import SidebarPaginationControls from './SidebarPaginationControls'; import add_box from '@/assets/icons/add_box.svg'; @@ -37,25 +36,11 @@ const mockBoardData: ItemList[] = [ ]; export default function Sidebar() { - const MAX_GROUPS_PER_PAGE = 3; - const [page, setPage] = useState(1); - const chunkedData = chunkArray(mockBoardData, 5); - - const totalPages = Math.ceil(chunkedData.length / MAX_GROUPS_PER_PAGE); - - const startIndex = (page - 1) * MAX_GROUPS_PER_PAGE; - const endIndex = page * MAX_GROUPS_PER_PAGE; - const currentGroups = chunkedData.slice(startIndex, endIndex); - - const canGoPrev = page > 1; - const canGoNext = page < totalPages; - - const handlePrev = () => { - if (canGoPrev) setPage((prev) => prev - 1); - }; - const handleNext = () => { - if (canGoNext) setPage((prev) => prev + 1); - }; + const { currentGroups, totalPages, canGoPrev, canGoNext, handlePrev, handleNext } = UseChunkPagination({ + items: mockBoardData, + chunkSize: 5, + maxGroupsPerPage: 3, + }); return ( diff --git a/src/components/Sidebar/SidebarPaginationControls.tsx b/src/components/Sidebar/SidebarPaginationControls.tsx deleted file mode 100644 index 4f1cacd..0000000 --- a/src/components/Sidebar/SidebarPaginationControls.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import ArrowLeft from './ArrowLeft'; -import ArrowRight from './ArrowRight'; - -interface PaginationControlsProps { - canGoPrev: boolean; - canGoNext: boolean; - handlePrev: () => void; - handleNext: () => void; -} - -export default function SidebarPaginationControls({ canGoPrev, canGoNext, handlePrev, handleNext }: PaginationControlsProps) { - return ( -
- - -
- ); -} diff --git a/src/components/pagination/Arrow.tsx b/src/components/pagination/Arrow.tsx new file mode 100644 index 0000000..959ab0b --- /dev/null +++ b/src/components/pagination/Arrow.tsx @@ -0,0 +1,24 @@ +import { ARROW_PATH_LEFT, ARROW_PATH_RIGHT } from '@/constants/paths'; +import { cn } from '@/utils/helper'; + +/* + * direction: 'left' | 'right' + * disabled일 경우 색상/커서 상태 변경 + */ +interface ArrowProps { + direction: 'left' | 'right'; + disabled?: boolean; +} + +export default function Arrow({ direction, disabled = false }: ArrowProps) { + const baseStyle = 'w-4 h-4 fill-current'; + const colorStyle = disabled ? 'text-gray-30 cursor-default' : 'text-gray-50 hover:text-gray-60 cursor-pointer'; + + const dValue = direction === 'left' ? ARROW_PATH_LEFT : ARROW_PATH_RIGHT; + + return ( + + + + ); +} diff --git a/src/components/pagination/PaginationControls.tsx b/src/components/pagination/PaginationControls.tsx new file mode 100644 index 0000000..9deffa9 --- /dev/null +++ b/src/components/pagination/PaginationControls.tsx @@ -0,0 +1,50 @@ +import Arrow from './Arrow'; + +/* + * "PaginationControls" 컴포넌트 사용 가이드 + * ------------------------------------------------------------ + * // 예시 + * + * + * - canGoPrev, canGoNext: "이전/다음 버튼" 활성/비활성 제어 + * - handlePrev, handleNext: 클릭 시 실제 페이지 이동 처리 함수 + * - totalPages: 총 페이지 수 + * - alwaysShow: true면 totalPages<=1이어도 버튼을 무조건 렌더(비활성). + * - className: display나 여백, 반응형 등을 제어하기 위해 부모가 원하는 클래스를 덧붙일 수 있음. + * ------------------------------------------------------------ + */ +interface PaginationControlsProps { + canGoPrev: boolean; + canGoNext: boolean; + handlePrev: () => void; + handleNext: () => void; + totalPages: number; + alwaysShow?: boolean; + className?: string; +} + +export default function PaginationControls({ canGoPrev, canGoNext, handlePrev, handleNext, totalPages, alwaysShow = false, className }: PaginationControlsProps) { + if (!alwaysShow && totalPages <= 1) { + return null; + } + + return ( +
+ + + +
+ ); +} diff --git a/src/constants/paths.ts b/src/constants/paths.ts new file mode 100644 index 0000000..1364744 --- /dev/null +++ b/src/constants/paths.ts @@ -0,0 +1,3 @@ +export const ARROW_PATH_LEFT = `M2.641 7.99933L8.91824 1.72208C9.08384 1.55648 9.16451 1.35937 9.16024 1.13074C9.15596 0.902106 9.07103 0.704996 8.90543 0.539412C8.73983 0.373815 8.54272 0.291016 8.31409 0.291016C8.08547 0.291016 7.88836 0.373815 7.72276 0.539412L1.34295 6.93204C1.19231 7.08268 1.08066 7.25147 1.00801 7.43843C0.93537 7.62541 0.899048 7.81237 0.899048 7.99933C0.899048 8.18629 0.93537 8.37325 1.00801 8.56022C1.08066 8.74718 1.19231 8.91598 1.34295 9.06662L7.73557 15.4592C7.90117 15.6248 8.09615 15.7055 8.32051 15.7012C8.54486 15.697 8.73983 15.612 8.90543 15.4464C9.07103 15.2808 9.15383 15.0837 9.15383 14.8551C9.15383 14.6265 9.07103 14.4294 8.90543 14.2638L2.641 7.99933Z`; + +export const ARROW_PATH_RIGHT = `M7.35901 7.99933L1.08176 1.72208C0.916163 1.55648 0.835496 1.35937 0.83976 1.13074C0.844038 0.902106 0.928975 0.704996 1.09457 0.539412C1.26017 0.373815 1.45728 0.291016 1.68591 0.291016C1.91453 0.291016 2.11164 0.373815 2.27724 0.539412L8.65705 6.93204C8.80769 7.08268 8.91934 7.25147 8.99199 7.43843C9.06463 7.62541 9.10095 7.81237 9.10095 7.99933C9.10095 8.18629 9.06463 8.37325 8.99199 8.56022C8.91934 8.74718 8.80769 8.91598 8.65705 9.06662L2.26443 15.4592C2.09883 15.6248 1.90385 15.7055 1.67949 15.7012C1.45514 15.697 1.26017 15.612 1.09457 15.4464C0.928975 15.2808 0.846176 15.0837 0.846176 14.8551C0.846176 14.6265 0.928975 14.4294 1.09457 14.2638L7.35901 7.99933Z`; diff --git a/src/hooks/useChunkPagination.ts b/src/hooks/useChunkPagination.ts new file mode 100644 index 0000000..f214101 --- /dev/null +++ b/src/hooks/useChunkPagination.ts @@ -0,0 +1,66 @@ +import { useState } from 'react'; +import { chunkArray } from '@/utils/chunkArray'; + +/* + * "UseChunkPagination" 커스텀 훅 사용 가이드 + * ------------------------------------------------------------ + * // 예시 + * const { + * currentGroups, + * page, + * canGoPrev, + * canGoNext, + * handlePrev, + * handleNext, + * totalPages, + * } = UseChunkPagination({ + * items: myDataArray, // 전체 아이템 배열 + * chunkSize: 5, // 한 묶음에 몇 개씩 자를지 + * maxGroupsPerPage: 3, // 한 페이지에 묶음을 몇 개까지 보여줄지 + * }); + * + * // 이후, currentGroups로 화면을 렌더하고, + * // canGoPrev/canGoNext로 이전/다음 버튼 활성화 여부를 결정하며, + * // handlePrev/handleNext로 페이지 이동을 제어 + * + * // 만약 "한 페이지에 1묶음씩" 보이길 원하면 maxGroupsPerPage를 1로 바꾸면 됨. + * // => 데이터가 2묶음 이상이면 다음 페이지가 생김 + * ------------------------------------------------------------ + */ +interface UseChunkPaginationProps { + items: T[]; + chunkSize: number; + maxGroupsPerPage: number; +} + +export function UseChunkPagination({ items, chunkSize, maxGroupsPerPage }: UseChunkPaginationProps) { + const [page, setPage] = useState(1); + + const chunkedData = chunkArray(items, chunkSize); + const totalPages = Math.ceil(chunkedData.length / maxGroupsPerPage); + + const startIndex = (page - 1) * maxGroupsPerPage; + const endIndex = page * maxGroupsPerPage; + const currentGroups = chunkedData.slice(startIndex, endIndex); + + const canGoPrev = page > 1; + const canGoNext = page < totalPages; + + const handlePrev = () => { + if (canGoPrev) setPage((prev) => prev - 1); + }; + + const handleNext = () => { + if (canGoNext) setPage((prev) => prev + 1); + }; + return { + currentGroups, + page, + setPage, + totalPages, + canGoPrev, + canGoNext, + handlePrev, + handleNext, + }; +}