0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

feat(console): pagination (#453)

* feat(console): pagination

* fix: large number

* fix: new icon
This commit is contained in:
Wang Sijie 2022-03-28 15:01:50 +08:00 committed by GitHub
parent a08abfd4c6
commit fd8ea43f0b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 137 additions and 5 deletions

View file

@ -36,6 +36,7 @@
"react-i18next": "^11.15.4",
"react-markdown": "^8.0.0",
"react-modal": "^3.14.4",
"react-paginate": "^8.1.2",
"react-router-dom": "^6.2.2",
"remark-gfm": "^3.0.1",
"swr": "^1.2.2"

View file

@ -1,9 +1,10 @@
import { I18nKey } from '@logto/phrases';
import { conditionalString } from '@silverhand/essentials';
import classNames from 'classnames';
import React, { HTMLProps, ReactNode } from 'react';
import React, { HTMLProps, ReactElement, ReactNode } from 'react';
import { useTranslation } from 'react-i18next';
import DangerousRaw from '../DangerousRaw';
import * as styles from './index.module.scss';
type BaseProps = Omit<HTMLProps<HTMLButtonElement>, 'type' | 'size' | 'title'> & {
@ -13,12 +14,12 @@ type BaseProps = Omit<HTMLProps<HTMLButtonElement>, 'type' | 'size' | 'title'> &
};
type TitleButtonProps = BaseProps & {
title: I18nKey;
title: I18nKey | ReactElement<typeof DangerousRaw>;
icon?: ReactNode;
};
type IconButtonProps = BaseProps & {
title?: I18nKey;
title?: I18nKey | ReactElement<typeof DangerousRaw>;
icon: ReactNode;
};
@ -30,6 +31,7 @@ const Button = ({
size = 'medium',
title,
icon,
className,
...rest
}: Props) => {
const { t } = useTranslation();
@ -40,13 +42,14 @@ const Button = ({
styles.button,
styles[type],
styles[size],
conditionalString(icon && styles.withIcon)
conditionalString(icon && styles.withIcon),
className
)}
type={htmlType}
{...rest}
>
{icon && <span className={styles.icon}>{icon}</span>}
{title && <span>{t(title)}</span>}
{title && (typeof title === 'string' ? <span>{t(title)}</span> : title)}
</button>
);
};

View file

@ -0,0 +1,9 @@
import React, { ReactNode } from 'react';
type Props = {
children: ReactNode;
};
const DangerousRaw = ({ children }: Props) => <span>{children}</span>;
export default DangerousRaw;

View file

@ -0,0 +1,14 @@
import React from 'react';
const Next = () => {
return (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M12.9509 9.40832L8.23425 4.69999C8.15679 4.62188 8.06462 4.55989 7.96307 4.51758C7.86152 4.47527 7.7526 4.45349 7.64259 4.45349C7.53258 4.45349 7.42366 4.47527 7.32211 4.51758C7.22056 4.55989 7.12839 4.62188 7.05092 4.69999C6.89571 4.85613 6.80859 5.06734 6.80859 5.28749C6.80859 5.50764 6.89571 5.71885 7.05092 5.87499L11.1759 10.0417L7.05092 14.1667C6.89571 14.3228 6.80859 14.534 6.80859 14.7542C6.80859 14.9743 6.89571 15.1855 7.05092 15.3417C7.1281 15.4204 7.22014 15.483 7.3217 15.526C7.42326 15.5689 7.53233 15.5912 7.64259 15.5917C7.75284 15.5912 7.86191 15.5689 7.96348 15.526C8.06504 15.483 8.15708 15.4204 8.23425 15.3417L12.9509 10.6333C13.0355 10.5553 13.103 10.4606 13.1492 10.3552C13.1954 10.2497 13.2192 10.1359 13.2192 10.0208C13.2192 9.90574 13.1954 9.7919 13.1492 9.68648C13.103 9.58107 13.0355 9.48636 12.9509 9.40832Z"
fill="#747778"
/>
</svg>
);
};
export default Next;

View file

