diff --git a/.gitignore b/.gitignore index a547bf3..578bac1 100644 --- a/.gitignore +++ b/.gitignore @@ -7,16 +7,38 @@ yarn-error.log* pnpm-debug.log* lerna-debug.log* +# Dependencies node_modules + +# Build output dist dist-ssr + +# Environment files +.env +.env.* +!.env.example +!.env.sample +!.env.template + +# Local overrides *.local +# Cache and temp files +.cache +.temp +tmp +temp +.eslintcache +.vite +coverage + # Editor directories and files .vscode/* !.vscode/extensions.json .idea .DS_Store +Thumbs.db *.suo *.ntvs* *.njsproj diff --git a/src/components/PaginationControls.jsx b/src/components/PaginationControls.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/ToolCard.jsx b/src/components/ToolCard.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/data/toolsData.js b/src/data/toolsData.js new file mode 100644 index 0000000..d3a1608 --- /dev/null +++ b/src/data/toolsData.js @@ -0,0 +1,132 @@ +import { + Contrast, + FileEdit, + Hash, + Image as ImageIcon, + Images, + Layers, + LayoutGrid, + Lock, + Minimize2, + RefreshCw, + SplitSquareHorizontal, + Stamp, +} from "lucide-react"; + +export const ALL_TOOLS = [ + { + id: 1, + name: "Merge PDF", + description: + "Combine multiple PDFs into a single document in milliseconds. Drag, drop, and organize securely.", + icon: Layers, + path: "/merge", + buttonText: "Open Merge Tool", + iconSize: "w-6 h-6", + }, + { + id: 2, + name: "Split PDF", + description: + "Extract specific pages or break a massive document down into smaller files instantly.", + icon: SplitSquareHorizontal, + path: "/split", + buttonText: "Open Split Tool", + iconSize: "w-6 h-6", + }, + { + id: 3, + name: "Add Watermark", + description: + "Stamp custom text diagonally across your documents. Perfect for sensitive drafts and contracts.", + icon: Stamp, + path: "/watermark", + buttonText: "Open Watermark Tool", + iconSize: "w-6 h-6", + }, + { + id: 4, + name: "Image to PDF", + description: + "Convert JPG and PNG images into a high-quality PDF document. Drag to reorder your pages.", + icon: ImageIcon, + path: "/image-to-pdf", + buttonText: "Open Image to PDF", + iconSize: "w-6 h-6", + }, + { + id: 5, + name: "Compress PDF", + description: "Reduce file size while maintaining visual quality.", + icon: Minimize2, + path: "/compress", + buttonText: "Open Compress PDF", + iconSize: "w-6 h-6", + }, + { + id: 6, + name: "Rotate PDF", + description: "Rotate pages in your PDF document.", + icon: RefreshCw, + path: "/rotate", + buttonText: "Open Rotate PDF", + iconSize: "w-10 h-10", + }, + { + id: 7, + name: "Organize PDF", + description: "Organize pages in your PDF document.", + icon: LayoutGrid, + path: "/organize", + buttonText: "Open Organize PDF", + iconSize: "w-10 h-10", + }, + { + id: 8, + name: "PDF to Images", + description: "Extract images from your PDF document.", + icon: Images, + path: "/pdf-to-image", + buttonText: "Open PDF to Images", + iconSize: "w-6 h-6", + }, + { + id: 9, + name: "Grayscale PDF", + description: "Convert your PDF to grayscale.", + icon: Contrast, + path: "/grayscale", + buttonText: "Open Grayscale PDF", + iconSize: "w-6 h-6", + }, + { + id: 10, + name: "Page Numbers", + description: + "Auto-stamp sequential numbers on every page footer. Choose position, prefix, and start number.", + icon: Hash, + path: "/page-numbers", + buttonText: "Open Page Numbers", + iconSize: "w-6 h-6", + }, + { + id: 11, + name: "Lock PDF", + description: + "Password-protect your PDF with RC4 encryption. Processed entirely in your browser - nothing is uploaded.", + icon: Lock, + path: "/lock-pdf", + buttonText: "Open Lock PDF", + iconSize: "w-6 h-6", + }, + { + id: 12, + name: "Edit PDF", + description: + "Draw, highlight, add text, and annotate your PDF pages directly in the browser.", + icon: FileEdit, + path: "/edit-pdf", + buttonText: "Open PDF Editor", + iconSize: "w-6 h-6", + }, +]; diff --git a/src/pages/Home/components/ToolsGrid.jsx b/src/pages/Home/components/ToolsGrid.jsx index be8a2c4..43172e6 100644 --- a/src/pages/Home/components/ToolsGrid.jsx +++ b/src/pages/Home/components/ToolsGrid.jsx @@ -1,5 +1,7 @@ -import React from "react"; +import React, { useState } from "react"; import { Link } from "react-router-dom"; +import { motion as Motion } from 'framer-motion'; + import { Layers, SplitSquareHorizontal, @@ -14,358 +16,282 @@ import { Lock, FileEdit, } from "lucide-react"; -import { motion } from "framer-motion"; + +// FIX #1: Move tools data outside component to avoid recreation on every render +const TOOLS_DATA = [ + { + id: 1, + name: "Merge PDF", + description: "Combine multiple PDFs into a single document in milliseconds. Drag, drop, and organize securely.", + icon: Layers, + path: "/merge", + buttonText: "Open Merge Tool", + iconSize: "w-6 h-6", + delay: 0, + }, + { + id: 2, + name: "Split PDF", + description: "Extract specific pages or break a massive document down into smaller files instantly.", + icon: SplitSquareHorizontal, + path: "/split", + buttonText: "Open Split Tool", + iconSize: "w-6 h-6", + delay: 0.1, + }, + { + id: 3, + name: "Add Watermark", + description: "Stamp custom text diagonally across your documents. Perfect for sensitive drafts and contracts.", + icon: Stamp, + path: "/watermark", + buttonText: "Open Watermark Tool", + iconSize: "w-6 h-6", + delay: 0.2, + }, + { + id: 4, + name: "Image to PDF", + description: "Convert JPG and PNG images into a high-quality PDF document. Drag to reorder your pages.", + icon: ImageIcon, + path: "/image-to-pdf", + buttonText: "Open Image to PDF", + iconSize: "w-6 h-6", + delay: 0.3, + }, + { + id: 5, + name: "Compress PDF", + description: "Reduce file size while maintaining visual quality.", + icon: Minimize2, + path: "/compress", + buttonText: "Open Compress PDF", + iconSize: "w-6 h-6", + delay: 0.3, + }, + { + id: 6, + name: "Rotate PDF", + description: "Rotate pages in your PDF document.", + icon: RefreshCw, + path: "/rotate", + buttonText: "Open Rotate PDF", + iconSize: "w-10 h-10", + delay: 0.3, + }, + { + id: 7, + name: "Organize PDF", + description: "Organize pages in your PDF document.", + icon: LayoutGrid, + path: "/organize", + buttonText: "Open Organize PDF", + iconSize: "w-10 h-10", + delay: 0.3, + }, + { + id: 8, + name: "PDF to Images", + description: "Extract images from your PDF document.", + icon: Images, + path: "/pdf-to-image", + buttonText: "Open PDF to Images", + iconSize: "w-6 h-6", + delay: 0.3, + }, + { + id: 9, + name: "Grayscale PDF", + description: "Convert your PDF to grayscale.", + icon: Contrast, + path: "/grayscale", + buttonText: "Open Grayscale PDF", + iconSize: "w-6 h-6", + delay: 0.3, + }, + { + id: 10, + name: "Page Numbers", + description: "Auto-stamp sequential numbers on every page footer. Choose position, prefix, and start number.", + icon: Hash, + path: "/page-numbers", + buttonText: "Open Page Numbers", + iconSize: "w-6 h-6", + delay: 0.4, + }, + { + id: 11, + name: "Lock PDF", + description: "Password-protect your PDF with RC4 encryption. Processed entirely in your browser — nothing is uploaded.", + icon: Lock, + path: "/lock-pdf", + buttonText: "Open Lock PDF", + iconSize: "w-6 h-6", + delay: 0.4, + }, + { + id: 12, + name: "Edit PDF", + description: "Draw, highlight, add text, and annotate your PDF pages directly in the browser.", + icon: FileEdit, + path: "/edit-pdf", + buttonText: "Open PDF Editor", + iconSize: "w-6 h-6", + delay: 0.4, + }, +]; export function ToolsGrid() { - const cardVariants = { + const [currentPage, setCurrentPage] = useState(1); + const toolsPerPage = 6; + const totalPages = Math.ceil(TOOLS_DATA.length / toolsPerPage); + + // FIX #4: Convert cardVariants to a function to use the custom prop for per-card delays + const cardVariants = (delay) => ({ hidden: { opacity: 0, y: 40 }, visible: { opacity: 1, y: 0, - transition: { type: "spring", stiffness: 200, damping: 20 }, + transition: { type: "spring", stiffness: 200, damping: 20, delay }, }, - }; + }); - return ( -
- {/* 1. Merge Card */} - - -
-
- -
-

