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:
parent
71650832d0
commit
12b9e7dfe0
13 changed files with 195 additions and 140 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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} />
|
||||
) : (
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
32
packages/console/src/components/FormCard/Skeleton/index.tsx
Normal file
32
packages/console/src/components/FormCard/Skeleton/index.tsx
Normal 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;
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: _.unit(4);
|
||||
}
|
|
@ -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;
|
|
@ -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 (
|
||||
|
|
Loading…
Add table
Reference in a new issue