0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-20 21:32:31 -05:00

Merge pull request #468 from logto-io/sijie--log-1975-table-scroll

feat(console): table scroll
This commit is contained in:
Wang Sijie 2022-04-01 13:40:09 +08:00 committed by GitHub
commit c9a8855c0d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 104 additions and 72 deletions

View file

@ -1,5 +1,11 @@
@use '@/scss/underscore' as _; @use '@/scss/underscore' as _;
.card {
display: flex;
flex-direction: column;
height: 100%;
}
.headline { .headline {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@ -10,21 +16,21 @@
} }
.table { .table {
margin-top: _.unit(4); flex: 1;
tbody { tr.clickable {
max-height: calc(100vh - _.unit(64)); cursor: pointer;
tr.clickable { &:hover {
cursor: pointer; background: var(--color-table-row-selected);
&:hover {
background: var(--color-table-row-selected);
}
} }
} }
} }
.pagination {
min-height: 64px;
}
.userName { .userName {
width: 360px; width: 360px;
} }

View file

@ -1,5 +1,6 @@
import { User } from '@logto/schemas'; import { User } from '@logto/schemas';
import { conditionalString } from '@silverhand/essentials'; import { conditionalString } from '@silverhand/essentials';
import classNames from 'classnames';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import Modal from 'react-modal'; import Modal from 'react-modal';
@ -18,6 +19,7 @@ import TableError from '@/components/Table/TableError';
import TableLoading from '@/components/Table/TableLoading'; import TableLoading from '@/components/Table/TableLoading';
import { RequestError } from '@/hooks/use-api'; import { RequestError } from '@/hooks/use-api';
import * as modalStyles from '@/scss/modal.module.scss'; import * as modalStyles from '@/scss/modal.module.scss';
import * as tableStyles from '@/scss/table.module.scss';
import CreateForm from './components/CreateForm'; import CreateForm from './components/CreateForm';
import * as styles from './index.module.scss'; import * as styles from './index.module.scss';
@ -39,7 +41,7 @@ const Users = () => {
const [users, totalCount] = data ?? []; const [users, totalCount] = data ?? [];
return ( return (
<Card> <Card className={styles.card}>
<div className={styles.headline}> <div className={styles.headline}>
<CardTitle title="users.title" subtitle="users.subtitle" /> <CardTitle title="users.title" subtitle="users.subtitle" />
<Button <Button
@ -68,68 +70,72 @@ const Users = () => {
<div className={styles.filter}> <div className={styles.filter}>
<Search defaultValue={keyword} onSearch={setKeyword} /> <Search defaultValue={keyword} onSearch={setKeyword} />
</div> </div>
<table className={styles.table}> <div className={classNames(styles.table, tableStyles.scrollable)}>
<colgroup> <table>
<col className={styles.userName} /> <colgroup>
<col /> <col className={styles.userName} />
<col /> <col />
</colgroup> <col />
<thead> </colgroup>
<tr> <thead>
<th>{t('users.user_name')}</th> <tr>
<th>{t('users.application_name')}</th> <th>{t('users.user_name')}</th>
<th>{t('users.latest_sign_in')}</th> <th>{t('users.application_name')}</th>
</tr> <th>{t('users.latest_sign_in')}</th>
</thead>
<tbody>
{error && (
<TableError
columns={3}
content={error.body.message}
onRetry={async () => mutate(undefined, true)}
/>
)}
{isLoading && <TableLoading columns={3} />}
{users?.length === 0 && (
<TableEmpty columns={3}>
<Button
title="admin_console.users.create"
type="outline"
onClick={() => {
setIsCreateFormOpen(true);
}}
/>
</TableEmpty>
)}
{users?.map(({ id, name, username }) => (
<tr
key={id}
className={styles.clickable}
onClick={() => {
navigate(`/users/${id}`);
}}
>
<td>
<ItemPreview
title={name ?? '-'}
subtitle={username ?? '-'}
icon={<ImagePlaceholder />}
to={`/users/${id}`}
/>
</td>
<td>Application</td>
<td>Last sign in</td>
</tr> </tr>
))} </thead>
</tbody> <tbody>
</table> {error && (
{totalCount !== undefined && totalCount > 0 && ( <TableError
<Pagination columns={3}
pageCount={Math.ceil(totalCount / pageSize)} content={error.body.message}
pageIndex={pageIndex} onRetry={async () => mutate(undefined, true)}
onChange={setPageIndex} />
/> )}
)} {isLoading && <TableLoading columns={3} />}
{users?.length === 0 && (
<TableEmpty columns={3}>
<Button
title="admin_console.users.create"
type="outline"
onClick={() => {
setIsCreateFormOpen(true);
}}
/>
</TableEmpty>
)}
{users?.map(({ id, name, username }) => (
<tr
key={id}
className={styles.clickable}
onClick={() => {
navigate(`/users/${id}`);
}}
>
<td>
<ItemPreview
title={name ?? '-'}
subtitle={username ?? '-'}
icon={<ImagePlaceholder />}
to={`/users/${id}`}
/>
</td>
<td>Application</td>
<td>Last sign in</td>
</tr>
))}
</tbody>
</table>
</div>
<div className={styles.pagination}>
{totalCount !== undefined && totalCount > 0 && (
<Pagination
pageCount={Math.ceil(totalCount / pageSize)}
pageIndex={pageIndex}
onChange={setPageIndex}
/>
)}
</div>
</Card> </Card>
); );
}; };

View file

@ -25,7 +25,6 @@ table {
border-spacing: 0; border-spacing: 0;
width: 100%; width: 100%;
table-layout: fixed; table-layout: fixed;
overflow: hidden;
thead { thead {
th { th {

View file

@ -0,0 +1,21 @@
@use '@/scss/underscore' as _;
.scrollable {
margin-top: _.unit(4);
overflow-y: auto;
border: 1px solid var(--color-neutral-90);
border-radius: _.unit(2);
table {
border: none;
thead tr {
position: sticky;
top: 0;
th {
background: var(--color-on-primary);
}
}
}
}