0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-02-17 22:04:19 -05:00

refactor(console): add skeleton for custom domain page (#4080)

This commit is contained in:
Xiao Yijun 2023-06-26 19:07:33 +08:00 committed by GitHub
parent 71650832d0
commit 12b9e7dfe0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 195 additions and 140 deletions

View file

@ -55,38 +55,4 @@
height: 28px;
@include _.shimmering-animation;
}
.content {
.introduction {
.title {
@include _.shimmering-animation;
height: 16px;
width: 80px;
}
.description {
.text {
@include _.shimmering-animation;
width: 100%;
height: 10px;
}
.text + .text {
margin-top: _.unit(2);
}
}
}
.form {
.field {
@include _.shimmering-animation;
width: 100%;
height: 44px;
}
.field + .field {
margin-top: _.unit(6);
}
}
}
}

View file

@ -0,0 +1,24 @@
import { FormCardSkeleton } from '@/components/FormCard';
import Spacer from '@/ds-components/Spacer';
import * as styles from './index.module.scss';
function Skeleton() {
return (
<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.tabBar} />
<FormCardSkeleton />
</div>
);
}
export default Skeleton;

View file

@ -8,9 +8,9 @@ import DynamicT from '@/ds-components/DynamicT';
import TextLink from '@/ds-components/TextLink';
import type { RequestError } from '@/hooks/use-api';
import DetailsSkeleton from '../DetailsSkeleton';
import RequestDataError from '../RequestDataError';
import Skeleton from './Skeleton';
import * as styles from './index.module.scss';
type Props = {
@ -38,7 +38,7 @@ function DetailsPage({
{typeof backLinkTitle === 'string' ? <DynamicT forKey={backLinkTitle} /> : backLinkTitle}
</TextLink>
{isLoading ? (
<DetailsSkeleton />
<Skeleton />
) : error ? (
<RequestDataError error={error} onRetry={onRetry} />
) : (

View file

@ -1,43 +0,0 @@
import classNames from 'classnames';
import * as formCardStyles from '@/components/FormCard/index.module.scss';
import Card from '@/ds-components/Card';
import Spacer from '@/ds-components/Spacer';
import * as styles from './index.module.scss';
function DetailsSkeleton() {
return (
<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.tabBar} />
<Card className={classNames(formCardStyles.container, styles.content)}>
<div className={classNames(formCardStyles.introduction, styles.introduction)}>
<div className={styles.title} />
<div className={styles.description}>
{Array.from({ length: 2 }).map((_, index) => (
// eslint-disable-next-line react/no-array-index-key
<div key={index} className={styles.text} />
))}
</div>
</div>
<div className={classNames(formCardStyles.form, styles.form)}>
{Array.from({ length: 4 }).map((_, index) => (
// eslint-disable-next-line react/no-array-index-key
<div key={index} className={styles.field} />
))}
</div>
</Card>
</div>
);
}
export default DetailsSkeleton;

View file

@ -0,0 +1,34 @@
@use '@/scss/underscore' as _;
.container {
padding: _.unit(6) _.unit(8);
display: flex;
}
.introduction {
width: 296px;
padding-bottom: _.unit(6);
margin-right: _.unit(14);
flex-shrink: 0;
> :not(:first-child) {
margin-top: _.unit(2);
}
}
.form {
flex-grow: 1;
overflow: hidden;
padding: 0 _.unit(1);
}
@media screen and (max-width: 1080px) {
.container {
flex-direction: column;
.introduction {
width: 100%;
margin-right: unset;
}
}
}

View file

@ -0,0 +1,21 @@
import { type ReactNode } from 'react';
import Card from '@/ds-components/Card';
import * as styles from './index.module.scss';
type Props = {
introduction: ReactNode;
children: ReactNode;
};
function FormCardLayout({ introduction, children }: Props) {
return (
<Card className={styles.container}>
<div className={styles.introduction}>{introduction}</div>
<div className={styles.form}>{children}</div>
</Card>
);
}
export default FormCardLayout;

View file

@ -0,0 +1,27 @@
@use '@/scss/underscore' as _;
.title {
@include _.shimmering-animation;
height: 16px;
width: 80px;
}
.text {
@include _.shimmering-animation;
width: 100%;
height: 10px;
}
.text + .text {
margin-top: _.unit(2);
}
.field {
@include _.shimmering-animation;
width: 100%;
height: 44px;
}
.field + .field {
margin-top: _.unit(6);
}

View file

@ -0,0 +1,32 @@
import FormCardLayout from '../FormCardLayout';
import * as styles from './index.module.scss';
type Props = {
formFieldCount?: number;
};
function Skeleton({ formFieldCount = 4 }: Props) {
return (
<FormCardLayout
introduction={
<>
<div className={styles.title} />
<div>
{Array.from({ length: 2 }).map((_, index) => (
// eslint-disable-next-line react/no-array-index-key
<div key={index} className={styles.text} />
))}
</div>
</>
}
>
{Array.from({ length: formFieldCount }).map((_, index) => (
// eslint-disable-next-line react/no-array-index-key
<div key={index} className={styles.field} />
))}
</FormCardLayout>
);
}
export default Skeleton;

View file

@ -1,44 +1,11 @@
@use '@/scss/underscore' as _;
.container {
padding: _.unit(6) _.unit(8);
display: flex;
.title {
@include _.section-head-1;
color: var(--color-neutral-variant-60);
}
.introduction {
width: 296px;
padding-bottom: _.unit(6);
margin-right: _.unit(14);
flex-shrink: 0;
> :not(:first-child) {
margin-top: _.unit(2);
}
.title {
@include _.section-head-1;
color: var(--color-neutral-variant-60);
}
.description {
font: var(--font-body-2);
color: var(--color-text-secondary);
}
}
@media screen and (max-width: 1080px) {
.container {
flex-direction: column;
.introduction {
width: 100%;
margin-right: unset;
}
}
}
.form {
flex-grow: 1;
overflow: hidden;
padding: 0 _.unit(1);
.description {
font: var(--font-body-2);
color: var(--color-text-secondary);
}

View file

@ -2,10 +2,10 @@ import type { AdminConsoleKey } from '@logto/phrases';
import type { ReactNode } from 'react';
import { useTranslation } from 'react-i18next';
import Card from '@/ds-components/Card';
import DynamicT from '@/ds-components/DynamicT';
import TextLink from '@/ds-components/TextLink';
import FormCardLayout from './FormCardLayout';
import * as styles from './index.module.scss';
type Props = {
@ -19,28 +19,33 @@ function FormCard({ title, description, learnMoreLink, children }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
return (
<Card className={styles.container}>
<div className={styles.introduction}>
<div className={styles.title}>
<DynamicT forKey={title} />
</div>
{description && (
<div className={styles.description}>
<DynamicT forKey={description} />
{learnMoreLink && (
<>
{' '}
<TextLink href={learnMoreLink} target="_blank" rel="noopener">
{t('general.learn_more')}
</TextLink>
</>
)}
<FormCardLayout
introduction={
<>
<div className={styles.title}>
<DynamicT forKey={title} />
</div>
)}
</div>
<div className={styles.form}>{children}</div>
</Card>
{description && (
<div className={styles.description}>
<DynamicT forKey={description} />
{learnMoreLink && (
<>
{' '}
<TextLink href={learnMoreLink} target="_blank" rel="noopener">
{t('general.learn_more')}
</TextLink>
</>
)}
</div>
)}
</>
}
>
{children}
</FormCardLayout>
);
}
export default FormCard;
export { default as FormCardSkeleton } from './Skeleton';

View file

@ -0,0 +1,7 @@
@use '@/scss/underscore' as _;
.container {
display: flex;
flex-direction: column;
gap: _.unit(4);
}

View file

@ -0,0 +1,14 @@
import { FormCardSkeleton } from '@/components/FormCard';
import * as styles from './index.module.scss';
function Skeleton() {
return (
<div className={styles.container}>
<FormCardSkeleton formFieldCount={1} />
<FormCardSkeleton formFieldCount={1} />
</div>
);
}
export default Skeleton;

View file

@ -9,6 +9,7 @@ import useDocumentationUrl from '@/hooks/use-documentation-url';
import AddDomainForm from './AddDomainForm';
import CustomDomain from './CustomDomain';
import DefaultDomain from './DefaultDomain';
import Skeleton from './Skeleton';
import * as styles from './index.module.scss';
function TenantDomainSettings() {
@ -16,7 +17,7 @@ function TenantDomainSettings() {
const { getDocumentationUrl } = useDocumentationUrl();
if (isLoading) {
return null;
return <Skeleton />;
}
return (