238 lines
6.2 KiB
TypeScript
238 lines
6.2 KiB
TypeScript
"use client";
|
|
|
|
import type { ButtonProps, TextProps } from "@chakra-ui/react";
|
|
import {
|
|
Button,
|
|
Pagination as ChakraPagination,
|
|
HStack,
|
|
IconButton,
|
|
Text,
|
|
createContext,
|
|
usePaginationContext,
|
|
} from "@chakra-ui/react";
|
|
import * as React from "react";
|
|
import { HiChevronLeft, HiChevronRight } from "react-icons/hi2";
|
|
import { LinkButton } from "./link-button";
|
|
|
|
interface ButtonVariantMap {
|
|
current: ButtonProps["variant"];
|
|
default: ButtonProps["variant"];
|
|
ellipsis: ButtonProps["variant"];
|
|
}
|
|
|
|
type PaginationVariant = "outline" | "solid" | "subtle";
|
|
|
|
interface ButtonVariantContext {
|
|
size: ButtonProps["size"];
|
|
variantMap: ButtonVariantMap;
|
|
getHref?: (page: number) => string;
|
|
}
|
|
|
|
const [RootPropsProvider, useRootProps] = createContext<ButtonVariantContext>({
|
|
name: "RootPropsProvider",
|
|
});
|
|
|
|
export interface PaginationRootProps
|
|
extends Omit<ChakraPagination.RootProps, "type"> {
|
|
size?: ButtonProps["size"];
|
|
variant?: PaginationVariant;
|
|
getHref?: (page: number) => string;
|
|
}
|
|
|
|
const variantMap: Record<PaginationVariant, ButtonVariantMap> = {
|
|
outline: { default: "ghost", ellipsis: "plain", current: "solid" },
|
|
solid: { default: "outline", ellipsis: "outline", current: "solid" },
|
|
subtle: { default: "ghost", ellipsis: "plain", current: "subtle" },
|
|
};
|
|
|
|
export const PaginationRoot = React.forwardRef<
|
|
HTMLDivElement,
|
|
PaginationRootProps
|
|
>(function PaginationRoot(props, ref) {
|
|
const { size = "sm", variant = "outline", getHref, ...rest } = props;
|
|
return (
|
|
<RootPropsProvider
|
|
value={{ size, variantMap: variantMap[variant], getHref }}
|
|
>
|
|
<ChakraPagination.Root
|
|
ref={ref}
|
|
type={getHref ? "link" : "button"}
|
|
{...rest}
|
|
/>
|
|
</RootPropsProvider>
|
|
);
|
|
});
|
|
|
|
export const PaginationEllipsis = React.forwardRef<
|
|
HTMLDivElement,
|
|
ChakraPagination.EllipsisProps
|
|
>(function PaginationEllipsis(props, ref) {
|
|
const { size, variantMap } = useRootProps();
|
|
return (
|
|
<ChakraPagination.Ellipsis ref={ref} {...props} asChild>
|
|
<Button as="span" variant={variantMap.ellipsis} size={size}>
|
|
...
|
|
</Button>
|
|
</ChakraPagination.Ellipsis>
|
|
);
|
|
});
|
|
|
|
export const PaginationItem = React.forwardRef<
|
|
HTMLButtonElement,
|
|
ChakraPagination.ItemProps
|
|
>(function PaginationItem(props, ref) {
|
|
const { page } = usePaginationContext();
|
|
const { size, variantMap, getHref } = useRootProps();
|
|
|
|
const current = page === props.value;
|
|
const variant = current ? variantMap.current : variantMap.default;
|
|
|
|
if (getHref) {
|
|
return (
|
|
<LinkButton
|
|
href={getHref(props.value)}
|
|
variant={variant}
|
|
size={size}
|
|
bg={current ? "#02A0A0" : "white"}
|
|
color={current ? "black" : "black"}
|
|
_hover={{ bg: "#02A0A0", color: "black" }}
|
|
>
|
|
{props.value}
|
|
</LinkButton>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<ChakraPagination.Item ref={ref} {...props} asChild>
|
|
<Button
|
|
bg={current ? "#F5F8FB" : "white"}
|
|
color={current ? "black" : "black"}
|
|
variant={variant}
|
|
size={size}
|
|
_hover={{ bg: "#F5F8FB", color: "black" }}
|
|
border="none"
|
|
>
|
|
{props.value}
|
|
</Button>
|
|
</ChakraPagination.Item>
|
|
);
|
|
});
|
|
|
|
export const PaginationPrevTrigger = React.forwardRef<
|
|
HTMLButtonElement,
|
|
ChakraPagination.PrevTriggerProps
|
|
>(function PaginationPrevTrigger(props, ref) {
|
|
const { size, variantMap, getHref } = useRootProps();
|
|
const { previousPage } = usePaginationContext();
|
|
|
|
if (getHref) {
|
|
return (
|
|
<LinkButton
|
|
href={previousPage != null ? getHref(previousPage) : undefined}
|
|
variant={variantMap.default}
|
|
size={size}
|
|
_hover={{ bg: "#F5F8FB", color: "black" }}
|
|
>
|
|
<HiChevronLeft />
|
|
</LinkButton>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<ChakraPagination.PrevTrigger ref={ref} asChild {...props}>
|
|
<IconButton variant={variantMap.default} size={size}>
|
|
<HiChevronLeft />
|
|
</IconButton>
|
|
</ChakraPagination.PrevTrigger>
|
|
);
|
|
});
|
|
|
|
export const PaginationNextTrigger = React.forwardRef<
|
|
HTMLButtonElement,
|
|
ChakraPagination.NextTriggerProps
|
|
>(function PaginationNextTrigger(props, ref) {
|
|
const { size, variantMap, getHref } = useRootProps();
|
|
const { nextPage } = usePaginationContext();
|
|
|
|
if (getHref) {
|
|
return (
|
|
<LinkButton
|
|
href={nextPage != null ? getHref(nextPage) : undefined}
|
|
variant={variantMap.default}
|
|
// size={size}
|
|
borderRadius="94px"
|
|
bg="#F5F8FB"
|
|
color="white"
|
|
w="136px"
|
|
fontSize="sm"
|
|
_hover={{ bg: "#026060" }}
|
|
>
|
|
Next
|
|
</LinkButton>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<ChakraPagination.NextTrigger ref={ref} asChild {...props}>
|
|
<Text
|
|
w="136px"
|
|
// borderRadius="94px"
|
|
// bg="#02A0A0"
|
|
color="black"
|
|
// variant={variantMap.default}
|
|
// size={size}
|
|
cursor="pointer"
|
|
// _hover={{ bg: "#026060" }}
|
|
display="flex"
|
|
gap="12px"
|
|
alignItems="center"
|
|
fontSize="xs"
|
|
justifyContent="center"
|
|
>
|
|
Next
|
|
<HiChevronRight />
|
|
</Text>
|
|
</ChakraPagination.NextTrigger>
|
|
);
|
|
});
|
|
|
|
export const PaginationItems = (props: React.HTMLAttributes<HTMLElement>) => {
|
|
return (
|
|
<ChakraPagination.Context>
|
|
{({ pages }) =>
|
|
pages.map((page, index) => (
|
|
<PaginationItem
|
|
key={index}
|
|
type="page"
|
|
value={page.value}
|
|
{...props}
|
|
/>
|
|
))
|
|
}
|
|
</ChakraPagination.Context>
|
|
);
|
|
};
|
|
|
|
interface PageTextProps extends TextProps {
|
|
format?: "short" | "compact" | "long";
|
|
}
|
|
|
|
export const PaginationPageText = React.forwardRef<
|
|
HTMLParagraphElement,
|
|
PageTextProps
|
|
>(function PaginationPageText(props, ref) {
|
|
const { format = "compact", ...rest } = props;
|
|
const { page, totalPages, pageRange, count } = usePaginationContext();
|
|
const content = React.useMemo(() => {
|
|
if (format === "short") return `${page} / ${totalPages}`;
|
|
if (format === "compact") return `${page} of ${totalPages}`;
|
|
return `${pageRange.start + 1} - ${Math.min(pageRange.end, count)} of ${count}`;
|
|
}, [format, page, totalPages, pageRange, count]);
|
|
|
|
return (
|
|
<Text fontWeight="medium" ref={ref} {...rest}>
|
|
{content}
|
|
</Text>
|
|
);
|
|
});
|