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

Merge pull request #825 from logto-io/charles-log-2423-add-page-skeletons-for-tables-and-details

feat(console): add page loading skeleton to data table and detail pages
This commit is contained in:
Charles Zhao 2022-05-16 13:20:18 +08:00 committed by GitHub
commit 4020096319
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 181 additions and 78 deletions

View file

@ -79,7 +79,7 @@ const Main = () => {
}
}, [location.pathname, navigate, sections]);
if (sections?.length === 0) {
if (!sections?.length) {
return <LogtoLoading message="general.loading" />;
}

View file

@ -0,0 +1,87 @@
@use '@/scss/underscore' as _;
.container {
height: 100%;
display: flex;
flex-direction: column;
overflow-y: auto;
.header {
display: flex;
align-items: center;
padding: _.unit(6);
border-radius: 16px;
background-color: var(--color-layer-1);
.icon {
@include _.shimmering-animation;
width: 60px;
height: 60px;
border-radius: 12px;
margin-right: _.unit(6);
}
.wrapper {
display: flex;
flex-direction: column;
.title {
@include _.shimmering-animation;
width: 113px;
height: 28px;
}
.tags {
@include _.shimmering-animation;
width: 453px;
height: 20px;
margin-top: _.unit(3);
}
}
.button {
@include _.shimmering-animation;
width: 158px;
height: 44px;
}
}
.content {
flex: 1;
display: flex;
flex-direction: column;
width: 100%;
padding: _.unit(6);
margin-top: _.unit(6);
border-radius: 16px;
background-color: var(--color-layer-1);
.tabBar {
@include _.shimmering-animation;
width: 100%;
height: 25px;
margin-bottom: _.unit(13.5);
}
.field {
@include _.shimmering-animation;
width: 566px;
height: 44px;
}
.field + .field {
margin-top: _.unit(6);
}
.footer {
display: flex;
justify-content: end;
.button {
@include _.shimmering-animation;
width: 194px;
height: 44px;
}
}
}
}

View file

@ -0,0 +1,32 @@
import React from 'react';
import Spacer from '@/components/Spacer';
import * as styles from './index.module.scss';
const DetailsSkeleton = () => (
<div className={styles.container}>
<div className={styles.header}>
<div className={styles.icon} />
<div className={styles.wrapper}>
<div className={styles.title} />
<div className={styles.tags} />
</div>
<Spacer />
<div className={styles.button} />
</div>
<div className={styles.content}>
<div className={styles.tabBar} />
{Array.from({ length: 4 }).map((_, index) => (
// eslint-disable-next-line react/no-array-index-key
<div key={index} className={styles.field} />
))}
<Spacer />
<div className={styles.footer}>
<div className={styles.button} />
</div>
</div>
</div>
);
export default DetailsSkeleton;

View file

@ -1,34 +0,0 @@
@use '@/scss/underscore' as _;
td > div.loading {
display: flex;
background: none;
height: 40px;
border-radius: unset;
.avatar {
width: 40px;
height: 40px;
border-radius: _.unit(2);
margin-right: _.unit(4);
background: var(--color-neutral-95);
}
.content {
flex: 1;
.title {
background: var(--color-neutral-95);
border-radius: _.unit(2);
height: 14px;
margin: _.unit(1) 0;
}
.subTitle {
background: var(--color-neutral-95);
border-radius: _.unit(2);
height: 12px;
margin-top: _.unit(2);
}
}
}

View file

@ -1,15 +0,0 @@
import React from 'react';
import * as styles from './ItemPreviewLoading.module.scss';
const ItemPreviewLoading = () => (
<div className={styles.loading}>
<div className={styles.avatar} />
<div className={styles.content}>
<div className={styles.title} />
<div className={styles.subTitle} />
</div>
</div>
);
export default ItemPreviewLoading;

View file

@ -1,9 +1,37 @@
@use '@/scss/underscore' as _;
.loading {
.itemPreview {
display: flex;
align-items: center;
.avatar {
@include _.shimmering-animation;
width: 40px;
height: 40px;
margin-right: _.unit(4);
border-radius: 12px;
}
.content {
width: 135px;
.title {
@include _.shimmering-animation;
height: 12px;
}
.subTitle {
@include _.shimmering-animation;
height: 8px;
margin-top: _.unit(2);
}
}
}
.rect {
border-radius: _.unit(2);
background: var(--color-neutral-95);
@include _.shimmering-animation;
height: 32px;
max-width: 344px;
}
}

View file

@ -1,6 +1,5 @@
import React, { useMemo } from 'react';
import React from 'react';
import ItemPreviewLoading from './ItemPreviewLoading';
import * as styles from './TableLoading.module.scss';
type Props = {
@ -8,27 +7,28 @@ type Props = {
};
const TableLoading = ({ columns }: Props) => {
const row = useMemo(
() => (
<tr className={styles.loading}>
<td>
<ItemPreviewLoading />
</td>
{Array.from({ length: columns - 1 }).map((_, index) => (
// eslint-disable-next-line react/no-array-index-key
<td key={index}>
<div className={styles.rect} />
</td>
))}
</tr>
),
[columns]
);
return (
<>
{row}
{row}
{Array.from({ length: 8 }).map((_, rowIndex) => (
// eslint-disable-next-line react/no-array-index-key
<tr key={`row-${rowIndex}`} className={styles.loading}>
<td>
<div className={styles.itemPreview}>
<div className={styles.avatar} />
<div className={styles.content}>
<div className={styles.title} />
<div className={styles.subTitle} />
</div>
</div>
</td>
{Array.from({ length: columns - 1 }).map((_, index) => (
// eslint-disable-next-line react/no-array-index-key
<td key={index}>
<div className={styles.rect} />
</td>
))}
</tr>
))}
</>
);
};

View file

@ -12,6 +12,7 @@ import ActionMenu, { ActionMenuItem } from '@/components/ActionMenu';
import Button from '@/components/Button';
import Card from '@/components/Card';
import CopyToClipboard from '@/components/CopyToClipboard';
import DetailsSkeleton from '@/components/DetailsSkeleton';
import Drawer from '@/components/Drawer';
import FormField from '@/components/FormField';
import ImagePlaceholder from '@/components/ImagePlaceholder';
@ -82,7 +83,7 @@ const ApiResourceDetails = () => {
title="admin_console.api_resource_details.back_to_api_resources"
className={styles.backLink}
/>
{isLoading && <div>loading</div>}
{isLoading && <DetailsSkeleton />}
{error && <div>{`error occurred: ${error.body.message}`}</div>}
{data && (
<>

View file

@ -12,6 +12,7 @@ import ActionMenu, { ActionMenuItem } from '@/components/ActionMenu';
import Button from '@/components/Button';
import Card from '@/components/Card';
import CopyToClipboard from '@/components/CopyToClipboard';
import DetailsSkeleton from '@/components/DetailsSkeleton';
import Drawer from '@/components/Drawer';
import ImagePlaceholder from '@/components/ImagePlaceholder';
import LinkButton from '@/components/LinkButton';
@ -43,7 +44,7 @@ const ApplicationDetails = () => {
SnakeCaseOidcConfig,
RequestError
>('/oidc/.well-known/openid-configuration');
const isLoading = !data && !error && !oidcConfig && !fetchOidcConfigError;
const isLoading = (!data && !error) || (!oidcConfig && !fetchOidcConfigError);
const [isReadmeOpen, setIsReadmeOpen] = useState(false);
const [isDeleteFormOpen, setIsDeleteFormOpen] = useState(false);
const api = useApi();
@ -102,7 +103,7 @@ const ApplicationDetails = () => {
title="admin_console.application_details.back_to_applications"
className={styles.backLink}
/>
{isLoading && <div>loading</div>}
{isLoading && <DetailsSkeleton />}
{data && oidcConfig && (
<>
<Card className={styles.header}>

View file

@ -10,6 +10,7 @@ import ActionMenu, { ActionMenuItem } from '@/components/ActionMenu';
import Button from '@/components/Button';
import Card from '@/components/Card';
import CodeEditor from '@/components/CodeEditor';
import DetailsSkeleton from '@/components/DetailsSkeleton';
import Drawer from '@/components/Drawer';
import ImagePlaceholder from '@/components/ImagePlaceholder';
import LinkButton from '@/components/LinkButton';
@ -105,7 +106,7 @@ const ConnectorDetails = () => {
title="admin_console.connector_details.back_to_connectors"
className={styles.backLink}
/>
{isLoading && <div>loading</div>}
{isLoading && <DetailsSkeleton />}
{error && <div>{`error occurred: ${error.body.message}`}</div>}
{data && (
<Card className={styles.header}>

View file

@ -14,6 +14,7 @@ import Button from '@/components/Button';
import Card from '@/components/Card';
import CodeEditor from '@/components/CodeEditor';
import CopyToClipboard from '@/components/CopyToClipboard';
import DetailsSkeleton from '@/components/DetailsSkeleton';
import FormField from '@/components/FormField';
import ImagePlaceholder from '@/components/ImagePlaceholder';
import LinkButton from '@/components/LinkButton';
@ -111,7 +112,7 @@ const UserDetails = () => {
title="admin_console.user_details.back_to_users"
className={styles.backLink}
/>
{isLoading && <div>loading</div>}
{isLoading && <DetailsSkeleton />}
{error && <div>{`error occurred: ${error.body.message}`}</div>}
{id && data && (
<>

View file

@ -34,6 +34,7 @@
background-color: $baseColor;
position: relative;
overflow: hidden;
border-radius: 8px;
&::after {
content: '';