feat(ui): tweak pagination buttons

- Fix off-by-one error when going to last page
- Update component to have minimal/no layout shift
This commit is contained in:
psychedelicious 2024-06-21 18:20:45 +10:00
parent 1f22f6ae02
commit 689dc30f87
2 changed files with 149 additions and 138 deletions

View File

@ -1,10 +1,23 @@
import { Button, Flex, IconButton, Text } from '@invoke-ai/ui-library';
import { useGalleryPagination } from '../../hooks/useGalleryPagination';
import { PiCaretLeftBold, PiCaretRightBold } from 'react-icons/pi';
import { Button, Flex, IconButton, Spacer, Text } from '@invoke-ai/ui-library';
import { useGalleryPagination } from 'features/gallery/hooks/useGalleryPagination';
import { PiCaretDoubleLeftBold, PiCaretDoubleRightBold, PiCaretLeftBold, PiCaretRightBold } from 'react-icons/pi';
export const GalleryPagination = () => {
const { goPrev, goNext, isPrevEnabled, isNextEnabled, pageButtons, goToPage, currentPage, rangeDisplay, total } =
useGalleryPagination();
const {
goPrev,
goNext,
goToFirst,
goToLast,
isFirstEnabled,
isLastEnabled,
isPrevEnabled,
isNextEnabled,
pageButtons,
goToPage,
currentPage,
rangeDisplay,
total,
} = useGalleryPagination();
if (!total) {
return <Flex flexDir="column" alignItems="center" gap="2" height="48px"></Flex>;
@ -12,7 +25,14 @@ export const GalleryPagination = () => {
return (
<Flex flexDir="column" alignItems="center" gap="2" height="48px">
<Flex gap={2} alignItems="flex-end">
<Flex gap={2} alignItems="center" w="full">
<IconButton
size="sm"
aria-label="prev"
icon={<PiCaretDoubleLeftBold />}
onClick={goToFirst}
isDisabled={!isFirstEnabled}
/>
<IconButton
size="sm"
aria-label="prev"
@ -20,8 +40,8 @@ export const GalleryPagination = () => {
onClick={goPrev}
isDisabled={!isPrevEnabled}
/>
{pageButtons.map((page) =>
typeof page === 'number' ? (
<Spacer />
{pageButtons.map((page) => (
<Button
size="sm"
key={page}
@ -30,10 +50,8 @@ export const GalleryPagination = () => {
>
{page + 1}
</Button>
) : (
<Text fontSize="md">...</Text>
)
)}
))}
<Spacer />
<IconButton
size="sm"
aria-label="next"
@ -41,6 +59,13 @@ export const GalleryPagination = () => {
onClick={goNext}
isDisabled={!isNextEnabled}
/>
<IconButton
size="sm"
aria-label="next"
icon={<PiCaretDoubleRightBold />}
onClick={goToLast}
isDisabled={!isLastEnabled}
/>
</Flex>
<Text>{rangeDisplay} Images</Text>
</Flex>

View File

@ -1,11 +1,10 @@
import { useMemo, useCallback } from "react";
import { useAppDispatch, useAppSelector } from "../../../app/store/storeHooks";
import { useGetBoardAssetsTotalQuery, useGetBoardImagesTotalQuery } from "../../../services/api/endpoints/boards";
import { useListImagesQuery } from "../../../services/api/endpoints/images";
import { selectListImagesQueryArgs } from "../store/gallerySelectors";
import { offsetChanged } from "../store/gallerySlice";
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { selectListImagesQueryArgs } from 'features/gallery/store/gallerySelectors';
import { offsetChanged } from 'features/gallery/store/gallerySlice';
import { useCallback, useMemo } from 'react';
import { useListImagesQuery } from 'services/api/endpoints/images';
export const useGalleryPagination = () => {
export const useGalleryPagination = (pageButtonsPerSide: number = 2) => {
const dispatch = useAppDispatch();
const { offset, limit } = useAppSelector((s) => s.gallery);
const queryArgs = useAppSelector(selectListImagesQueryArgs);
@ -49,40 +48,26 @@ export const useGalleryPagination = () => {
dispatch(offsetChanged(0));
}, [dispatch]);
const goToLast = useCallback(() => {
dispatch(offsetChanged(pages * (limit || 0)));
dispatch(offsetChanged((pages - 1) * (limit || 0)));
}, [dispatch, pages, limit]);
// calculate the page buttons to display - current page with 3 around it
const pageButtons = useMemo(() => {
const buttons = [];
const maxPageButtons = 3;
let startPage = Math.max(currentPage - (Math.floor(maxPageButtons / 2)), 0);
let endPage = Math.min(startPage + maxPageButtons - 1, pages - 1);
const maxPageButtons = pageButtonsPerSide * 2 + 1;
let startPage = Math.max(currentPage - Math.floor(maxPageButtons / 2), 0);
const endPage = Math.min(startPage + maxPageButtons - 1, pages - 1);
if (endPage - startPage < maxPageButtons - 1) {
startPage = Math.max(endPage - maxPageButtons + 1, 0);
}
if (startPage > 0) {
buttons.push(0);
if (startPage > 1) {
buttons.push('...');
}
}
for (let i = startPage; i <= endPage; i++) {
buttons.push(i);
}
if (endPage < pages - 1) {
if (endPage < pages - 2) {
buttons.push('...');
}
buttons.push(pages - 1);
}
return buttons;
}, [currentPage, pages]);
}, [currentPage, pageButtonsPerSide, pages]);
const isFirstEnabled = useMemo(() => currentPage > 0, [currentPage]);
const isLastEnabled = useMemo(() => currentPage < pages - 1, [currentPage, pages]);
@ -109,7 +94,7 @@ export const useGalleryPagination = () => {
pageButtons,
isFirstEnabled,
isLastEnabled,
rangeDisplay
rangeDisplay,
}),
[
count,
@ -126,8 +111,9 @@ export const useGalleryPagination = () => {
pageButtons,
isFirstEnabled,
isLastEnabled,
rangeDisplay
rangeDisplay,
]
);
return api;
};