Skip to content

Commit

Permalink
feat: added shadcn-pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
Artsiom Voitas committed Jan 9, 2024
1 parent 1f15066 commit 1a1ec23
Show file tree
Hide file tree
Showing 7 changed files with 903 additions and 392 deletions.
995 changes: 680 additions & 315 deletions package-lock.json

Large diffs are not rendered by default.

41 changes: 20 additions & 21 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,38 @@
"lint": "next lint"
},
"dependencies": {
"@clerk/nextjs": "^4.27.2",
"@clerk/nextjs": "^4.29.2",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-slot": "^1.0.2",
"axios": "^1.6.2",
"axios": "^1.6.5",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"framer-motion": "^10.16.5",
"geist": "^1.1.0",
"lucide-react": "^0.292.0",
"next": "14.0.3",
"clsx": "^2.1.0",
"framer-motion": "^10.17.9",
"geist": "^1.2.0",
"lucide-react": "^0.307.0",
"next": "14.0.4",
"next-themes": "^0.2.1",
"react": "^18.2.0",
"react-animated-heart": "^0.0.8",
"react-dom": "^18.2.0",
"react-paginate": "^8.2.0",
"react-responsive-masonry": "^2.1.7",
"tailwind-merge": "^2.0.0",
"tailwind-merge": "^2.2.0",
"tailwindcss-animate": "^1.0.7",
"usehooks-ts": "^2.9.1"
"usehooks-ts": "^2.9.2"
},
"devDependencies": {
"@types/node": "^20.9.4",
"@types/react": "^18.2.38",
"@types/react-dom": "^18.2.17",
"@types/node": "^20.10.7",
"@types/react": "^18.2.47",
"@types/react-dom": "^18.2.18",
"@types/react-responsive-masonry": "^2.1.3",
"autoprefixer": "^10.4.16",
"eslint": "^8.54.0",
"eslint-config-next": "14.0.3",
"eslint-config-prettier": "^9.0.0",
"postcss": "^8.4.31",
"prettier": "^3.1.0",
"prettier-plugin-tailwindcss": "^0.5.7",
"tailwindcss": "^3.3.5",
"typescript": "^5.3.2"
"eslint": "^8.56.0",
"eslint-config-next": "14.0.4",
"eslint-config-prettier": "^9.1.0",
"postcss": "^8.4.33",
"prettier": "^3.1.1",
"prettier-plugin-tailwindcss": "^0.5.11",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3"
}
}
4 changes: 2 additions & 2 deletions src/components/FavoriteImages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default function FavoriteImages() {
const [isLoading, setIsLoading] = useState<boolean>(true);

useEffect(() => {
if (favorites.length > 0) {
if (favorites) {
setTimeout(() => {
setIsLoading(false);
}, 1500);
Expand All @@ -20,7 +20,7 @@ export default function FavoriteImages() {
return (
<>
<h1 className="mb-8 mt-4 flex scroll-m-20 justify-center text-4xl font-extrabold tracking-tight lg:text-5xl">
Your Favorite pictures
{`Your Favorite pictures ${favorites.length < 1 ? 'are empty, for now!' : ''}`}
</h1>
{isLoading ? <SkeletonImages /> : <ImagesCards images={favorites} />}
</>
Expand Down
5 changes: 1 addition & 4 deletions src/components/FoundImages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@ import { unsplashBaseUrl, unsplashKey } from '@/lib/utils';
import axios from 'axios';
import { useSearchParams } from 'next/navigation';
import { useEffect, useState } from 'react';
import Masonry, { ResponsiveMasonry } from 'react-responsive-masonry';
import ImageCard from './ImageCard';
import SkeletonImages from './SkeletonImages';
import PaginatedItems from './ui/pagination';
import ImagesCards from './ImagesCards';
import SkeletonImages from './SkeletonImages';

export interface UnsplashRespond {
id: string;
Expand Down
95 changes: 95 additions & 0 deletions src/components/ImagesCardPagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious
} from '@/components/ui/pagination';

interface ImagesCardPaginationProps {
currentCollection: string;
currentOrder: string;
pagesAmount: number;
currentPage: number;
}

export function ImagesCardPagination({
pagesAmount,
currentPage,
currentCollection,
currentOrder
}: ImagesCardPaginationProps) {
const pageNumbers = [];
for (let i = 1; i <= pagesAmount; i++) {
pageNumbers.push(i);
}
const previousPage: number = currentPage > 1 ? currentPage - 1 : 1;
const nextPage: number = currentPage < pagesAmount ? currentPage + 1 : pagesAmount;

const maxPageNum = 3;
const pageNumLimit = Math.floor(maxPageNum / 2);

let activePages = pageNumbers.slice(
Math.max(0, currentPage - 1 - pageNumLimit),
Math.min(currentPage - 1 + pageNumLimit + 1, pageNumbers.length)
);

const renderPages = () => {
const renderedPages = activePages.map((page, idx) => (
<PaginationItem key={idx}>
<PaginationLink
isActive={currentPage === page}
href={`/?collection=${currentCollection}&page=${page}&order_by=${currentOrder}`}>
{page}
</PaginationLink>
</PaginationItem>
));

if (activePages[0] > 1) {
if (activePages[0] !== 2) {
renderedPages.unshift(<PaginationEllipsis key="ellipsis-start" />);
}
renderedPages.unshift(
<PaginationLink
href={`/?collection=${currentCollection}&page=${1}&order_by=${currentOrder}`}>
{1}
</PaginationLink>
);
}

if (activePages[activePages.length - 1] < pageNumbers.length) {
if (activePages[activePages.length - 1] !== pageNumbers.length - 1)
renderedPages.push(<PaginationEllipsis key="ellipsis-end" />);
renderedPages.push(
<PaginationLink
href={`/?collection=${currentCollection}&page=${pagesAmount}&order_by=${currentOrder}`}>
{pagesAmount}
</PaginationLink>
);
}

return renderedPages;
};

return (
<Pagination className="my-8">
<PaginationContent>
<PaginationItem>
<PaginationPrevious
href={`/?collection=${currentCollection}&page=${previousPage}&order_by=${currentOrder}`}
/>
</PaginationItem>

{renderPages()}

<PaginationItem>
<PaginationNext
href={`/?collection=${currentCollection}&page=${nextPage}&order_by=${currentOrder}`}
/>
</PaginationItem>
</PaginationContent>
</Pagination>
);
}
6 changes: 3 additions & 3 deletions src/components/ImagesCards.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Masonry, { ResponsiveMasonry } from 'react-responsive-masonry';
import { UnsplashRespond } from './FoundImages';
import ImageCard from './ImageCard';
import PaginatedItems from './ui/pagination';
import { ImagesCardPagination } from './ImagesCardPagination';

interface ImagesCardsProps {
images: UnsplashRespond[];
Expand All @@ -19,10 +19,10 @@ export default function ImagesCards({
}: ImagesCardsProps) {
const pagination =
query && orderBy && page && pagesAmount ? (
<PaginatedItems
pagesAmount={pagesAmount}
<ImagesCardPagination
currentCollection={query}
currentOrder={orderBy}
pagesAmount={pagesAmount}
currentPage={Number(page)}
/>
) : (
Expand Down
149 changes: 102 additions & 47 deletions src/components/ui/pagination.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,104 @@
'use client';

import * as React from 'react';
import { ChevronLeft, ChevronRight, MoreHorizontal } from 'lucide-react';
import { useRouter } from 'next/navigation';
import ReactPaginate from 'react-paginate';

interface PaginationProps {
currentCollection: string;
currentOrder: string;
pagesAmount: number;
currentPage: number;
}

export default function Pagination({
pagesAmount,
currentPage,
currentCollection,
currentOrder
}: PaginationProps) {
const { push } = useRouter();
const page: number = currentPage - 1;

const handlePageClick = (event: any) => {
const newPage = event.selected + 1;
push(`/?collection=${currentCollection}&page=${newPage}&order_by=${currentOrder}`);
};

return (
<ReactPaginate
className="my-8 flex items-center justify-center gap-1 sm:gap-4"
breakLabel={<MoreHorizontal size={18} />}
nextLabel={<ChevronRight size={18} />}
onPageChange={handlePageClick}
marginPagesDisplayed={1}
pageRangeDisplayed={5}
pageCount={pagesAmount}
previousLabel={<ChevronLeft size={18} />}
forcePage={page}
renderOnZeroPageCount={null}
pageClassName={
'w-6 h-6 sm:w-8 sm:h-8 p-4 flex items-center justify-center hover:opacity-[70%] bg-black dark:bg-white text-white dark:text-black rounded-xl cursor-pointer'
}
activeClassName={'opacity-[50%] hover:cursor-default hover:opacity-[50%]'}
activeLinkClassName={'hover:cursor-default'}
disabledClassName={'opacity-[50%] hover:cursor-default'}
disabledLinkClassName={'hover:cursor-default'}

import { cn } from '@/lib/utils';
import Link from 'next/link';

import { ButtonProps, buttonVariants } from '@/components/ui/button';

const Pagination = ({ className, ...props }: React.ComponentProps<'nav'>) => (
<nav
role="navigation"
aria-label="pagination"
className={cn('mx-auto flex w-full justify-center', className)}
{...props}
/>
);

const PaginationContent = React.forwardRef<HTMLUListElement, React.ComponentProps<'ul'>>(
({ className, ...props }, ref) => (
<ul
ref={ref}
className={cn('flex flex-row items-center gap-1', className)}
{...props}
/>
)
);
PaginationContent.displayName = 'PaginationContent';

const PaginationItem = React.forwardRef<HTMLLIElement, React.ComponentProps<'li'>>(
({ className, ...props }, ref) => (
<li
ref={ref}
className={cn('', className)}
{...props}
/>
);
}
)
);
PaginationItem.displayName = 'PaginationItem';

type PaginationLinkProps = {
isActive?: boolean;
} & Pick<ButtonProps, 'size'> &
React.ComponentProps<typeof Link>;

const PaginationLink = ({ className, isActive, size = 'sm', ...props }: PaginationLinkProps) => (
<PaginationItem>
<Link
aria-current={isActive ? 'page' : undefined}
className={cn(
buttonVariants({
variant: isActive ? 'outline' : 'ghost',
size
}),
className
)}
{...props}
/>
</PaginationItem>
);
PaginationLink.displayName = 'PaginationLink';

const PaginationPrevious = ({
className,
...props
}: React.ComponentProps<typeof PaginationLink>) => (
<PaginationLink
aria-label="Go to previous page"
size="sm"
className={cn('gap-1', className)}
{...props}>
<ChevronLeft className="h-4 w-4" />
</PaginationLink>
);
PaginationPrevious.displayName = 'PaginationPrevious';

const PaginationNext = ({ className, ...props }: React.ComponentProps<typeof PaginationLink>) => (
<PaginationLink
aria-label="Go to next page"
size="sm"
className={cn('gap-1', className)}
{...props}>
<ChevronRight className="h-4 w-4" />
</PaginationLink>
);

const PaginationEllipsis = ({ className, ...props }: React.ComponentProps<'span'>) => (
<span
aria-hidden
className={cn('flex h-9 w-9 items-center justify-center', className)}
{...props}>
<MoreHorizontal className="h-4 w-4" />
<span className="sr-only">More pages</span>
</span>
);

export {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious
};

0 comments on commit 1a1ec23

Please sign in to comment.