shadcn/ui 动态 pagination

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

Implementing Pagination in shadcn UI: A Complete Guide

首先

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

tsx 复制代码
  const buildLink = useCallback((newPage: number) => {
    return `${parentPath}/${newPage}`;
  }, []);

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

然后

renderPageNumbers也做了一些调整

tsx 复制代码
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 去 实现对应跳转

tsx 复制代码
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 对应页面

也就是这样用:

tsx 复制代码
<PaginationItem key="ellipsis-end">
   <PaginationLink href={buildLink(page + 2)}>
     <NavigationDots type="right" />
   </PaginationLink>
</PaginationItem>

最后

使用:

tsx 复制代码
 <AwsomePagination parentPath="/home" pageSize={6} page={1} totalCount={total} />

源码:

tsx 复制代码
"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>
  );
}
相关推荐
我是伪码农12 分钟前
Vue 1.26
前端·javascript·vue.js
晚霞的不甘1 小时前
Flutter for OpenHarmony 创意实战:打造一款炫酷的“太空舱”倒计时应用
开发语言·前端·flutter·正则表达式·前端框架·postman
这儿有一堆花1 小时前
CSS 拟真光影设计:从扁平到深度的技术复盘
前端·css
_OP_CHEN2 小时前
【前端开发之CSS】(三)CSS 常用元素属性宝典(上):从字体到文本,手把手教你打造高颜值网页!
前端·css·html·网页开发·文本属性·字体属性·页面美化
你脸上有BUG2 小时前
【工程化】记给ant-design-vue打补丁的示例
前端·javascript·vue.js·补丁·ant-design-vue
灰灰勇闯IT3 小时前
Flutter for OpenHarmony:布局组件实战指南
前端·javascript·flutter
⑩-4 小时前
Vue框架学习
前端·vue.js·学习
a程序小傲4 小时前
京东Java面试被问:基于Gossip协议的最终一致性实现和收敛时间
java·开发语言·前端·数据库·python·面试·状态模式
小二·4 小时前
Python Web 开发进阶实战:AI 原生应用商店 —— 在 Flask + Vue 中构建模型即服务(MaaS)与智能体分发平台
前端·人工智能·python
Devlive 开源社区4 小时前
技术日报|推理RAG文档索引PageIndex登顶日增1374星,React视频工具Remotion二连冠进前二
前端·react.js·前端框架