diff --git a/ui/v2.5/src/components/List/Pagination.tsx b/ui/v2.5/src/components/List/Pagination.tsx index 47d59e6cbe0..acca41f28a2 100644 --- a/ui/v2.5/src/components/List/Pagination.tsx +++ b/ui/v2.5/src/components/List/Pagination.tsx @@ -1,6 +1,132 @@ -import React from "react"; -import { Button, ButtonGroup } from "react-bootstrap"; -import { FormattedMessage, FormattedNumber, useIntl } from "react-intl"; +import React, { useEffect, useMemo, useRef, useState } from "react"; +import { + Button, + ButtonGroup, + Dropdown, + Form, + InputGroup, + Overlay, + Popover, +} from "react-bootstrap"; +import { FormattedMessage, useIntl } from "react-intl"; +import useFocus from "src/utils/focus"; +import { Icon } from "../Shared/Icon"; +import { faCheck, faChevronDown } from "@fortawesome/free-solid-svg-icons"; + +const PageCount: React.FC<{ + totalPages: number; + currentPage: number; + onChangePage: (page: number) => void; +}> = ({ totalPages, currentPage, onChangePage }) => { + const intl = useIntl(); + + const currentPageCtrl = useRef(null); + + const [pageInput, pageFocus] = useFocus(); + + const [showSelectPage, setShowSelectPage] = useState(false); + + useEffect(() => { + if (showSelectPage) { + pageFocus(); + } + }, [showSelectPage, pageFocus]); + + const pageOptions = useMemo(() => { + const maxPagesToShow = 10; + const min = Math.max(1, currentPage - maxPagesToShow / 2); + const max = Math.min(min + maxPagesToShow, totalPages); + const pages = []; + for (let i = min; i <= max; i++) { + pages.push(i); + } + return pages; + }, [totalPages, currentPage]); + + function onCustomChangePage() { + const newPage = Number.parseInt(pageInput.current?.value ?? "0"); + if (newPage) { + onChangePage(newPage); + } + setShowSelectPage(false); + } + + return ( +
+ + + + + + + + {pageOptions.map((s) => ( + onChangePage(s)} + > + {s} + + ))} + + + + setShowSelectPage(false)} + > + +
+ + ) => { + if (e.key === "Enter") { + onCustomChangePage(); + e.preventDefault(); + } + }} + onFocus={(e: React.FocusEvent) => + e.target.select() + } + /> + + + + +
+
+
+
+ ); +}; interface IPaginationProps { itemsPerPage: number; @@ -23,91 +149,55 @@ export const Pagination: React.FC = ({ totalItems, onChangePage, }) => { - const totalPages = Math.ceil(totalItems / itemsPerPage); - - let startPage: number; - let endPage: number; - if (totalPages <= 10) { - // less than 10 total pages so show all - startPage = 1; - endPage = totalPages; - } else if (currentPage <= 6) { - startPage = 1; - endPage = 10; - } else if (currentPage + 4 >= totalPages) { - startPage = totalPages - 9; - endPage = totalPages; - } else { - startPage = currentPage - 5; - endPage = currentPage + 4; - } + const intl = useIntl(); - const pages = [...Array(endPage + 1 - startPage).keys()].map( - (i) => startPage + i + const totalPages = useMemo( + () => Math.ceil(totalItems / itemsPerPage), + [totalItems, itemsPerPage] ); - const calculatePageClass = (buttonPage: number) => { - if (pages.length <= 4) return ""; - - if (currentPage === 1 && buttonPage <= 4) return ""; - const maxPage = pages[pages.length - 1]; - if (currentPage === maxPage && buttonPage > maxPage - 3) return ""; - if (Math.abs(buttonPage - currentPage) <= 1) return ""; - return "d-none d-sm-block"; - }; - - const pageButtons = pages.map((page: number) => ( - - )); - - if (pages.length <= 1) return
; + if (totalPages <= 1) return
; return ( - + - {pageButtons} + + + ); diff --git a/ui/v2.5/src/components/List/styles.scss b/ui/v2.5/src/components/List/styles.scss index f67b5b8b33d..1c8d993a309 100644 --- a/ui/v2.5/src/components/List/styles.scss +++ b/ui/v2.5/src/components/List/styles.scss @@ -7,14 +7,27 @@ padding-right: 15px; transition: none; + &.page-count { + padding-right: 5px; + } + + &.page-count-dropdown { + padding-left: 5px; + } + &:first-child { border-left: none; + border-right: none; } &:last-child { border-right: none; } } + + .page-count-container .btn { + border-radius: 0; + } } .center-text { diff --git a/ui/v2.5/src/index.scss b/ui/v2.5/src/index.scss index 73cc4da01e9..2e9587f9632 100755 --- a/ui/v2.5/src/index.scss +++ b/ui/v2.5/src/index.scss @@ -713,7 +713,8 @@ div.dropdown-menu { } .filter-container, -.operation-container { +.operation-container, +.pagination { align-items: center; display: flex; justify-content: center; @@ -1330,6 +1331,11 @@ $detailTabWidth: calc(100% / 3); border-top-right-radius: 0; } +.btn-group > .dropdown:not(:first-child) > .btn { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} + dl.details-list { display: grid; grid-column-gap: 10px; diff --git a/ui/v2.5/src/locales/en-GB.json b/ui/v2.5/src/locales/en-GB.json index 55d834b3c0e..76d573fefd0 100644 --- a/ui/v2.5/src/locales/en-GB.json +++ b/ui/v2.5/src/locales/en-GB.json @@ -1160,6 +1160,7 @@ "version": "Version" }, "pagination": { + "current_total": "{current} of {total}", "first": "First", "last": "Last", "next": "Next",