- Merge PDF -

-

- Combine multiple PDFs into a single document in milliseconds. Drag, - drop, and organize securely. -

-
- Open Merge Tool -
- - + // FIX #3: Rename maxVisible to switchThreshold to clarify its purpose + const getPageNumbers = () => { + const pages = []; + const switchThreshold = 5; // Threshold for switching to ellipsis layout - {/* 2. Split Card */} - - -
-
- -
-

- Split PDF -

-

- Extract specific pages or break a massive document down into smaller - files instantly. -

-
- Open Split Tool -
- - - - {/* 3. Watermark Card */} - - -
-
- -
-

- Add Watermark -

-

- Stamp custom text diagonally across your documents. Perfect for - sensitive drafts and contracts. -

-
- Open Watermark Tool -
- - - - {/* 4. Image to PDF Card */} - - -
-
- -
-

- Image to PDF -

-

- Convert JPG and PNG images into a high-quality PDF document. Drag to - reorder your pages. -

-
- Open Image to PDF -
- - - - {/* 5. Compress PDF Card */} - - -
-
- -
-

- Compress PDF -

-

- Reduce file size while maintaining visual quality. -

-
- Open Compress PDF -
- - + if (totalPages <= switchThreshold) { + for (let i = 1; i <= totalPages; i++) { + pages.push(i); + } + } else { + if (currentPage <= 3) { + for (let i = 1; i <= 4; i++) pages.push(i); + pages.push('...'); + pages.push(totalPages); + } else if (currentPage >= totalPages - 2) { + pages.push(1); + pages.push('...'); + for (let i = totalPages - 3; i <= totalPages; i++) pages.push(i); + } else { + pages.push(1); + pages.push('...'); + for (let i = currentPage - 1; i <= currentPage + 1; i++) pages.push(i); + pages.push('...'); + pages.push(totalPages); + } + } + return pages; + }; - {/* 6. Rotate PDF Card */} - - -
-
- -
-

