mirror of
https://github.com/ant-design/ant-design.git
synced 2025-07-30 11:26:27 +08:00
refactor: import other components async in component doc (#46491)
* refactor: ContributorAvatar component * refactor: import other components async in component doc * chore: fix lint * docs: add suspense fallback * docs: add suspense fallback * docs: add suspense fallback
This commit is contained in:
parent
f6d43add73
commit
acbbbfebc2
38
.dumi/theme/slots/Content/ContributorAvatar.tsx
Normal file
38
.dumi/theme/slots/Content/ContributorAvatar.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Avatar, Skeleton, Tooltip } from 'antd';
|
||||||
|
|
||||||
|
const AvatarPlaceholder: React.FC<{ num?: number }> = ({ num = 3 }) => (
|
||||||
|
<li>
|
||||||
|
{Array.from({ length: num }).map((_, i) => (
|
||||||
|
<Skeleton.Avatar size="small" active key={i} style={{ marginLeft: i === 0 ? 0 : -8 }} />
|
||||||
|
))}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
|
||||||
|
interface ContributorAvatarProps {
|
||||||
|
username?: string;
|
||||||
|
url?: string;
|
||||||
|
loading?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ContributorAvatar: React.FC<ContributorAvatarProps> = ({ username, url, loading }) => {
|
||||||
|
if (loading) {
|
||||||
|
return <AvatarPlaceholder />;
|
||||||
|
}
|
||||||
|
if (username?.includes('github-actions')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Tooltip title={username}>
|
||||||
|
<li>
|
||||||
|
<a href={`https://github.com/${username}`} target="_blank" rel="noopener noreferrer">
|
||||||
|
<Avatar size="small" src={url} alt={username}>
|
||||||
|
{username}
|
||||||
|
</Avatar>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ContributorAvatar;
|
82
.dumi/theme/slots/Content/Contributors.tsx
Normal file
82
.dumi/theme/slots/Content/Contributors.tsx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import React, { useContext } from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { useIntl } from 'dumi';
|
||||||
|
import { createStyles } from 'antd-style';
|
||||||
|
import ContributorsList from '@qixian.cs/github-contributors-list';
|
||||||
|
import ContributorAvatar from './ContributorAvatar';
|
||||||
|
import SiteContext from '../SiteContext';
|
||||||
|
|
||||||
|
const useStyle = createStyles(({ token, css }) => {
|
||||||
|
const { antCls } = token;
|
||||||
|
|
||||||
|
return {
|
||||||
|
contributorsList: css`
|
||||||
|
margin-top: 120px !important;
|
||||||
|
`,
|
||||||
|
listMobile: css`
|
||||||
|
margin: 1em 0 !important;
|
||||||
|
`,
|
||||||
|
title: css`
|
||||||
|
font-size: 12px;
|
||||||
|
opacity: 0.45;
|
||||||
|
`,
|
||||||
|
list: css`
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
clear: both;
|
||||||
|
|
||||||
|
li {
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li,
|
||||||
|
${antCls}-avatar + ${antCls}-avatar {
|
||||||
|
transition: all ${token.motionDurationSlow};
|
||||||
|
margin-inline-end: -8px;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
li,
|
||||||
|
${antCls}-avatar {
|
||||||
|
margin-inline-end: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
interface ContributorsProps {
|
||||||
|
filename?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Contributors: React.FC<ContributorsProps> = ({ filename }) => {
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
const { styles } = useStyle();
|
||||||
|
const { isMobile } = useContext(SiteContext);
|
||||||
|
|
||||||
|
if (!filename) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames(styles.contributorsList, { [styles.listMobile]: isMobile })}>
|
||||||
|
<div className={styles.title}>{formatMessage({ id: 'app.content.contributors' })}</div>
|
||||||
|
<ContributorsList
|
||||||
|
cache
|
||||||
|
repo="ant-design"
|
||||||
|
owner="ant-design"
|
||||||
|
fileName={filename}
|
||||||
|
className={styles.list}
|
||||||
|
renderItem={(item, loading) => (
|
||||||
|
<ContributorAvatar
|
||||||
|
key={item?.username}
|
||||||
|
username={item?.username}
|
||||||
|
url={item?.url}
|
||||||
|
loading={loading}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Contributors;
|
137
.dumi/theme/slots/Content/DocAnchor.tsx
Normal file
137
.dumi/theme/slots/Content/DocAnchor.tsx
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { Anchor } from 'antd';
|
||||||
|
import { createStyles, useTheme } from 'antd-style';
|
||||||
|
import { useRouteMeta, useTabMeta } from 'dumi';
|
||||||
|
|
||||||
|
const useStyle = createStyles(({ token, css }) => {
|
||||||
|
const { antCls } = token;
|
||||||
|
|
||||||
|
return {
|
||||||
|
toc: css`
|
||||||
|
${antCls}-anchor {
|
||||||
|
${antCls}-anchor-link-title {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
tocWrapper: css`
|
||||||
|
position: fixed;
|
||||||
|
top: ${token.headerHeight + token.contentMarginTop}px;
|
||||||
|
inset-inline-end: 0;
|
||||||
|
width: 160px;
|
||||||
|
margin: 0 0 12px 0;
|
||||||
|
padding: 8px 0;
|
||||||
|
padding-inline: 4px 8px;
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
border-radius: ${token.borderRadius}px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
z-index: 1000;
|
||||||
|
|
||||||
|
.toc-debug {
|
||||||
|
color: ${token.purple6};
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: ${token.purple5};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> div {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
max-height: calc(100vh - 40px) !important;
|
||||||
|
margin: 0 auto;
|
||||||
|
overflow: auto;
|
||||||
|
padding-inline: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: ${token.screenLG}px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
articleWrapper: css`
|
||||||
|
padding: 0 170px 32px 64px;
|
||||||
|
|
||||||
|
&.rtl {
|
||||||
|
padding: 0 64px 144px 170px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: ${token.screenLG}px) {
|
||||||
|
&,
|
||||||
|
&.rtl {
|
||||||
|
padding: 0 48px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
interface DocAnchorProps {
|
||||||
|
showDebug?: boolean;
|
||||||
|
debugDemos?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
type AnchorItem = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
children?: AnchorItem[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const DocAnchor: React.FC<DocAnchorProps> = ({ showDebug, debugDemos = [] }) => {
|
||||||
|
const { styles } = useStyle();
|
||||||
|
const token = useTheme();
|
||||||
|
const meta = useRouteMeta();
|
||||||
|
const tab = useTabMeta();
|
||||||
|
|
||||||
|
const renderAnchorItem = (item: AnchorItem) => ({
|
||||||
|
href: `#${item.id}`,
|
||||||
|
title: item.title,
|
||||||
|
key: item.id,
|
||||||
|
children: item.children
|
||||||
|
?.filter((child) => showDebug || !debugDemos.includes(child.id))
|
||||||
|
.map((child) => ({
|
||||||
|
key: child.id,
|
||||||
|
href: `#${child.id}`,
|
||||||
|
title: (
|
||||||
|
<span className={classNames(debugDemos.includes(child.id) && 'toc-debug')}>
|
||||||
|
{child?.title}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
|
||||||
|
const anchorItems = useMemo(
|
||||||
|
() =>
|
||||||
|
(tab?.toc || meta.toc).reduce<AnchorItem[]>((result, item) => {
|
||||||
|
if (item.depth === 2) {
|
||||||
|
result.push({ ...item });
|
||||||
|
} else if (item.depth === 3) {
|
||||||
|
const parent = result[result.length - 1];
|
||||||
|
if (parent) {
|
||||||
|
parent.children = parent.children || [];
|
||||||
|
parent.children.push({ ...item });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}, []),
|
||||||
|
[tab?.toc, meta.toc],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!meta.frontmatter.toc) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={styles.tocWrapper}>
|
||||||
|
<Anchor
|
||||||
|
className={styles.toc}
|
||||||
|
affix={false}
|
||||||
|
targetOffset={token.anchorTop}
|
||||||
|
showInkInFixed
|
||||||
|
items={anchorItems.map(renderAnchorItem)}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DocAnchor;
|
79
.dumi/theme/slots/Content/DocMeta.tsx
Normal file
79
.dumi/theme/slots/Content/DocMeta.tsx
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import React, { useState, useLayoutEffect, useMemo } from 'react';
|
||||||
|
import { Typography, Space, Skeleton, Avatar } from 'antd';
|
||||||
|
import { useRouteMeta } from 'dumi';
|
||||||
|
import DayJS from 'dayjs';
|
||||||
|
import { CalendarOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
|
const AuthorAvatar: React.FC<{ name: string; avatar: string }> = ({ name, avatar }) => {
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [error, setError] = useState(false);
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
const img = new Image();
|
||||||
|
img.src = avatar;
|
||||||
|
img.onload = () => setLoading(false);
|
||||||
|
img.onerror = () => setError(true);
|
||||||
|
}, []);
|
||||||
|
if (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (loading) {
|
||||||
|
return <Skeleton.Avatar size="small" active />;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Avatar size="small" src={avatar} alt={name}>
|
||||||
|
{name}
|
||||||
|
</Avatar>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const DocMeta: React.FC<{}> = () => {
|
||||||
|
const meta = useRouteMeta();
|
||||||
|
|
||||||
|
const mergedAuthorInfos = useMemo(() => {
|
||||||
|
const { author } = meta.frontmatter;
|
||||||
|
if (!author) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
if (typeof author === 'string') {
|
||||||
|
return author.split(',').map((item) => ({
|
||||||
|
name: item,
|
||||||
|
avatar: `https://github.com/${item}.png`,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
if (Array.isArray(author)) {
|
||||||
|
return author;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}, [meta.frontmatter.author]);
|
||||||
|
|
||||||
|
if (!meta.frontmatter.date && !meta.frontmatter.author) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Typography.Paragraph>
|
||||||
|
<Space>
|
||||||
|
{meta.frontmatter.date && (
|
||||||
|
<span style={{ opacity: 0.65 }}>
|
||||||
|
<CalendarOutlined /> {DayJS(meta.frontmatter.date).format('YYYY-MM-DD')}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{mergedAuthorInfos.map((info) => (
|
||||||
|
<a
|
||||||
|
href={`https://github.com/${info.name}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
key={info.name}
|
||||||
|
>
|
||||||
|
<Space size={3}>
|
||||||
|
<AuthorAvatar name={info.name} avatar={info.avatar} />
|
||||||
|
<span style={{ opacity: 0.65 }}>@{info.name}</span>
|
||||||
|
</Space>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</Space>
|
||||||
|
</Typography.Paragraph>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DocMeta;
|
@ -1,94 +1,26 @@
|
|||||||
import { CalendarOutlined } from '@ant-design/icons';
|
|
||||||
import { createStyles, useTheme } from 'antd-style';
|
|
||||||
import ContributorsList from '@qixian.cs/github-contributors-list';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import DayJS from 'dayjs';
|
import { FormattedMessage, useRouteMeta } from 'dumi';
|
||||||
import { FormattedMessage, useIntl, useRouteMeta, useTabMeta } from 'dumi';
|
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import React, { useContext, useLayoutEffect, useMemo, useState } from 'react';
|
import React, { Suspense, useContext, useLayoutEffect, useMemo } from 'react';
|
||||||
import { Anchor, Avatar, Col, Skeleton, Space, Tooltip, Typography } from 'antd';
|
import { Col, Space, Typography, Skeleton } from 'antd';
|
||||||
|
import { createStyles } from 'antd-style';
|
||||||
import useLayoutState from '../../../hooks/useLayoutState';
|
import useLayoutState from '../../../hooks/useLayoutState';
|
||||||
import useLocation from '../../../hooks/useLocation';
|
import useLocation from '../../../hooks/useLocation';
|
||||||
import EditButton from '../../common/EditButton';
|
|
||||||
import PrevAndNext from '../../common/PrevAndNext';
|
|
||||||
import ComponentChangelog from '../../common/ComponentChangelog';
|
|
||||||
import type { DemoContextProps } from '../DemoContext';
|
import type { DemoContextProps } from '../DemoContext';
|
||||||
import DemoContext from '../DemoContext';
|
import DemoContext from '../DemoContext';
|
||||||
import Footer from '../Footer';
|
|
||||||
import SiteContext from '../SiteContext';
|
import SiteContext from '../SiteContext';
|
||||||
import ColumnCard from './ColumnCard';
|
|
||||||
|
|
||||||
const useStyle = createStyles(({ token, css }) => {
|
const Contributors = React.lazy(() => import('./Contributors'));
|
||||||
const { antCls } = token;
|
const ColumnCard = React.lazy(() => import('./ColumnCard'));
|
||||||
|
const DocAnchor = React.lazy(() => import('./DocAnchor'));
|
||||||
|
const DocMeta = React.lazy(() => import('./DocMeta'));
|
||||||
|
const Footer = React.lazy(() => import('../Footer'));
|
||||||
|
const PrevAndNext = React.lazy(() => import('../../common/PrevAndNext'));
|
||||||
|
const ComponentChangelog = React.lazy(() => import('../../common/ComponentChangelog'));
|
||||||
|
const EditButton = React.lazy(() => import('../../common/EditButton'));
|
||||||
|
|
||||||
return {
|
const useStyle = createStyles(({ token, css }) => ({
|
||||||
contributorsList: css`
|
articleWrapper: css`
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
margin-top: 120px !important;
|
|
||||||
clear: both;
|
|
||||||
|
|
||||||
li {
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
li,
|
|
||||||
${antCls}-avatar + ${antCls}-avatar {
|
|
||||||
transition: all ${token.motionDurationSlow};
|
|
||||||
margin-inline-end: -8px;
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
li,
|
|
||||||
${antCls}-avatar {
|
|
||||||
margin-inline-end: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
listMobile: css`
|
|
||||||
margin: 1em 0 !important;
|
|
||||||
`,
|
|
||||||
toc: css`
|
|
||||||
${antCls}-anchor {
|
|
||||||
${antCls}-anchor-link-title {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
tocWrapper: css`
|
|
||||||
position: fixed;
|
|
||||||
top: ${token.headerHeight + token.contentMarginTop}px;
|
|
||||||
inset-inline-end: 0;
|
|
||||||
width: 160px;
|
|
||||||
margin: 0 0 12px 0;
|
|
||||||
padding: 8px 0;
|
|
||||||
padding-inline: 4px 8px;
|
|
||||||
backdrop-filter: blur(8px);
|
|
||||||
border-radius: ${token.borderRadius}px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
z-index: 1000;
|
|
||||||
|
|
||||||
.toc-debug {
|
|
||||||
color: ${token.purple6};
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: ${token.purple5};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> div {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
max-height: calc(100vh - 40px) !important;
|
|
||||||
margin: 0 auto;
|
|
||||||
overflow: auto;
|
|
||||||
padding-inline: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: ${token.screenLG}px) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
articleWrapper: css`
|
|
||||||
padding: 0 170px 32px 64px;
|
padding: 0 170px 32px 64px;
|
||||||
|
|
||||||
&.rtl {
|
&.rtl {
|
||||||
@ -102,53 +34,13 @@ const useStyle = createStyles(({ token, css }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
};
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
type AnchorItem = {
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
children?: AnchorItem[];
|
|
||||||
};
|
|
||||||
|
|
||||||
const AvatarPlaceholder: React.FC<{ num?: number }> = ({ num = 3 }) => (
|
|
||||||
<li>
|
|
||||||
{Array.from({ length: num }).map((_, i) => (
|
|
||||||
<Skeleton.Avatar size="small" active key={i} style={{ marginLeft: i === 0 ? 0 : -8 }} />
|
|
||||||
))}
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
|
|
||||||
const AuthorAvatar: React.FC<{ name: string; avatar: string }> = ({ name, avatar }) => {
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [error, setError] = useState(false);
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
const img = new Image();
|
|
||||||
img.src = avatar;
|
|
||||||
img.onload = () => setLoading(false);
|
|
||||||
img.onerror = () => setError(true);
|
|
||||||
}, []);
|
|
||||||
if (error) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (loading) {
|
|
||||||
return <Skeleton.Avatar size="small" active />;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Avatar size="small" src={avatar} alt={name}>
|
|
||||||
{name}
|
|
||||||
</Avatar>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const Content: React.FC<{ children: ReactNode }> = ({ children }) => {
|
const Content: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
const meta = useRouteMeta();
|
const meta = useRouteMeta();
|
||||||
const tab = useTabMeta();
|
|
||||||
const { pathname, hash } = useLocation();
|
const { pathname, hash } = useLocation();
|
||||||
const { formatMessage } = useIntl();
|
const { direction } = useContext(SiteContext);
|
||||||
const { styles } = useStyle();
|
const { styles } = useStyle();
|
||||||
const token = useTheme();
|
|
||||||
const { direction, isMobile } = useContext(SiteContext);
|
|
||||||
|
|
||||||
const [showDebug, setShowDebug] = useLayoutState(false);
|
const [showDebug, setShowDebug] = useLayoutState(false);
|
||||||
const debugDemos = useMemo(
|
const debugDemos = useMemo(
|
||||||
@ -167,71 +59,14 @@ const Content: React.FC<{ children: ReactNode }> = ({ children }) => {
|
|||||||
[showDebug, debugDemos],
|
[showDebug, debugDemos],
|
||||||
);
|
);
|
||||||
|
|
||||||
const anchorItems = useMemo(
|
|
||||||
() =>
|
|
||||||
(tab?.toc || meta.toc).reduce<AnchorItem[]>((result, item) => {
|
|
||||||
if (item.depth === 2) {
|
|
||||||
result.push({ ...item });
|
|
||||||
} else if (item.depth === 3) {
|
|
||||||
const parent = result[result.length - 1];
|
|
||||||
if (parent) {
|
|
||||||
parent.children = parent.children || [];
|
|
||||||
parent.children.push({ ...item });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}, []),
|
|
||||||
[tab?.toc, meta.toc],
|
|
||||||
);
|
|
||||||
|
|
||||||
const isRTL = direction === 'rtl';
|
const isRTL = direction === 'rtl';
|
||||||
|
|
||||||
const mergedAuthorInfos = useMemo(() => {
|
|
||||||
const { author } = meta.frontmatter;
|
|
||||||
if (!author) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
if (typeof author === 'string') {
|
|
||||||
return author.split(',').map((item) => ({
|
|
||||||
name: item,
|
|
||||||
avatar: `https://github.com/${item}.png`,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
if (Array.isArray(author)) {
|
|
||||||
return author;
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}, [meta.frontmatter.author]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DemoContext.Provider value={contextValue}>
|
<DemoContext.Provider value={contextValue}>
|
||||||
<Col xxl={20} xl={19} lg={18} md={18} sm={24} xs={24}>
|
<Col xxl={20} xl={19} lg={18} md={18} sm={24} xs={24}>
|
||||||
{!!meta.frontmatter.toc && (
|
<Suspense fallback={<Skeleton.Input active size="small" />}>
|
||||||
<section className={styles.tocWrapper}>
|
<DocAnchor showDebug={showDebug} debugDemos={debugDemos} />
|
||||||
<Anchor
|
</Suspense>
|
||||||
className={styles.toc}
|
|
||||||
affix={false}
|
|
||||||
targetOffset={token.anchorTop}
|
|
||||||
showInkInFixed
|
|
||||||
items={anchorItems.map((item) => ({
|
|
||||||
href: `#${item.id}`,
|
|
||||||
title: item.title,
|
|
||||||
key: item.id,
|
|
||||||
children: item.children
|
|
||||||
?.filter((child) => showDebug || !debugDemos.includes(child.id))
|
|
||||||
.map((child) => ({
|
|
||||||
key: child.id,
|
|
||||||
href: `#${child.id}`,
|
|
||||||
title: (
|
|
||||||
<span className={classNames(debugDemos.includes(child.id) && 'toc-debug')}>
|
|
||||||
{child?.title}
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
})),
|
|
||||||
}))}
|
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
<article className={classNames(styles.articleWrapper, { rtl: isRTL })}>
|
<article className={classNames(styles.articleWrapper, { rtl: isRTL })}>
|
||||||
{meta.frontmatter?.title ? (
|
{meta.frontmatter?.title ? (
|
||||||
<Typography.Title style={{ fontSize: 30, position: 'relative' }}>
|
<Typography.Title style={{ fontSize: 30, position: 'relative' }}>
|
||||||
@ -240,90 +75,43 @@ const Content: React.FC<{ children: ReactNode }> = ({ children }) => {
|
|||||||
{meta.frontmatter?.subtitle}
|
{meta.frontmatter?.subtitle}
|
||||||
|
|
||||||
{!pathname.startsWith('/components/overview') && (
|
{!pathname.startsWith('/components/overview') && (
|
||||||
<EditButton
|
<Suspense fallback={null}>
|
||||||
title={<FormattedMessage id="app.content.edit-page" />}
|
<EditButton
|
||||||
filename={meta.frontmatter.filename}
|
title={<FormattedMessage id="app.content.edit-page" />}
|
||||||
/>
|
filename={meta.frontmatter.filename}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
)}
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
{pathname.startsWith('/components/') && <ComponentChangelog pathname={pathname} />}
|
{pathname.startsWith('/components/') && (
|
||||||
|
<Suspense fallback={null}>
|
||||||
|
<ComponentChangelog pathname={pathname} />
|
||||||
|
</Suspense>
|
||||||
|
)}
|
||||||
</Typography.Title>
|
</Typography.Title>
|
||||||
) : null}
|
) : null}
|
||||||
{/* 添加作者、时间等信息 */}
|
<Suspense fallback={<Skeleton.Input active size="small" />}>
|
||||||
{meta.frontmatter.date || meta.frontmatter.author ? (
|
<DocMeta />
|
||||||
<Typography.Paragraph>
|
</Suspense>
|
||||||
<Space>
|
|
||||||
{meta.frontmatter.date && (
|
|
||||||
<span style={{ opacity: 0.65 }}>
|
|
||||||
<CalendarOutlined /> {DayJS(meta.frontmatter.date).format('YYYY-MM-DD')}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{mergedAuthorInfos.map((info) => (
|
|
||||||
<a
|
|
||||||
href={`https://github.com/${info.name}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
key={info.name}
|
|
||||||
>
|
|
||||||
<Space size={3}>
|
|
||||||
<AuthorAvatar name={info.name} avatar={info.avatar} />
|
|
||||||
<span style={{ opacity: 0.65 }}>@{info.name}</span>
|
|
||||||
</Space>
|
|
||||||
</a>
|
|
||||||
))}
|
|
||||||
</Space>
|
|
||||||
</Typography.Paragraph>
|
|
||||||
) : null}
|
|
||||||
{!meta.frontmatter.__autoDescription && meta.frontmatter.description}
|
{!meta.frontmatter.__autoDescription && meta.frontmatter.description}
|
||||||
<div style={{ minHeight: 'calc(100vh - 64px)' }}>{children}</div>
|
<div style={{ minHeight: 'calc(100vh - 64px)' }}>{children}</div>
|
||||||
{(meta.frontmatter?.zhihu_url ||
|
<Suspense fallback={<Skeleton.Input active size="small" />}>
|
||||||
meta.frontmatter?.yuque_url ||
|
|
||||||
meta.frontmatter?.juejin_url) && (
|
|
||||||
<ColumnCard
|
<ColumnCard
|
||||||
zhihuLink={meta.frontmatter.zhihu_url}
|
zhihuLink={meta.frontmatter.zhihu_url}
|
||||||
yuqueLink={meta.frontmatter.yuque_url}
|
yuqueLink={meta.frontmatter.yuque_url}
|
||||||
juejinLink={meta.frontmatter.juejin_url}
|
juejinLink={meta.frontmatter.juejin_url}
|
||||||
/>
|
/>
|
||||||
)}
|
</Suspense>
|
||||||
{meta.frontmatter.filename && (
|
<Suspense fallback={<Skeleton.Input active size="small" />}>
|
||||||
<ContributorsList
|
<Contributors filename={meta.frontmatter.filename} />
|
||||||
cache
|
</Suspense>
|
||||||
repo="ant-design"
|
|
||||||
owner="ant-design"
|
|
||||||
className={classNames(styles.contributorsList, { [styles.listMobile]: isMobile })}
|
|
||||||
fileName={meta.frontmatter.filename}
|
|
||||||
renderItem={(item, loading) => {
|
|
||||||
if (!item || loading) {
|
|
||||||
return <AvatarPlaceholder />;
|
|
||||||
}
|
|
||||||
if (item.username?.includes('github-actions')) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
mouseEnterDelay={0.3}
|
|
||||||
title={`${formatMessage({ id: 'app.content.contributors' })}: ${item.username}`}
|
|
||||||
key={item.username}
|
|
||||||
>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
href={`https://github.com/${item.username}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Avatar size="small" src={item.url} alt={item.username}>
|
|
||||||
{item.username}
|
|
||||||
</Avatar>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</article>
|
</article>
|
||||||
<PrevAndNext rtl={isRTL} />
|
<Suspense fallback={<Skeleton.Input active size="small" />}>
|
||||||
<Footer />
|
<PrevAndNext rtl={isRTL} />
|
||||||
|
</Suspense>
|
||||||
|
<Suspense fallback={null}>
|
||||||
|
<Footer />
|
||||||
|
</Suspense>
|
||||||
</Col>
|
</Col>
|
||||||
</DemoContext.Provider>
|
</DemoContext.Provider>
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user