PaginationWrapper.tsx 5.0 KB

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