mirror of
http://112.124.100.131/huang.ze/ebiz-dify-ai.git
synced 2025-12-09 19:06:51 +08:00
Feat: new pagination (#11170)
This commit is contained in:
189
web/app/components/base/pagination/pagination.tsx
Normal file
189
web/app/components/base/pagination/pagination.tsx
Normal file
@@ -0,0 +1,189 @@
|
||||
import React from 'react'
|
||||
import clsx from 'clsx'
|
||||
import usePagination from './hook'
|
||||
import type {
|
||||
ButtonProps,
|
||||
IPagination,
|
||||
IPaginationProps,
|
||||
PageButtonProps,
|
||||
} from './type'
|
||||
|
||||
const defaultState: IPagination = {
|
||||
currentPage: 0,
|
||||
setCurrentPage: () => {},
|
||||
truncableText: '...',
|
||||
truncableClassName: '',
|
||||
pages: [],
|
||||
hasPreviousPage: false,
|
||||
hasNextPage: false,
|
||||
previousPages: [],
|
||||
isPreviousTruncable: false,
|
||||
middlePages: [],
|
||||
isNextTruncable: false,
|
||||
nextPages: [],
|
||||
}
|
||||
|
||||
const PaginationContext: React.Context<IPagination> = React.createContext<IPagination>(defaultState)
|
||||
|
||||
export const PrevButton = ({
|
||||
className,
|
||||
children,
|
||||
dataTestId,
|
||||
as = <button />,
|
||||
...buttonProps
|
||||
}: ButtonProps) => {
|
||||
const pagination = React.useContext(PaginationContext)
|
||||
const previous = () => {
|
||||
if (pagination.currentPage + 1 > 1)
|
||||
pagination.setCurrentPage(pagination.currentPage - 1)
|
||||
}
|
||||
|
||||
const disabled = pagination.currentPage === 0
|
||||
|
||||
return (
|
||||
<as.type
|
||||
{...buttonProps}
|
||||
{...as.props}
|
||||
className={clsx(className, as.props.className)}
|
||||
onClick={() => previous()}
|
||||
tabIndex={disabled ? '-1' : 0}
|
||||
disabled={disabled}
|
||||
data-testid={dataTestId}
|
||||
onKeyPress={(event: React.KeyboardEvent) => {
|
||||
event.preventDefault()
|
||||
if (event.key === 'Enter' && !disabled)
|
||||
previous()
|
||||
}}
|
||||
>
|
||||
{as.props.children ?? children}
|
||||
</as.type>
|
||||
)
|
||||
}
|
||||
|
||||
export const NextButton = ({
|
||||
className,
|
||||
children,
|
||||
dataTestId,
|
||||
as = <button />,
|
||||
...buttonProps
|
||||
}: ButtonProps) => {
|
||||
const pagination = React.useContext(PaginationContext)
|
||||
const next = () => {
|
||||
if (pagination.currentPage + 1 < pagination.pages.length)
|
||||
pagination.setCurrentPage(pagination.currentPage + 1)
|
||||
}
|
||||
|
||||
const disabled = pagination.currentPage === pagination.pages.length - 1
|
||||
|
||||
return (
|
||||
<as.type
|
||||
{...buttonProps}
|
||||
{...as.props}
|
||||
className={clsx(className, as.props.className)}
|
||||
onClick={() => next()}
|
||||
tabIndex={disabled ? '-1' : 0}
|
||||
disabled={disabled}
|
||||
data-testid={dataTestId}
|
||||
onKeyPress={(event: React.KeyboardEvent) => {
|
||||
event.preventDefault()
|
||||
if (event.key === 'Enter' && !disabled)
|
||||
next()
|
||||
}}
|
||||
>
|
||||
{as.props.children ?? children}
|
||||
</as.type>
|
||||
)
|
||||
}
|
||||
|
||||
type ITruncableElementProps = {
|
||||
prev?: boolean
|
||||
}
|
||||
|
||||
const TruncableElement = ({ prev }: ITruncableElementProps) => {
|
||||
const pagination: IPagination = React.useContext(PaginationContext)
|
||||
|
||||
const {
|
||||
isPreviousTruncable,
|
||||
isNextTruncable,
|
||||
truncableText,
|
||||
truncableClassName,
|
||||
} = pagination
|
||||
|
||||
return ((isPreviousTruncable && prev === true) || (isNextTruncable && !prev))
|
||||
? (
|
||||
<li className={truncableClassName || undefined}>{truncableText}</li>
|
||||
)
|
||||
: null
|
||||
}
|
||||
|
||||
export const PageButton = ({
|
||||
as = <a />,
|
||||
className,
|
||||
dataTestIdActive,
|
||||
dataTestIdInactive,
|
||||
activeClassName,
|
||||
inactiveClassName,
|
||||
renderExtraProps,
|
||||
}: PageButtonProps) => {
|
||||
const pagination: IPagination = React.useContext(PaginationContext)
|
||||
|
||||
const renderPageButton = (page: number) => (
|
||||
<li key={page}>
|
||||
<as.type
|
||||
data-testid={
|
||||
clsx({
|
||||
[`${dataTestIdActive}`]:
|
||||
dataTestIdActive && pagination.currentPage + 1 === page,
|
||||
[`${dataTestIdInactive}-${page}`]:
|
||||
dataTestIdActive && pagination.currentPage + 1 !== page,
|
||||
}) || undefined
|
||||
}
|
||||
tabIndex={0}
|
||||
onKeyPress={(event: React.KeyboardEvent) => {
|
||||
if (event.key === 'Enter')
|
||||
pagination.setCurrentPage(page - 1)
|
||||
}}
|
||||
onClick={() => pagination.setCurrentPage(page - 1)}
|
||||
className={clsx(
|
||||
className,
|
||||
pagination.currentPage + 1 === page
|
||||
? activeClassName
|
||||
: inactiveClassName,
|
||||
)}
|
||||
{...as.props}
|
||||
{...(renderExtraProps ? renderExtraProps(page) : {})}
|
||||
>
|
||||
{page}
|
||||
</as.type>
|
||||
</li>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
{pagination.previousPages.map(renderPageButton)}
|
||||
<TruncableElement prev />
|
||||
{pagination.middlePages.map(renderPageButton)}
|
||||
<TruncableElement />
|
||||
{pagination.nextPages.map(renderPageButton)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const Pagination = ({
|
||||
dataTestId,
|
||||
...paginationProps
|
||||
}: IPaginationProps & { dataTestId?: string }) => {
|
||||
const pagination = usePagination(paginationProps)
|
||||
|
||||
return (
|
||||
<PaginationContext.Provider value={pagination}>
|
||||
<div className={paginationProps.className} data-testid={dataTestId}>
|
||||
{paginationProps.children}
|
||||
</div>
|
||||
</PaginationContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
Pagination.PrevButton = PrevButton
|
||||
Pagination.NextButton = NextButton
|
||||
Pagination.PageButton = PageButton
|
||||
Reference in New Issue
Block a user