- Rotate PDF -

-

- Rotate pages in your PDF document. -

-
- Open Rotate PDF -
- - + const handlePageChange = (page) => { + setCurrentPage(page); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }; - {/* 7. Organize PDF Card */} - - -
-
- -
-

- Organize PDF -

-

- Organize pages in your PDF document. -

-
- Open Organize PDF -
- - + const startIdx = (currentPage - 1) * toolsPerPage; + const endIdx = startIdx + toolsPerPage; + const currentTools = TOOLS_DATA.slice(startIdx, endIdx); - {/* 8. PDF to Images Card */} - - -
-
- , -
-

- PDF to Images -

-

- Extract images from your PDF document. -

-
- Open PDF to Images -
- - + return ( +
+ {/* Tools Grid */} +
+ {currentTools.map((tool) => { + const IconComponent = tool.icon; + return ( + + +
+
+ +
+

+ {tool.name} +

+

+ {tool.description} +

+
+ {tool.buttonText} +
+ + + ); + })} +
- {/* 9. Grayscale Card */} - - -
-
- -
-

- Grayscale PDF -

-

- Convert your PDF to grayscale. -

-
- Open Grayscale PDF -
- - + {/* FIX #5: Add responsive pagination controls with flex-wrap and responsive padding */} + {totalPages > 1 && ( +
+
+ {/* Previous Button */} + - {/* 10. Page Numbers Card */} - - -
-
- -
-

- Page Numbers -

-

- Auto-stamp sequential numbers on every page footer. Choose position, prefix, and start number. -

-
- Open Page Numbers -
- - + {/* Page Numbers */} +
+ {getPageNumbers().map((page, index) => ( + page === '...' ? ( + + ... + + ) : ( + + ) + ))} +
- {/* 11. Lock PDF Card */} - - -
-
- + {/* Next Button */} +
-

- Lock PDF -

-

- Password-protect your PDF with RC4 encryption. Processed entirely in your browser — nothing is uploaded. -

-
- Open Lock PDF -
- - - {/* 12. Edit PDF Card */} - - -
-
- -
-

- Edit PDF -

-

- Draw, highlight, add text, and annotate your PDF pages directly in the browser. -

-
- Open PDF Editor + {/* Page Info */} +
+ Page {currentPage} of {totalPages}
- - +
+ )}
); } diff --git a/src/pages/Rotate/Rotate.jsx b/src/pages/Rotate/Rotate.jsx index 14932e6..f7caf62 100644 --- a/src/pages/Rotate/Rotate.jsx +++ b/src/pages/Rotate/Rotate.jsx @@ -43,7 +43,7 @@ function usePdfPages(file) { })(); return () => { cancelled = true; }; - }, [file]); // eslint-disable-line + }, [file]); async function loadBatch(pdf, from, to, cancelled) { setIsLoading(true); @@ -76,7 +76,7 @@ function usePdfPages(file) { if (from > pdf.numPages) return; const to = Math.min(from + PAGE_BATCH - 1, pdf.numPages); loadBatch(pdf, from, to, false); - }, [isLoading]); // eslint-disable-line + }, [isLoading]); const hasMore = loadedRef.current < totalCount;