mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
* LibraryPanels: Improves Get All Api * Refactor: using useReducer instead of useState * Refactor: adds Pagination to UI * Tests: adds reducer tests * Refactor: using Observable instead to avoid flickering * Refactor: moves exclusion to backend instead * Chore: changing back the perPage default value
126 lines
4.1 KiB
TypeScript
126 lines
4.1 KiB
TypeScript
import React from 'react';
|
|
import { css } from 'emotion';
|
|
import { stylesFactory } from '../../themes';
|
|
import { Button, ButtonVariant } from '../Button';
|
|
import { Icon } from '../Icon/Icon';
|
|
|
|
const PAGE_LENGTH_TO_CONDENSE = 8;
|
|
|
|
interface Props {
|
|
/** The current page index being shown. */
|
|
currentPage: number;
|
|
/** Number of total pages. */
|
|
numberOfPages: number;
|
|
/** Callback function for fetching the selected page */
|
|
onNavigate: (toPage: number) => void;
|
|
/** When set to true and the pagination result is only one page it will not render the pagination at all */
|
|
hideWhenSinglePage?: boolean;
|
|
}
|
|
|
|
export const Pagination: React.FC<Props> = ({ currentPage, numberOfPages, onNavigate, hideWhenSinglePage }) => {
|
|
const styles = getStyles();
|
|
const pages = [...new Array(numberOfPages).keys()];
|
|
|
|
const condensePages = numberOfPages > PAGE_LENGTH_TO_CONDENSE;
|
|
const getListItem = (page: number, variant: 'primary' | 'secondary') => (
|
|
<li key={page} className={styles.item}>
|
|
<Button size="sm" variant={variant} onClick={() => onNavigate(page)}>
|
|
{page}
|
|
</Button>
|
|
</li>
|
|
);
|
|
|
|
const pageButtons = pages.reduce<JSX.Element[]>((pagesToRender, pageIndex) => {
|
|
const page = pageIndex + 1;
|
|
const variant: ButtonVariant = page === currentPage ? 'primary' : 'secondary';
|
|
|
|
// The indexes at which to start and stop condensing pages
|
|
const lowerBoundIndex = PAGE_LENGTH_TO_CONDENSE;
|
|
const upperBoundIndex = numberOfPages - PAGE_LENGTH_TO_CONDENSE + 1;
|
|
// When the indexes overlap one another this number is negative
|
|
const differenceOfBounds = upperBoundIndex - lowerBoundIndex;
|
|
|
|
const isFirstOrLastPage = page === 1 || page === numberOfPages;
|
|
// This handles when the lowerBoundIndex < currentPage < upperBoundIndex
|
|
const currentPageIsBetweenBounds =
|
|
differenceOfBounds > -1 && currentPage >= lowerBoundIndex && currentPage <= upperBoundIndex;
|
|
|
|
if (condensePages) {
|
|
if (
|
|
isFirstOrLastPage ||
|
|
(currentPage < lowerBoundIndex && page < lowerBoundIndex) ||
|
|
(differenceOfBounds >= 0 && currentPage > upperBoundIndex && page > upperBoundIndex) ||
|
|
(differenceOfBounds < 0 && currentPage >= lowerBoundIndex && page > upperBoundIndex) ||
|
|
(currentPageIsBetweenBounds && page >= currentPage - 2 && page <= currentPage + 2)
|
|
) {
|
|
// Renders a button for the page
|
|
pagesToRender.push(getListItem(page, variant));
|
|
} else if (
|
|
(page === lowerBoundIndex && currentPage < lowerBoundIndex) ||
|
|
(page === upperBoundIndex && currentPage > upperBoundIndex) ||
|
|
(currentPageIsBetweenBounds && (page === currentPage - 3 || page === currentPage + 3))
|
|
) {
|
|
// Renders and ellipsis to represent condensed pages
|
|
pagesToRender.push(
|
|
<li key={page} className={styles.item}>
|
|
<Icon className={styles.ellipsis} name="ellipsis-v" />
|
|
</li>
|
|
);
|
|
}
|
|
} else {
|
|
pagesToRender.push(getListItem(page, variant));
|
|
}
|
|
return pagesToRender;
|
|
}, []);
|
|
|
|
if (hideWhenSinglePage && numberOfPages <= 1) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<div className={styles.container}>
|
|
<ol>
|
|
<li className={styles.item}>
|
|
<Button
|
|
aria-label="previous"
|
|
size="sm"
|
|
variant="secondary"
|
|
onClick={() => onNavigate(currentPage - 1)}
|
|
disabled={currentPage === 1}
|
|
>
|
|
<Icon name="angle-left" />
|
|
</Button>
|
|
</li>
|
|
{pageButtons}
|
|
<li className={styles.item}>
|
|
<Button
|
|
aria-label="next"
|
|
size="sm"
|
|
variant="secondary"
|
|
onClick={() => onNavigate(currentPage + 1)}
|
|
disabled={currentPage === numberOfPages}
|
|
>
|
|
<Icon name="angle-right" />
|
|
</Button>
|
|
</li>
|
|
</ol>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const getStyles = stylesFactory(() => {
|
|
return {
|
|
container: css`
|
|
float: right;
|
|
`,
|
|
item: css`
|
|
display: inline-block;
|
|
padding-left: 10px;
|
|
margin-bottom: 5px;
|
|
`,
|
|
ellipsis: css`
|
|
transform: rotate(90deg);
|
|
`,
|
|
};
|
|
});
|