import { CalendarOutlined } from '@ant-design/icons'; import { css } from '@emotion/react'; import ContributorsList from '@qixian.cs/github-contributors-list'; import { Affix, Anchor, Avatar, Col, Skeleton, Space, Tooltip, Typography } from 'antd'; import classNames from 'classnames'; import DayJS from 'dayjs'; import { FormattedMessage, useIntl, useRouteMeta, useTabMeta } from 'dumi'; import type { ReactNode } from 'react'; import React, { useContext, useLayoutEffect, useMemo, useState } from 'react'; import useLayoutState from '../../../hooks/useLayoutState'; import useLocation from '../../../hooks/useLocation'; import useSiteToken from '../../../hooks/useSiteToken'; import EditButton from '../../common/EditButton'; import PrevAndNext from '../../common/PrevAndNext'; import type { DemoContextProps } from '../DemoContext'; import DemoContext from '../DemoContext'; import Footer from '../Footer'; import SiteContext from '../SiteContext'; import ColumnCard from './ColumnCard'; const useStyle = () => { const { token } = useSiteToken(); const { antCls } = token; return { contributorsList: css` display: flex; flex-wrap: wrap; margin-top: 120px !important; clear: both; a, ${antCls}-avatar + ${antCls}-avatar { transition: all ${token.motionDurationSlow}; margin-inline-end: -8px; } &:hover { a, ${antCls}-avatar { margin-inline-end: 0; } } `, toc: css` ${antCls}-anchor { ${antCls}-anchor-link-title { font-size: 12px; } } `, tocWrapper: css` position: absolute; top: 8px; inset-inline-end: 0; width: 160px; margin: 12px 0; padding: 8px 0; padding-inline: 4px 8px; backdrop-filter: blur(8px); border-radius: ${token.borderRadius}px; box-sizing: border-box; .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; } } `, }; }; type AnchorItem = { id: string; title: string; children?: AnchorItem[]; }; const AvatarPlaceholder = ({ num = 3 }: { num?: number }) => ( <> {Array.from({ length: num }).map((_, i) => ( ))} ); const AuthorAvatar = ({ name, avatar }: { name: string; avatar: string }) => { 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 ; return ( {name} ); }; const Content: React.FC<{ children: ReactNode }> = ({ children }) => { const meta = useRouteMeta(); const tab = useTabMeta(); const { pathname, hash } = useLocation(); const { formatMessage } = useIntl(); const styles = useStyle(); const { token } = useSiteToken(); const { direction } = useContext(SiteContext); const [showDebug, setShowDebug] = useLayoutState(false); const debugDemos = useMemo( () => meta.toc?.filter((item) => item._debug_demo).map((item) => item.id) || [], [meta], ); const isDebugDemo = debugDemos.includes(hash.slice(1)); useLayoutEffect(() => { setShowDebug(process.env.NODE_ENV === 'development' || isDebugDemo); }, []); const contextValue = useMemo( () => ({ showDebug, setShowDebug }), [showDebug, debugDemos], ); const anchorItems = useMemo( () => (tab?.toc || meta.toc).reduce((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'; // support custom author info in frontmatter // e.g. // --- // author: // - name: qixian // avatar: https://avatars.githubusercontent.com/u/11746742?v=4 // - name: yutingzhao1991 // avatar: https://avatars.githubusercontent.com/u/5378891?v=4 // --- 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 (
({ 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: ( {child?.title} ), })), }))} />
{meta.frontmatter?.title ? ( {meta.frontmatter?.title} {meta.frontmatter.subtitle && ( {meta.frontmatter.subtitle} )} {!pathname.startsWith('/components/overview') && ( } filename={meta.frontmatter.filename} /> )} ) : null} {/* 添加作者、时间等信息 */} {meta.frontmatter.date || meta.frontmatter.author ? ( {meta.frontmatter.date && ( {DayJS(meta.frontmatter.date).format('YYYY-MM-DD')} )} {mergedAuthorInfos.map((info) => ( @{info.name} ))} ) : null} {!meta.frontmatter.__autoDescription && meta.frontmatter.description} {children} {(meta.frontmatter?.zhihu_url || meta.frontmatter?.yuque_url) && ( )} {meta.frontmatter.filename && ( loading || !item ? ( ) : ( {item.username} ) } /> )}