Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/components/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ export { Dropdown } from './dropdown';
export { Filter } from './filter';
export { Icon } from './icon';
export { Modal, Notification } from './modal';
export { Pagination } from './pagination';
6 changes: 3 additions & 3 deletions src/components/ui/input/TimeInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import { useCallback, useRef, useState } from 'react';
import Input from './input';

export default function TimeInput() {
const { value: open, toggle, setClose } = useToggle(false);
const { isOpen, toggle, setClose } = useToggle(false);
const [period, setPeriod] = useState<Period>('오전');
const [selectedTime, setSelectedTime] = useState<Date | null>(null);
const [inputValue, setInputValue] = useState(''); // typing 사용

const wrapperRef = useRef<HTMLDivElement>(null);

useClickOutside(wrapperRef, () => {
if (open) setClose();
if (isOpen) setClose();
});

// 시간 업데이트 중앙 관리
Expand Down Expand Up @@ -94,7 +94,7 @@ export default function TimeInput() {
onChange={handleTimeInputChange}
/>

{open && (
{isOpen && (
<div className='z-1 absolute'>
<TimeSelector
onSelect={handleTimeSelect}
Expand Down
121 changes: 75 additions & 46 deletions src/components/ui/pagination/pagination.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import { Icon } from '@/components/ui';
import { cn } from '@/lib/utils/cn';
import { useEffect, useState } from 'react';

const PAGE_GROUP_SIZE = 7;

const BUTTON_ALIGN = 'flex items-center justify-center shrink-0';
interface PaginationProps {
totalItems: number;
currentPage: number;
itemsPerPage: number;
onPageChange: (page: number) => void;
total: number; // 전체 개수 (count)
offset: number; // 현재 offset
limit: number; // 한 페이지당 아이템 수
onPageChange: (next: number) => void; // 새 offset 전달
className?: string;
}
// totalItems 는 API 통신 시 Response 로 받는 """count""" 부모로부터 넘겨주기!

const Pagination = ({ totalItems, currentPage, itemsPerPage, onPageChange }: PaginationProps) => {
/* <Pagination total={count} limit={limit} offset={offset} onPageChange={} */

const Pagination = ({ total, offset, limit, onPageChange, className }: PaginationProps) => {
const [pageGroup, setPageGroup] = useState(0);

const totalPages = totalItems ? Math.ceil(totalItems / itemsPerPage) : 0;
const totalPages = total ? Math.ceil(total / limit) : 0;
const currentPage = Math.floor(offset / limit) + 1; // offset → page 변환

useEffect(() => {
const newGroup = Math.floor((currentPage - 1) / PAGE_GROUP_SIZE);
Expand All @@ -29,45 +33,70 @@ const Pagination = ({ totalItems, currentPage, itemsPerPage, onPageChange }: Pag
const isPrevDisabled = pageGroup === 0;
const isNextDisabled = (pageGroup + 1) * PAGE_GROUP_SIZE >= totalPages;

/* 이전 그룹으로 이동 */
const handlePrevPage = () => {
if (!isPrevDisabled) {
setPageGroup(prev => Math.max(prev - 1, 0));
}
};

/* 다음 그룹으로 이동 */
const handleNextPage = () => {
if (!isNextDisabled) {
setPageGroup(prev => ((prev + 1) * PAGE_GROUP_SIZE < totalPages ? prev + 1 : prev));
}
};

/* 페이지 클릭 시 offset 계산 후 전달 */
const handlePageClick = (page: number) => {
const newOffset = (page - 1) * limit;
onPageChange(newOffset);
};

return (
<>
<div className='flex h-14 items-center justify-center bg-white tablet:h-16'>
<div className='flex h-8 items-center justify-center gap-[2px] px-3 py-2 tablet:h-10'>
<button className='flex items-center justify-center'>
<Icon
className={`h-5 w-5 ${isPrevDisabled ? 'cursor-default bg-gray-400' : 'cursor-pointer'}`}
onClick={() => !isPrevDisabled && setPageGroup(prev => Math.max(prev - 1, 0))}
iconName='chevronLeft'
ariaLabel='이전'
/>
</button>
{pageNumbers.map(page => (
<button
key={page}
onClick={() => onPageChange(page)}
className={
page === currentPage
? 'h-8 w-8 rounded bg-red-300 text-caption text-white tablet:text-body-s'
: 'h-8 w-8 text-caption tablet:text-body-s'
}
>
{page}
</button>
))}
<button className='flex items-center justify-center'>
<Icon
className={`h-5 w-5 ${isNextDisabled ? 'cursor-default bg-gray-400' : 'cursor-pointer'}`}
onClick={() =>
!isNextDisabled &&
setPageGroup(prev => ((prev + 1) * PAGE_GROUP_SIZE < totalPages ? prev + 1 : prev))
}
iconName='chevronRight'
ariaLabel='이후'
/>
</button>
</div>
</div>
</>
<div className={cn(BUTTON_ALIGN, 'gap-[2px]', className)}>
{/* 이전 */}
<button
className={cn(BUTTON_ALIGN, 'mr-4')}
onClick={handlePrevPage}
disabled={isPrevDisabled}
>
<Icon
iconName='chevronLeft'
ariaLabel='이전'
iconSize='rg'
className={cn(isPrevDisabled ? 'cursor-default bg-gray-400' : 'cursor-pointer bg-black')}
/>
</button>

{/* 페이지 번호 */}
{pageNumbers.map(page => (
<button
key={page}
onClick={() => handlePageClick(page)}
className={cn(
'aspect-square w-8 rounded text-caption tablet:w-10 tablet:text-body-s',
page === currentPage && 'bg-red-300 font-bold text-white'
)}
>
{page}
</button>
))}

{/* 다음 */}
<button
className={cn(BUTTON_ALIGN, 'ml-4')}
onClick={handleNextPage}
disabled={isNextDisabled}
>
<Icon
iconName='chevronRight'
ariaLabel='이후'
iconSize='rg'
className={cn(isNextDisabled ? 'cursor-default bg-gray-400' : 'cursor-pointer bg-black')}
/>
</button>
</div>
);
};

Expand Down