You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

преди 11 месеца
преди 11 месеца
преди 11 месеца
преди 11 месеца
преди 11 месеца
преди 11 месеца
преди 11 месеца
преди 11 месеца
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. import React from 'react'
  2. import cn from 'classnames'
  3. import usePagination from './hook'
  4. import type {
  5. ButtonProps,
  6. IPagination,
  7. IPaginationProps,
  8. PageButtonProps,
  9. } from './type'
  10. import { noop } from 'lodash-es'
  11. const defaultState: IPagination = {
  12. currentPage: 0,
  13. setCurrentPage: noop,
  14. truncableText: '...',
  15. truncableClassName: '',
  16. pages: [],
  17. hasPreviousPage: false,
  18. hasNextPage: false,
  19. previousPages: [],
  20. isPreviousTruncable: false,
  21. middlePages: [],
  22. isNextTruncable: false,
  23. nextPages: [],
  24. }
  25. const PaginationContext: React.Context<IPagination> = React.createContext<IPagination>(defaultState)
  26. export const PrevButton = ({
  27. className,
  28. children,
  29. dataTestId,
  30. as = <button />,
  31. ...buttonProps
  32. }: ButtonProps) => {
  33. const pagination = React.useContext(PaginationContext)
  34. const previous = () => {
  35. if (pagination.currentPage + 1 > 1)
  36. pagination.setCurrentPage(pagination.currentPage - 1)
  37. }
  38. const disabled = pagination.currentPage === 0
  39. return (
  40. <as.type
  41. {...buttonProps}
  42. {...as.props}
  43. className={cn(className, as.props.className)}
  44. onClick={() => previous()}
  45. tabIndex={disabled ? '-1' : 0}
  46. disabled={disabled}
  47. data-testid={dataTestId}
  48. onKeyPress={(event: React.KeyboardEvent) => {
  49. event.preventDefault()
  50. if (event.key === 'Enter' && !disabled)
  51. previous()
  52. }}
  53. >
  54. {as.props.children ?? children}
  55. </as.type>
  56. )
  57. }
  58. export const NextButton = ({
  59. className,
  60. children,
  61. dataTestId,
  62. as = <button />,
  63. ...buttonProps
  64. }: ButtonProps) => {
  65. const pagination = React.useContext(PaginationContext)
  66. const next = () => {
  67. if (pagination.currentPage + 1 < pagination.pages.length)
  68. pagination.setCurrentPage(pagination.currentPage + 1)
  69. }
  70. const disabled = pagination.currentPage === pagination.pages.length - 1
  71. return (
  72. <as.type
  73. {...buttonProps}
  74. {...as.props}
  75. className={cn(className, as.props.className)}
  76. onClick={() => next()}
  77. tabIndex={disabled ? '-1' : 0}
  78. disabled={disabled}
  79. data-testid={dataTestId}
  80. onKeyPress={(event: React.KeyboardEvent) => {
  81. event.preventDefault()
  82. if (event.key === 'Enter' && !disabled)
  83. next()
  84. }}
  85. >
  86. {as.props.children ?? children}
  87. </as.type>
  88. )
  89. }
  90. type ITruncableElementProps = {
  91. prev?: boolean
  92. }
  93. const TruncableElement = ({ prev }: ITruncableElementProps) => {
  94. const pagination: IPagination = React.useContext(PaginationContext)
  95. const {
  96. isPreviousTruncable,
  97. isNextTruncable,
  98. truncableText,
  99. truncableClassName,
  100. } = pagination
  101. return ((isPreviousTruncable && prev === true) || (isNextTruncable && !prev))
  102. ? (
  103. <li className={truncableClassName || undefined}>{truncableText}</li>
  104. )
  105. : null
  106. }
  107. export const PageButton = ({
  108. as = <a />,
  109. className,
  110. dataTestIdActive,
  111. dataTestIdInactive,
  112. activeClassName,
  113. inactiveClassName,
  114. renderExtraProps,
  115. }: PageButtonProps) => {
  116. const pagination: IPagination = React.useContext(PaginationContext)
  117. const renderPageButton = (page: number) => (
  118. <li key={page}>
  119. <as.type
  120. data-testid={
  121. cn({
  122. [`${dataTestIdActive}`]:
  123. dataTestIdActive && pagination.currentPage + 1 === page,
  124. [`${dataTestIdInactive}-${page}`]:
  125. dataTestIdActive && pagination.currentPage + 1 !== page,
  126. }) || undefined
  127. }
  128. tabIndex={0}
  129. onKeyPress={(event: React.KeyboardEvent) => {
  130. if (event.key === 'Enter')
  131. pagination.setCurrentPage(page - 1)
  132. }}
  133. onClick={() => pagination.setCurrentPage(page - 1)}
  134. className={cn(
  135. className,
  136. pagination.currentPage + 1 === page
  137. ? activeClassName
  138. : inactiveClassName,
  139. )}
  140. {...as.props}
  141. {...(renderExtraProps ? renderExtraProps(page) : {})}
  142. >
  143. {page}
  144. </as.type>
  145. </li>
  146. )
  147. return (
  148. <>
  149. {pagination.previousPages.map(renderPageButton)}
  150. <TruncableElement prev />
  151. {pagination.middlePages.map(renderPageButton)}
  152. <TruncableElement />
  153. {pagination.nextPages.map(renderPageButton)}
  154. </>
  155. )
  156. }
  157. export const Pagination = ({
  158. dataTestId,
  159. ...paginationProps
  160. }: IPaginationProps & { dataTestId?: string }) => {
  161. const pagination = usePagination(paginationProps)
  162. return (
  163. <PaginationContext.Provider value={pagination}>
  164. <div className={paginationProps.className} data-testid={dataTestId}>
  165. {paginationProps.children}
  166. </div>
  167. </PaginationContext.Provider>
  168. )
  169. }
  170. Pagination.PrevButton = PrevButton
  171. Pagination.NextButton = NextButton
  172. Pagination.PageButton = PageButton