diff --git a/app/components/ui/Pagination.tsx b/app/components/ui/Pagination.tsx new file mode 100644 index 0000000..152dc0d --- /dev/null +++ b/app/components/ui/Pagination.tsx @@ -0,0 +1,141 @@ +'use client'; + +import React, { useRef, useEffect, useState } from 'react'; +import { motion } from 'framer-motion'; + +interface PaginationProps { + totalPages: number; + currentPage: number; + onPageChange: (page: number) => void; + className?: string; + maxVisiblePages?: number; +} + +export default function Pagination({ + totalPages, + currentPage, + onPageChange, + className = '', + maxVisiblePages = 7, +}: PaginationProps) { + const buttonRefs = useRef<(HTMLButtonElement | null)[]>([]); + const [underlineStyle, setUnderlineStyle] = useState<{ left: number; width: number }>({ + left: 0, + width: 0, + }); + const [isMounted, setIsMounted] = useState(false); + + useEffect(() => { + setIsMounted(true); + }, []); + + useEffect(() => { + // Reset refs array when total pages change + buttonRefs.current = buttonRefs.current.slice(0, totalPages); + }, [totalPages]); + + useEffect(() => { + if (!isMounted) return; + const currentBtn = buttonRefs.current[currentPage - 1]; + if (currentBtn && currentBtn.parentElement) { + const rect = currentBtn.getBoundingClientRect(); + const parentRect = currentBtn.parentElement.getBoundingClientRect(); + setUnderlineStyle({ + left: rect.left - parentRect.left, + width: rect.width, + }); + } + }, [currentPage, totalPages, isMounted]); + + const generatePages = () => { + if (totalPages <= maxVisiblePages) return Array.from({ length: totalPages }, (_, i) => i + 1); + + const pages: (number | -1)[] = []; + const first = 1; + const last = totalPages; + const sideCount = 1; + const middleCount = maxVisiblePages - 2 * sideCount - 2; + + pages.push(first); + + let left = Math.max(currentPage - Math.floor(middleCount / 2), sideCount + 1); + let right = Math.min(currentPage + Math.floor(middleCount / 2), totalPages - sideCount); + + if (left > sideCount + 1) pages.push(-1); + else left = sideCount + 1; + + for (let i = left; i <= right; i++) pages.push(i); + + if (right < totalPages - sideCount) pages.push(-1); + + pages.push(last); + + return pages; + }; + + if (totalPages <= 1) return null; + + const pagesToShow = generatePages(); + + return ( +
+ + +
+ {pagesToShow.map((pageNum, i) => + pageNum === -1 ? ( + + ) : ( + + ) + )} + + {isMounted && ( + + )} +
+ + +
+ ); +} diff --git a/app/page.tsx b/app/page.tsx index 09e17c6..d88ecbc 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -16,6 +16,8 @@ import ConvertXIcon from './components/theme/Icon/convertXIcon'; import GenieIcon from './components/theme/Icon/genieIcon'; import DevUtilsIcon from './components/theme/Icon/devUtilsIcon'; import { useTheme } from './contexts/themeContext'; +import Pagination from './components/ui/Pagination'; +import { motion } from 'framer-motion'; const CATEGORY_GROUPS = [ 'Text Lab', @@ -134,6 +136,11 @@ const Page = () => { const [selectedBasis, setSelectedBasis] = useState('All'); const [favorites, setFavorites] = useState([]); const [showFavoritesOnly, setShowFavoritesOnly] = useState(false); + const [currentPage, setCurrentPage] = useState(1); + + useEffect(() => { + setCurrentPage(1); + }, [selectedCategory, selectedBasis, showFavoritesOnly]); useEffect(() => { try { @@ -178,11 +185,13 @@ const Page = () => { setValue('txtSearch', ''); setSearchTerm(''); setIsSearch(false); + setCurrentPage(1); }; const handleSearchChange = (event: any) => { setSearchTerm(event.target.value); setIsSearch(event.target.value.length > 0); + setCurrentPage(1); }; const allItems = Object.entries( @@ -243,6 +252,13 @@ const Page = () => { {} as Record ); + const pageSize = isSearch ? 9 : 30; + const totalPages = Math.ceil(filteredItems.length / pageSize); + const paginatedItems = filteredItems.slice( + (currentPage - 1) * pageSize, + currentPage * pageSize + ); + return ( <> { No tools found ) : ( -
- {filteredItems.map((item: any, index: number) => ( - + + {paginatedItems.map((item: any, index: number) => ( + { {item?.__group} • {item?.__basis}
- ))} - + ))} + + {filteredItems.length > pageSize && ( + + )} + )}