diff --git a/.dumi/theme/builtins/ComponentTokenTable/index.tsx b/.dumi/theme/builtins/ComponentTokenTable/index.tsx index 7d64eb2d5a..45134cdca3 100644 --- a/.dumi/theme/builtins/ComponentTokenTable/index.tsx +++ b/.dumi/theme/builtins/ComponentTokenTable/index.tsx @@ -1,13 +1,26 @@ -import { RightOutlined, LinkOutlined, QuestionCircleOutlined } from '@ant-design/icons'; +import React, { useMemo, useState } from 'react'; +import { LinkOutlined, QuestionCircleOutlined, RightOutlined } from '@ant-design/icons'; +import { ConfigProvider, Popover, Table, Typography } from 'antd'; import { createStyles, css, useTheme } from 'antd-style'; import { getDesignToken } from 'antd-token-previewer'; -import React, { useMemo, useState } from 'react'; import tokenMeta from 'antd/es/version/token-meta.json'; import tokenData from 'antd/es/version/token.json'; -import { ConfigProvider, Table, Popover, Typography } from 'antd'; + import useLocale from '../../../hooks/useLocale'; import { useColumns } from '../TokenTable'; +const compare = (token1: string, token2: string) => { + const hasColor1 = token1.toLowerCase().includes('color'); + const hasColor2 = token2.toLowerCase().includes('color'); + if (hasColor1 && !hasColor2) { + return -1; + } + if (!hasColor1 && hasColor2) { + return 1; + } + return token1 < token2 ? -1 : 1; +}; + const defaultToken = getDesignToken(); const locales = { @@ -18,6 +31,8 @@ const locales = { value: '默认值', componentToken: '组件 Token', globalToken: '全局 Token', + componentComment: '这里是你的组件 token', + globalComment: '这里是你的全局 token', help: '如何定制?', customizeTokenLink: '/docs/react/customize-theme-cn#修改主题变量', customizeComponentTokenLink: '/docs/react/customize-theme-cn#修改组件变量', @@ -29,6 +44,8 @@ const locales = { value: 'Default Value', componentToken: 'Component Token', globalToken: 'Global Token', + componentComment: 'here is your component tokens', + globalComment: 'here is your global tokens', help: 'How to use?', customizeTokenLink: '/docs/react/customize-theme#customize-design-token', customizeComponentTokenLink: 'docs/react/customize-theme#customize-component-token', @@ -46,13 +63,13 @@ const useStyle = createStyles(() => ({ `, arrowIcon: css` font-size: 16px; - margin-right: 8px; + margin-inline-end: 8px; & svg { transition: all 0.3s; } `, help: css` - margin-left: 8px; + margin-inline-start: 8px; font-size: 12px; font-weight: normal; color: #999; @@ -69,16 +86,14 @@ interface SubTokenTableProps { helpLink: string; tokens: string[]; component?: string; + comment?: { + componentComment?: string; + globalComment?: string; + }; } -const SubTokenTable: React.FC = ({ - defaultOpen, - tokens, - title, - helpText, - helpLink, - component, -}) => { +const SubTokenTable: React.FC = (props) => { + const { defaultOpen, tokens, title, helpText, helpLink, component, comment } = props; const [, lang] = useLocale(locales); const token = useTheme(); const columns = useColumns(); @@ -92,24 +107,7 @@ const SubTokenTable: React.FC = ({ } const data = tokens - .sort( - component - ? undefined - : (token1, token2) => { - const hasColor1 = token1.toLowerCase().includes('color'); - const hasColor2 = token2.toLowerCase().includes('color'); - - if (hasColor1 && !hasColor2) { - return -1; - } - - if (!hasColor1 && hasColor2) { - return 1; - } - - return token1 < token2 ? -1 : 1; - }, - ) + .sort(component ? undefined : compare) .map((name) => { const meta = component ? tokenMeta.components[component].find((item) => item.token === name) @@ -133,7 +131,7 @@ const SubTokenTable: React.FC = ({ theme={{ components: { ${component}: { - /* here is your component tokens */ + /* ${comment?.componentComment} */ }, }, }} @@ -143,7 +141,7 @@ const SubTokenTable: React.FC = ({ : ` @@ -161,16 +159,17 @@ const SubTokenTable: React.FC = ({ popupStyle={{ width: 400 }} content={ + {/* {code} */}
{code}
- + {helpText}
} > - + {helpText} @@ -217,12 +216,16 @@ const ComponentTokenTable: React.FC = ({ component }) <> {tokenMeta.components[component] && ( item.token)} component={component} - defaultOpen + comment={{ + componentComment: locale.componentComment, + globalComment: locale.globalComment, + }} /> )} = ({ component }) helpText={locale.help} helpLink={locale.customizeComponentTokenLink} tokens={mergedGlobalTokens} + comment={{ + componentComment: locale.componentComment, + globalComment: locale.globalComment, + }} /> ); diff --git a/.dumi/theme/builtins/Container/index.tsx b/.dumi/theme/builtins/Container/index.tsx new file mode 100644 index 0000000000..0d299bf903 --- /dev/null +++ b/.dumi/theme/builtins/Container/index.tsx @@ -0,0 +1,39 @@ +/** + * copied: https://github.com/arvinxx/dumi-theme-antd-style/tree/master/src/builtins/Container + */ +import * as React from 'react'; +import { Alert } from 'antd'; +import { type FC, type ReactNode } from 'react'; +import useStyles from './style'; + +const Container: FC<{ + type: 'info' | 'warning' | 'success' | 'error'; + title?: string; + children: ReactNode; +}> = ({ type, title, children }) => { + const { styles, cx } = useStyles(); + + return ( +
+ + {children} +
+ } + className={styles.alert} + /> + + ); +}; + +export default Container; diff --git a/.dumi/theme/builtins/Container/style.ts b/.dumi/theme/builtins/Container/style.ts new file mode 100644 index 0000000000..eea3d77bd0 --- /dev/null +++ b/.dumi/theme/builtins/Container/style.ts @@ -0,0 +1,22 @@ +import { createStyles } from 'antd-style'; + +const useStyles = createStyles(({ prefixCls, css }) => ({ + container: css` + margin: 8px 0; + `, + + alert: css` + .${prefixCls}-alert-message { + font-weight: bold; + } + `, + + /* 使用 `&&` 加一点点权重 */ + desc: css` + && p { + margin: 0; + } + `, +})); + +export default useStyles; diff --git a/.dumi/theme/slots/Content/ContributorAvatar.tsx b/.dumi/theme/slots/Content/ContributorAvatar.tsx new file mode 100644 index 0000000000..933e06f53d --- /dev/null +++ b/.dumi/theme/slots/Content/ContributorAvatar.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { Avatar, Skeleton, Tooltip } from 'antd'; + +const AvatarPlaceholder: React.FC<{ num?: number }> = ({ num = 3 }) => ( +
  • + {Array.from({ length: num }).map((_, i) => ( + + ))} +
  • +); + +interface ContributorAvatarProps { + username?: string; + url?: string; + loading?: boolean; +} + +const ContributorAvatar: React.FC = ({ username, url, loading }) => { + if (loading) { + return ; + } + if (username?.includes('github-actions')) { + return null; + } + return ( + +
  • + + + {username} + + +
  • +
    + ); +}; + +export default ContributorAvatar; diff --git a/.dumi/theme/slots/Content/Contributors.tsx b/.dumi/theme/slots/Content/Contributors.tsx new file mode 100644 index 0000000000..7b4be21510 --- /dev/null +++ b/.dumi/theme/slots/Content/Contributors.tsx @@ -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 = ({ filename }) => { + const { formatMessage } = useIntl(); + const { styles } = useStyle(); + const { isMobile } = useContext(SiteContext); + + if (!filename) { + return null; + } + + return ( +
    +
    {formatMessage({ id: 'app.content.contributors' })}
    + ( + + )} + /> +
    + ); +}; + +export default Contributors; diff --git a/.dumi/theme/slots/Content/DocAnchor.tsx b/.dumi/theme/slots/Content/DocAnchor.tsx new file mode 100644 index 0000000000..c15d278e23 --- /dev/null +++ b/.dumi/theme/slots/Content/DocAnchor.tsx @@ -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 = ({ 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: ( + + {child?.title} + + ), + })), + }); + + 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], + ); + + if (!meta.frontmatter.toc) { + return null; + } + + return ( +
    + +
    + ); +}; + +export default DocAnchor; diff --git a/.dumi/theme/slots/Content/DocMeta.tsx b/.dumi/theme/slots/Content/DocMeta.tsx new file mode 100644 index 0000000000..b0de544ec4 --- /dev/null +++ b/.dumi/theme/slots/Content/DocMeta.tsx @@ -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 ; + } + return ( + + {name} + + ); +}; + +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 ( + + + {meta.frontmatter.date && ( + + {DayJS(meta.frontmatter.date).format('YYYY-MM-DD')} + + )} + {mergedAuthorInfos.map((info) => ( + + + + @{info.name} + + + ))} + + + ); +}; + +export default DocMeta; diff --git a/.dumi/theme/slots/Content/index.tsx b/.dumi/theme/slots/Content/index.tsx index 59365bdd03..1036e7918f 100644 --- a/.dumi/theme/slots/Content/index.tsx +++ b/.dumi/theme/slots/Content/index.tsx @@ -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 DayJS from 'dayjs'; -import { FormattedMessage, useIntl, useRouteMeta, useTabMeta } from 'dumi'; +import { FormattedMessage, useRouteMeta } from 'dumi'; import type { ReactNode } from 'react'; -import React, { useContext, useLayoutEffect, useMemo, useState } from 'react'; -import { Anchor, Avatar, Col, Skeleton, Space, Tooltip, Typography } from 'antd'; +import React, { Suspense, useContext, useLayoutEffect, useMemo } from 'react'; +import { Col, Space, Typography, Skeleton } from 'antd'; +import { createStyles } from 'antd-style'; import useLayoutState from '../../../hooks/useLayoutState'; 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 DemoContext from '../DemoContext'; -import Footer from '../Footer'; import SiteContext from '../SiteContext'; -import ColumnCard from './ColumnCard'; -const useStyle = createStyles(({ token, css }) => { - const { antCls } = token; +const Contributors = React.lazy(() => import('./Contributors')); +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 { - contributorsList: 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` +const useStyle = createStyles(({ token, css }) => ({ + articleWrapper: css` padding: 0 170px 32px 64px; &.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 }) => ( -
  • - {Array.from({ length: num }).map((_, i) => ( - - ))} -
  • -); - -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 ; - } - return ( - - {name} - - ); -}; +})); const Content: React.FC<{ children: ReactNode }> = ({ children }) => { const meta = useRouteMeta(); - const tab = useTabMeta(); const { pathname, hash } = useLocation(); - const { formatMessage } = useIntl(); + const { direction } = useContext(SiteContext); const { styles } = useStyle(); - const token = useTheme(); - const { direction, isMobile } = useContext(SiteContext); const [showDebug, setShowDebug] = useLayoutState(false); const debugDemos = useMemo( @@ -167,71 +59,14 @@ const Content: React.FC<{ children: ReactNode }> = ({ children }) => { [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'; - 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 ( - {!!meta.frontmatter.toc && ( -
    - ({ - 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 ? ( @@ -240,90 +75,43 @@ const Content: React.FC<{ children: ReactNode }> = ({ children }) => { {meta.frontmatter?.subtitle} {!pathname.startsWith('/components/overview') && ( - } - filename={meta.frontmatter.filename} - /> + + } + filename={meta.frontmatter.filename} + /> + )} - {pathname.startsWith('/components/') && } + {pathname.startsWith('/components/') && ( + + + + )} ) : 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?.juejin_url) && ( + }> - )} - {meta.frontmatter.filename && ( - { - if (!item || loading) { - return ; - } - if (item.username?.includes('github-actions')) { - return null; - } - return ( - -
  • - - - {item.username} - - -
  • -
    - ); - }} - /> - )} +
    + }> + +
    - -