PaginationWrapper.tsx 5.1 KB

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