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 && (
+
+ )}
+ >
)}