shadcn/ui 动态 pagination

Category:

知识

Tags:

nextjs
typescript
react

最近由于在 nextui 和 shadcn/ui 做切换,遇到些问题,由于 shadcn/ui 灵活性高,导致没有 pagination 动态配置功能,所以基于以下链接封装了pagination组件 AwsomePagination。

loading bookmark...

首先

以上链接的 buildLink 是用的query的自适应,而对于的的需求都是用 params的形式去设计,所以调整为新的函数

typescript
const buildLink = useCallback((newPage: number) => { return `${parentPath}/${newPage}`; }, []);

我的component基本运用于服务端组件,所以不需要任何依赖,parentPath则为 分页页面的父路由。

然后

renderPageNumbers也做了一些调整

typescript
const renderPageNumbers = useCallback(() => { const items: ReactNode[] = []; if (totalPageCount <= maxVisiblePages) { for (let i = 1; i <= totalPageCount; i++) { items.push( <PaginationItem key={i}> <PaginationLink href={buildLink(i)} isActive={page === i}> {i} </PaginationLink> </PaginationItem> ); } } else { items.push( <PaginationItem key={1}> <PaginationLink href={buildLink(1)} isActive={page === 1}> 1 </PaginationLink> </PaginationItem> ); if (page > 3) { items.push( <PaginationItem key="ellipsis-start"> <PaginationLink href={buildLink(page - 2)}> <NavigationDots type="left" /> </PaginationLink> </PaginationItem> ); } const start = Math.max(2, page - 1); const end = Math.min(totalPageCount - 1, page + 1); for (let i = start; i <= end; i++) { items.push( <PaginationItem key={i}> <PaginationLink href={buildLink(i)} isActive={page === i}> {i} </PaginationLink> </PaginationItem> ); } if (page < totalPageCount - 2) { items.push( <PaginationItem key="ellipsis-end"> <PaginationLink href={buildLink(page + 2)}> <NavigationDots type="right" /> </PaginationLink> </PaginationItem> ); } items.push( <PaginationItem key={totalPageCount}> <PaginationLink href={buildLink(totalPageCount)} isActive={page === totalPageCount}> {totalPageCount} </PaginationLink> </PaginationItem> ); } return items; }, []);

这里原文章用 pagination的 dots组件展示样式,并没有jump功能,这里实现了 hover 变 jump 功能。指定 buildLink 去 实现对应跳转

typescript
export function NavigationDots({ type }: { type: "left" | "right" }) { const wrapperRef = useRef<HTMLDivElement>(null); const isHovered = useHover(wrapperRef); const hoverDom = useMemo(() => { if (type === "left") { return <Icon icon={"tabler:arrow-badge-left-filled"} />; } return <Icon icon={"tabler:arrow-badge-right-filled"} />; }, [type]); return ( <div className="cursor-pointer" ref={wrapperRef}> {isHovered ? hoverDom : <Icon icon={"tabler:dots"} />} </div> ); }

这里用到的是 iconify 和 ahooks 去实现 ,hover 图标切换,点击 jump 对应页面

也就是这样用:

typescript
<PaginationItem key="ellipsis-end"> <PaginationLink href={buildLink(page + 2)}> <NavigationDots type="right" /> </PaginationLink> </PaginationItem>

最后

使用:

typescript
<AwsomePagination parentPath="/home" pageSize={6} page={1} totalCount={total} />

源码:

typescript
"use client"; import { Icon } from "@iconify/react/dist/iconify.js"; import { useHover } from "ahooks"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { type ReactNode, useCallback, useMemo, useRef } from "react"; import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious } from "../ui/pagination"; export interface AwsomePaginationProps { totalCount: number; pageSize: number; page: number; maxVisiblePages?: number; parentPath: string; } export function NavigationDots({ type }: { type: "left" | "right" }) { const wrapperRef = useRef<HTMLDivElement>(null); const isHovered = useHover(wrapperRef); const hoverDom = useMemo(() => { if (type === "left") { return <Icon icon={"tabler:arrow-badge-left-filled"} />; } return <Icon icon={"tabler:arrow-badge-right-filled"} />; }, [type]); return ( <div className="cursor-pointer" ref={wrapperRef}> {isHovered ? hoverDom : <Icon icon={"tabler:dots"} />} </div> ); } export function AwsomePagination({ pageSize, totalCount, page, maxVisiblePages = 5, parentPath }: AwsomePaginationProps) { const router = useRouter(); const pathname = usePathname(); const searchParams = useSearchParams(); const totalPageCount = Math.ceil(totalCount / pageSize); const buildLink = useCallback((newPage: number) => { return `${parentPath}/${newPage}`; }, []); const renderPageNumbers = useCallback(() => { const items: ReactNode[] = []; if (totalPageCount <= maxVisiblePages) { for (let i = 1; i <= totalPageCount; i++) { items.push( <PaginationItem key={i}> <PaginationLink href={buildLink(i)} isActive={page === i}> {i} </PaginationLink> </PaginationItem> ); } } else { items.push( <PaginationItem key={1}> <PaginationLink href={buildLink(1)} isActive={page === 1}> 1 </PaginationLink> </PaginationItem> ); if (page > 3) { items.push( <PaginationItem key="ellipsis-start"> <PaginationLink href={buildLink(page - 2)}> <NavigationDots type="left" /> </PaginationLink> </PaginationItem> ); } const start = Math.max(2, page - 1); const end = Math.min(totalPageCount - 1, page + 1); for (let i = start; i <= end; i++) { items.push( <PaginationItem key={i}> <PaginationLink href={buildLink(i)} isActive={page === i}> {i} </PaginationLink> </PaginationItem> ); } if (page < totalPageCount - 2) { items.push( <PaginationItem key="ellipsis-end"> <PaginationLink href={buildLink(page + 2)}> <NavigationDots type="right" /> </PaginationLink> </PaginationItem> ); } items.push( <PaginationItem key={totalPageCount}> <PaginationLink href={buildLink(totalPageCount)} isActive={page === totalPageCount}> {totalPageCount} </PaginationLink> </PaginationItem> ); } return items; }, []); return ( <div className="flex flex-col md:flex-row h-9 overflow-y-hidden items-center gap-3 w-full"> <Pagination> <PaginationContent className="max-sm:gap-0"> <PaginationItem> <PaginationPrevious href={buildLink(Math.max(page - 1, 1))} aria-disabled={page === 1} tabIndex={page === 1 ? -1 : undefined} className={page === 1 ? "pointer-events-none opacity-50" : undefined} /> </PaginationItem> {renderPageNumbers()} <PaginationItem> <PaginationNext href={buildLink(Math.min(page + 1, totalPageCount))} aria-disabled={page === totalPageCount} tabIndex={page === totalPageCount ? -1 : undefined} className={page === totalPageCount ? "pointer-events-none opacity-50" : undefined} /> </PaginationItem> </PaginationContent> </Pagination> </div> ); }

css

css
.body{ color: #F00; }

loading bookmark...