mirror of
				https://github.com/langgenius/dify.git
				synced 2025-10-31 02:42:59 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			191 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			191 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import React from 'react'
 | |
| import clsx from 'clsx'
 | |
| import usePagination from './hook'
 | |
| import type {
 | |
|   ButtonProps,
 | |
|   IPagination,
 | |
|   IPaginationProps,
 | |
|   PageButtonProps,
 | |
| } from './type'
 | |
| import { noop } from 'lodash-es'
 | |
| 
 | |
| const defaultState: IPagination = {
 | |
|   currentPage: 0,
 | |
|   setCurrentPage: noop,
 | |
|   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
 | 
