PaginationWrapper.tsx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import type { FC } from 'react';
  2. import React, { type JSX, memo, useCallback, useMemo } from 'react';
  3. import { Pagination, PaginationItem, PaginationLink } from 'reactstrap';
  4. type Props = {
  5. activePage: number;
  6. changePage?: (activePage: number) => void;
  7. totalItemsCount: number;
  8. pagingLimit?: number;
  9. align?: string;
  10. size?: string;
  11. };
  12. const PaginationWrapper: FC<Props> = memo((props: Props) => {
  13. const { activePage, changePage, totalItemsCount, pagingLimit, align } = props;
  14. /**
  15. * various numbers used to generate pagination dom
  16. */
  17. const paginationNumbers = useMemo(() => {
  18. // avoid using null
  19. const limit = pagingLimit || Infinity;
  20. // calc totalPageNumber
  21. const totalPage =
  22. Math.floor(totalItemsCount / limit) +
  23. (totalItemsCount % limit === 0 ? 0 : 1);
  24. let paginationStart = activePage - 2;
  25. let maxViewPageNum = activePage + 2;
  26. // if pagiNation Number area size = 5 , pageNumber is calculated here
  27. // activePage Position calculate ex. 4 5 [6] 7 8 (Page8 over is Max), 3 4 5 [6] 7 (Page7 is Max)
  28. if (paginationStart < 1) {
  29. const diff = 1 - paginationStart;
  30. paginationStart += diff;
  31. maxViewPageNum = Math.min(totalPage, maxViewPageNum + diff);
  32. }
  33. if (maxViewPageNum > totalPage) {
  34. const diff = maxViewPageNum - totalPage;
  35. maxViewPageNum -= diff;
  36. paginationStart = Math.max(1, paginationStart - diff);
  37. }
  38. return {
  39. totalPage,
  40. paginationStart,
  41. maxViewPageNum,
  42. };
  43. }, [activePage, totalItemsCount, pagingLimit]);
  44. const { paginationStart } = paginationNumbers;
  45. const { maxViewPageNum } = paginationNumbers;
  46. const { totalPage } = paginationNumbers;
  47. /**
  48. * generate Elements of Pagination First Prev
  49. * ex. << < 1 2 3 > >>
  50. * this function set << & <
  51. */
  52. const generateFirstPrev = useCallback(() => {
  53. const paginationItems: JSX.Element[] = [];
  54. if (activePage !== 1) {
  55. paginationItems.push(
  56. <PaginationItem key="painationItemFirst">
  57. <PaginationLink
  58. first
  59. onClick={() => {
  60. return changePage != null && changePage(1);
  61. }}
  62. />
  63. </PaginationItem>,
  64. <PaginationItem key="painationItemPrevious">
  65. <PaginationLink
  66. previous
  67. onClick={() => {
  68. return changePage != null && changePage(activePage - 1);
  69. }}
  70. />
  71. </PaginationItem>,
  72. );
  73. } else {
  74. paginationItems.push(
  75. <PaginationItem key="painationItemFirst" disabled>
  76. <PaginationLink first />
  77. </PaginationItem>,
  78. <PaginationItem key="painationItemPrevious" disabled>
  79. <PaginationLink previous />
  80. </PaginationItem>,
  81. );
  82. }
  83. return paginationItems;
  84. }, [activePage, changePage]);
  85. /**
  86. * generate Elements of Pagination First Prev
  87. * ex. << < 4 5 6 7 8 > >>, << < 1 2 3 4 > >>
  88. * this function set numbers
  89. */
  90. const generatePaginations = useCallback(() => {
  91. const paginationItems: JSX.Element[] = [];
  92. for (let number = paginationStart; number <= maxViewPageNum; number++) {
  93. paginationItems.push(
  94. <PaginationItem
  95. key={`paginationItem-${number}`}
  96. active={number === activePage}
  97. >
  98. <PaginationLink
  99. onClick={() => {
  100. return changePage != null && changePage(number);
  101. }}
  102. >
  103. {number}
  104. </PaginationLink>
  105. </PaginationItem>,
  106. );
  107. }
  108. return paginationItems;
  109. }, [activePage, changePage, paginationStart, maxViewPageNum]);
  110. /**
  111. * generate Elements of Pagination First Prev
  112. * ex. << < 1 2 3 > >>
  113. * this function set > & >>
  114. */
  115. const generateNextLast = useCallback(() => {
  116. const paginationItems: JSX.Element[] = [];
  117. if (totalPage !== activePage) {
  118. paginationItems.push(
  119. <PaginationItem key="painationItemNext">
  120. <PaginationLink
  121. next
  122. onClick={() => {
  123. return changePage != null && changePage(activePage + 1);
  124. }}
  125. />
  126. </PaginationItem>,
  127. <PaginationItem key="painationItemLast">
  128. <PaginationLink
  129. last
  130. onClick={() => {
  131. return changePage != null && changePage(totalPage);
  132. }}
  133. />
  134. </PaginationItem>,
  135. );
  136. } else {
  137. paginationItems.push(
  138. <PaginationItem key="painationItemNext" disabled>
  139. <PaginationLink next />
  140. </PaginationItem>,
  141. <PaginationItem key="painationItemLast" disabled>
  142. <PaginationLink last />
  143. </PaginationItem>,
  144. );
  145. }
  146. return paginationItems;
  147. }, [activePage, changePage, totalPage]);
  148. const getListClassName = useMemo(() => {
  149. const listClassNames: string[] = [];
  150. if (align === 'center') {
  151. listClassNames.push('justify-content-center');
  152. }
  153. if (align === 'right') {
  154. listClassNames.push('justify-content-end');
  155. }
  156. return listClassNames.join(' ');
  157. }, [align]);
  158. return (
  159. <React.Fragment>
  160. <Pagination size={props.size} listClassName={getListClassName}>
  161. {generateFirstPrev()}
  162. {generatePaginations()}
  163. {generateNextLast()}
  164. </Pagination>
  165. </React.Fragment>
  166. );
  167. });
  168. PaginationWrapper.displayName = 'PaginationWrapper';
  169. PaginationWrapper.defaultProps = {
  170. align: 'left',
  171. size: 'md',
  172. pagingLimit: Infinity,
  173. };
  174. export default PaginationWrapper;