@ -0,0 +1,14 @@
import React from 'react';
const Previous = () => {
return (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M7.04908 9.40832L11.7657 4.69999C11.8432 4.62188 11.9354 4.55989 12.0369 4.51758C12.1385 4.47527 12.2474 4.45349 12.3574 4.45349C12.4674 4.45349 12.5763 4.47527 12.6779 4.51758C12.7794 4.55989 12.8716 4.62188 12.9491 4.69999C13.1043 4.85613 13.1914 5.06734 13.1914 5.28749C13.1914 5.50764 13.1043 5.71885 12.9491 5.87499L8.82408 10.0417L12.9491 14.1667C13.1043 14.3228 13.1914 14.534 13.1914 14.7542C13.1914 14.9743 13.1043 15.1855 12.9491 15.3417C12.8719 15.4204 12.7799 15.483 12.6783 15.526C12.5767 15.5689 12.4677 15.5912 12.3574 15.5917C12.2472 15.5912 12.1381 15.5689 12.0365 15.526C11.935 15.483 11.8429 15.4204 11.7657 15.3417L7.04908 10.6333C6.96449 10.5553 6.89698 10.4606 6.85081 10.3552C6.80464 10.2497 6.7808 10.1359 6.7808 10.0208C6.7808 9.90574 6.80464 9.7919 6.85081 9.68648C6.89698 9.58107 6.96449 9.48636 7.04908 9.40832Z"
fill="#747778"
/>
</svg>
);
};
export default Previous;

View file

@ -0,0 +1,37 @@
@use '@/scss/underscore' as _;
.pagination {
display: flex;
justify-content: right;
li {
list-style: none;
&:not(:first-child) {
margin-left: _.unit(2);
}
.button {
border-radius: 6px;
min-width: 32px;
padding: 0 6px;
height: 32px;
text-overflow: unset;
> span {
margin: 0 auto;
}
}
&.disabled {
.button {
cursor: not-allowed;
background: var(--color-neutral-95);
&:hover {
background: var(--color-neutral-95);
}
}
}
}
}

View file

@ -0,0 +1,43 @@
import React from 'react';
import ReactPaginate from 'react-paginate';
import Button from '../Button';
import DangerousRaw from '../DangerousRaw';
import Next from './Next';
import Previous from './Previous';
import * as styles from './index.module.scss';
type Props = {
pageIndex: number;
pageCount: number;
onChange?: (pageIndex: number) => void;
};
const Pagination = ({ pageIndex, pageCount, onChange }: Props) => {
return (
<ReactPaginate
className={styles.pagination}
pageCount={pageCount}
forcePage={pageIndex - 1}
pageLabelBuilder={(page: number) => (
<Button
type={page === pageIndex ? 'outline' : 'default'}
className={styles.button}
size="small"
title={<DangerousRaw>{page}</DangerousRaw>}
/>
)}
previousLabel={<Button className={styles.button} size="small" icon={<Previous />} />}
nextLabel={<Button className={styles.button} size="small" icon={<Next />} />}
breakLabel={
<Button className={styles.button} size="small" title={<DangerousRaw>...</DangerousRaw>} />
}
disabledClassName={styles.disabled}
onPageChange={({ selected }) => {
onChange?.(selected + 1);
}}
/>
);
};
export default Pagination;

View file

@ -57,6 +57,7 @@ importers:
react-i18next: ^11.15.4
react-markdown: ^8.0.0
react-modal: ^3.14.4
react-paginate: ^8.1.2
react-router-dom: ^6.2.2
remark-gfm: ^3.0.1
stylelint: ^13.13.1
@ -82,6 +83,7 @@ importers:
react-i18next: 11.15.4_2c37a602a29bb6bd53f3de707a8cfcc5
react-markdown: 8.0.0_cfedea9b3ed0faf0dded75c187406c5e
react-modal: 3.14.4_react-dom@17.0.2+react@17.0.2
react-paginate: 8.1.2_react@17.0.2
react-router-dom: 6.2.2_react-dom@17.0.2+react@17.0.2
remark-gfm: 3.0.1
swr: 1.2.2_react@17.0.2
@ -11982,6 +11984,15 @@ packages:
warning: 4.0.3
dev: false
/react-paginate/8.1.2_react@17.0.2:
resolution: {integrity: sha512-buBkBiN9J8gvZYwYNixlTGRmWOC5C6+tH2XHTN8B5qGkRPOSYFkAqxhWUnxSVAeLKxpVZGPya/gOL2eJQNZGvg==}
peerDependencies:
react: ^16 || ^17
dependencies:
prop-types: 15.8.1
react: 17.0.2
dev: false
/react-phone-number-input/3.1.46_react-dom@17.0.2+react@17.0.2:
resolution: {integrity: sha512-afYl7BMy/0vMqWtzsZBmOgiPdqQAGyPO/Z3auorFs4K/zgFSBq3YoaASleodBkeRO/PygJ4ML8Wnb4Ce+3dlVQ==}
peerDependencies: