diff --git a/.dumi/hooks/useDark.tsx b/.dumi/hooks/useDark.tsx new file mode 100644 index 000000000000..6b31dee12c40 --- /dev/null +++ b/.dumi/hooks/useDark.tsx @@ -0,0 +1,7 @@ +import React from 'react'; + +export const DarkContext = React.createContext(false); + +export default function useDark() { + return React.useContext(DarkContext); +} diff --git a/.dumi/mirror-modal.js b/.dumi/mirror-modal.js new file mode 100644 index 000000000000..71ce7b2e28f4 --- /dev/null +++ b/.dumi/mirror-modal.js @@ -0,0 +1,172 @@ +(function createMirrorModal() { + if ( + (navigator.languages.includes('zh') || navigator.languages.includes('zh-CN')) && + /-cn\/?$/.test(window.location.pathname) && + !['ant-design.gitee.io', 'ant-design.antgroup.com'].includes(window.location.hostname) + ) { + const ANTD_DOT_NOT_SHOW_MIRROR_MODAL = 'ANT_DESIGN_DO_NOT_OPEN_MIRROR_MODAL'; + + const lastShowTime = window.localStorage.getItem(ANTD_DOT_NOT_SHOW_MIRROR_MODAL); + if ( + lastShowTime && + lastShowTime !== 'true' && + Date.now() - new Date(lastShowTime).getTime() < 7 * 24 * 60 * 60 * 1000 + ) { + return; + } + + const style = document.createElement('style'); + style.innerHTML = ` + @keyframes mirror-fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } + } + + @keyframes mirror-zoom-in { + from { + transform: scale(0.8); + } + to { + transform: scale(1); + } + } + + .mirror-modal-mask { + position: fixed; + inset: 0; + height: '100vh'; + width: '100vw'; + background: rgba(0, 0, 0, 0.3); + z-index: 9999; + animation: mirror-fade-in 0.3s forwards; + } + + .mirror-modal-dialog { + position: fixed; + inset: 0; + top: 120px; + margin-left: auto; + margin-right: auto; + width: 400px; + height: 120px; + display: flex; + align-items: center; + flex-direction: column; + border-radius: 8px; + border: 1px solid #eee; + background: #fff; + padding: 20px 24px; + box-shadow: 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 9px 28px 8px rgba(0, 0, 0, 0.05); + animation: mirror-zoom-in 0.3s forwards; + } + + .mirror-modal-title { + font-size: 16px; + font-weight: 500; + align-self: flex-start; + margin-bottom: 8px; + } + + .mirror-modal-content { + font-size: 14px; + align-self: flex-start; + margin-bottom: 16px; + } + + .mirror-modal-btns { + align-self: flex-end; + margin-top: auto; + display: flex; + align-items: center; + } + + .mirror-modal-btn { + border-radius: 6px; + cursor: pointer; + height: 32px; + box-sizing: border-box; + font-size: 14px; + padding: 4px 16px; + display: inline-flex; + align-items: center; + text-decoration: none; + transition: all 0.2s; + } + + .mirror-modal-confirm-btn { + background: #1677ff; + color: #fff; + } + + .mirror-modal-confirm-btn:hover { + background: #4096ff; + } + + .mirror-modal-confirm-btn:active { + background: #0958d9; + } + + .mirror-modal-cancel-btn { + border: 1px solid #eee; + color: #000; + margin-right: 8px; + } + + .mirror-modal-cancel-btn:hover { + border-color: #4096ff; + color: #4096ff + } + + .mirror-modal-cancel-btn:active { + border-color: #0958d9; + color: #0958d9; + } + `; + document.head.append(style); + + const modal = document.createElement('div'); + modal.className = 'mirror-modal-mask'; + + const dialog = document.createElement('div'); + dialog.className = 'mirror-modal-dialog'; + modal.append(dialog); + + const title = document.createElement('div'); + title.className = 'mirror-modal-title'; + title.innerText = '提示'; + dialog.append(title); + + const content = document.createElement('div'); + content.className = 'mirror-modal-content'; + content.innerText = '国内用户推荐访问国内镜像以获得极速体验~'; + dialog.append(content); + + const btnWrapper = document.createElement('div'); + btnWrapper.className = 'mirror-modal-btns'; + dialog.append(btnWrapper); + + const cancelBtn = document.createElement('a'); + cancelBtn.className = 'mirror-modal-cancel-btn mirror-modal-btn'; + cancelBtn.innerText = '7 天内不再显示'; + btnWrapper.append(cancelBtn); + cancelBtn.addEventListener('click', () => { + window.localStorage.setItem(ANTD_DOT_NOT_SHOW_MIRROR_MODAL, new Date().toISOString()); + document.body.removeChild(modal); + document.head.removeChild(style); + document.body.style.overflow = ''; + }); + + const confirmBtn = document.createElement('a'); + confirmBtn.className = 'mirror-modal-confirm-btn mirror-modal-btn'; + confirmBtn.href = window.location.href.replace(window.location.host, 'ant-design.antgroup.com'); + confirmBtn.innerText = '🚀 立刻前往'; + btnWrapper.append(confirmBtn); + + document.body.append(modal); + document.body.style.overflow = 'hidden'; + } +})(); diff --git a/.dumi/pages/index/components/ComponentsList.tsx b/.dumi/pages/index/components/ComponentsList.tsx index 4885c7a0ba92..a528d1c4ce4a 100644 --- a/.dumi/pages/index/components/ComponentsList.tsx +++ b/.dumi/pages/index/components/ComponentsList.tsx @@ -1,21 +1,23 @@ /* eslint-disable react/jsx-pascal-case */ import React, { useContext } from 'react'; -import dayjs from 'dayjs'; import { CustomerServiceOutlined, QuestionCircleOutlined, SyncOutlined } from '@ant-design/icons'; -import { createStyles, css, useTheme } from 'antd-style'; -import classNames from 'classnames'; import { - Space, - Typography, - Tour, - Tag, - DatePicker, Alert, - Modal, + Carousel, + DatePicker, FloatButton, + Modal, Progress, - Carousel, + Space, + Tag, + Tour, + Typography, } from 'antd'; +import { createStyles, css, useTheme } from 'antd-style'; +import classNames from 'classnames'; +import dayjs from 'dayjs'; + +import useDark from '../../../hooks/useDark'; import useLocale from '../../../hooks/useLocale'; import SiteContext from '../../../theme/slots/SiteContext'; import { getCarouselStyle } from './util'; @@ -55,40 +57,45 @@ const locales = { }, }; -const useStyle = createStyles(({ token }) => { - const { carousel } = getCarouselStyle(); +const useStyle = () => { + const isRootDark = useDark(); - return { - card: css` - border-radius: ${token.borderRadius}px; - background: #f5f8ff; - padding: ${token.paddingXL}px; - flex: none; - overflow: hidden; - position: relative; - display: flex; - flex-direction: column; - align-items: stretch; + return createStyles(({ token }) => { + const { carousel } = getCarouselStyle(); - > * { + return { + card: css` + border-radius: ${token.borderRadius}px; + border: 1px solid ${isRootDark ? token.colorBorder : 'transparent'}; + background: ${isRootDark ? token.colorBgContainer : '#f5f8ff'}; + padding: ${token.paddingXL}px; flex: none; - } - `, - cardCircle: css` - position: absolute; - width: 120px; - height: 120px; - background: #1677ff; - border-radius: 50%; - filter: blur(40px); - opacity: 0.1; - `, - mobileCard: css` - height: 395px; - `, - carousel, - }; -}); + overflow: hidden; + position: relative; + display: flex; + flex-direction: column; + align-items: stretch; + + > * { + flex: none; + } + `, + cardCircle: css` + position: absolute; + width: 120px; + height: 120px; + background: #1677ff; + border-radius: 50%; + filter: blur(40px); + opacity: 0.1; + `, + mobileCard: css` + height: 395px; + `, + carousel, + }; + })(); +}; const ComponentItem: React.FC = ({ title, node, type, index }) => { const tagColor = type === 'new' ? 'processing' : 'warning'; diff --git a/.dumi/pages/index/components/DesignFramework.tsx b/.dumi/pages/index/components/DesignFramework.tsx index 4060493b23c4..1f744b343329 100644 --- a/.dumi/pages/index/components/DesignFramework.tsx +++ b/.dumi/pages/index/components/DesignFramework.tsx @@ -1,10 +1,12 @@ import React, { useContext } from 'react'; +import { Col, Row, Typography } from 'antd'; import { createStyles, useTheme } from 'antd-style'; import { Link, useLocation } from 'dumi'; -import { Col, Row, Typography } from 'antd'; + +import useDark from '../../../hooks/useDark'; import useLocale from '../../../hooks/useLocale'; -import * as utils from '../../../theme/utils'; import SiteContext from '../../../theme/slots/SiteContext'; +import * as utils from '../../../theme/utils'; const SECONDARY_LIST = [ { @@ -60,12 +62,17 @@ const locales = { }, }; -const useStyle = createStyles(({ token, css }) => ({ - card: css` +const useStyle = () => { + const isRootDark = useDark(); + + return createStyles(({ token, css }) => ({ + card: css` padding: ${token.paddingSM}px; border-radius: ${token.borderRadius * 2}px; - background: #fff; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03), 0 1px 6px -1px rgba(0, 0, 0, 0.02), + background: ${isRootDark ? 'rgba(0,0,0,0.45)' : token.colorBgElevated}; + box-shadow: + 0 1px 2px rgba(0, 0, 0, 0.03), + 0 1px 6px -1px rgba(0, 0, 0, 0.02), 0 2px 4px rgba(0, 0, 0, 0.02); img { @@ -75,18 +82,19 @@ const useStyle = createStyles(({ token, css }) => ({ } `, - cardMini: css` + cardMini: css` display: block; border-radius: ${token.borderRadius * 2}px; padding: ${token.paddingMD}px ${token.paddingLG}px; - background: rgba(0, 0, 0, 0.02); - border: 1px solid rgba(0, 0, 0, 0.06); + background: ${isRootDark ? 'rgba(0,0,0,0.25)' : 'rgba(0, 0, 0, 0.02)'}; + border: 1px solid ${isRootDark ? 'rgba(255,255,255, 0.45)' : 'rgba(0, 0, 0, 0.06)'}; img { height: 48px; } `, -})); + }))(); +}; export default function DesignFramework() { const [locale] = useLocale(locales); diff --git a/.dumi/pages/index/components/PreviewBanner/ComponentsBlock.tsx b/.dumi/pages/index/components/PreviewBanner/ComponentsBlock.tsx new file mode 100644 index 000000000000..bda81b5b2967 --- /dev/null +++ b/.dumi/pages/index/components/PreviewBanner/ComponentsBlock.tsx @@ -0,0 +1,256 @@ +import React from 'react'; +import { AntDesignOutlined, CheckOutlined, CloseOutlined } from '@ant-design/icons'; +import { + Alert, + Button, + Checkbox, + ColorPicker, + Dropdown, + Input, + message, + Modal, + Progress, + Select, + Slider, + Steps, + Switch, + Tooltip, +} from 'antd'; +import { createStyles } from 'antd-style'; +import classNames from 'classnames'; + +import useLocale from '../../../../hooks/useLocale'; + +const { _InternalPanelDoNotUseOrYouWillBeFired: ModalPanel } = Modal; +const { _InternalPanelDoNotUseOrYouWillBeFired: InternalTooltip } = Tooltip; +const { _InternalPanelDoNotUseOrYouWillBeFired: InternalMessage } = message; + +const locales = { + cn: { + range: '设置范围', + text: 'Ant Design 5.0 使用 CSS-in-JS 技术以提供动态与混合主题的能力。与此同时,我们使用组件级别的 CSS-in-JS 解决方案,让你的应用获得更好的性能。', + infoText: '信息内容展示', + dropdown: '下拉菜单', + finished: '已完成', + inProgress: '进行中', + waiting: '等待中', + option: '选项', + apple: '苹果', + banana: '香蕉', + orange: '橘子', + watermelon: '西瓜', + primary: '主要按钮', + danger: '危险按钮', + default: '默认按钮', + dashed: '虚线按钮', + icon: '图标按钮', + hello: '你好,Ant Design!', + release: 'Ant Design 5.0 正式发布!', + }, + en: { + range: 'Set Range', + text: 'Ant Design 5.0 use CSS-in-JS technology to provide dynamic & mix theme ability. And which use component level CSS-in-JS solution get your application a better performance.', + infoText: 'Info Text', + dropdown: 'Dropdown', + finished: 'Finished', + inProgress: 'In Progress', + waiting: 'Waiting', + option: 'Option', + apple: 'Apple', + banana: 'Banana', + orange: 'Orange', + watermelon: 'Watermelon', + primary: 'Primary', + danger: 'Danger', + default: 'Default', + dashed: 'Dashed', + icon: 'Icon', + hello: 'Hello, Ant Design!', + release: 'Ant Design 5.0 is released!', + }, +}; + +const useStyle = createStyles(({ token, css }) => { + const gap = token.padding; + + return { + holder: css` + width: 500px; + display: flex; + flex-direction: column; + row-gap: ${gap}px; + opacity: 0.65; + `, + + flex: css` + display: flex; + flex-wrap: nowrap; + column-gap: ${gap}px; + `, + ptg_20: css` + flex: 0 1 20%; + `, + ptg_none: css` + flex: none; + `, + block: css` + background-color: ${token.colorBgContainer}; + padding: ${token.paddingXS}px ${token.paddingSM}px; + border-radius: ${token.borderRadius}px; + border: 1px solid ${token.colorBorder}; + `, + noMargin: css` + margin: 0; + `, + }; +}); + +export interface ComponentsBlockProps { + className?: string; +} + +export default function ComponentsBlock(props: ComponentsBlockProps) { + const { className } = props; + + const [locale] = useLocale(locales); + const { styles } = useStyle(); + + return ( +
+ + {locale.text} + + + + + {/* Line */} +
+ +
+ ({ + key: `opt${index}`, + label: `${locale.option} ${index}`, + })), + }} + > + {locale.dropdown} + +
+ + +
+ + + + + + + {/* Line */} +
+ 100°C, + }, + }} + defaultValue={[26, 37]} + /> +
+ + {/* Line */} +
+ + + + + +
+ + {/* Line */} +
+
+ } + unCheckedChildren={} + /> + + +
+
+ +
+ +
+ + + + +
+ ); +} diff --git a/.dumi/pages/index/components/PreviewBanner/index.tsx b/.dumi/pages/index/components/PreviewBanner/index.tsx new file mode 100644 index 000000000000..25ed675aa561 --- /dev/null +++ b/.dumi/pages/index/components/PreviewBanner/index.tsx @@ -0,0 +1,135 @@ +import React from 'react'; +import { Button, ConfigProvider, Space, Typography } from 'antd'; +import { createStyles, useTheme } from 'antd-style'; +import { Link, useLocation } from 'dumi'; + +import useLocale from '../../../../hooks/useLocale'; +import SiteContext from '../../../../theme/slots/SiteContext'; +import * as utils from '../../../../theme/utils'; +import { GroupMask } from '../Group'; +import ComponentsBlock from './ComponentsBlock'; + +const locales = { + cn: { + slogan: '助力设计开发者「更灵活」地搭建出「更美」的产品,让用户「快乐工作」~', + start: '开始使用', + designLanguage: '设计语言', + }, + en: { + slogan: + 'Help designers/developers building beautiful products more flexible and working with happiness', + start: 'Getting Started', + designLanguage: 'Design Language', + }, +}; + +const useStyle = createStyles(({ token, css }) => { + const textShadow = `0 0 3px ${token.colorBgContainer}`; + const { direction } = React.useContext(ConfigProvider.ConfigContext); + const isRTL = direction === 'rtl'; + + return { + holder: css` + height: 520px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + overflow: hidden; + perspective: 800px; + row-gap: ${token.marginXL}px; + `, + + typography: css` + text-align: center; + position: relative; + z-index: 1; + padding-inline: ${token.paddingXL}px; + text-shadow: ${new Array(5) + .fill(null) + .map(() => textShadow) + .join(', ')}; + + h1 { + font-family: AliPuHui, ${token.fontFamily} !important; + font-weight: 900 !important; + font-size: ${token.fontSizeHeading2 * 2}px !important; + line-height: ${token.lineHeightHeading2} !important; + } + + p { + font-size: ${token.fontSizeLG}px !important; + font-weight: normal !important; + margin-bottom: 0; + } + `, + + block: css` + position: absolute; + inset-inline-end: 0; + top: -38px; + transform: ${isRTL ? 'rotate3d(24, 83, -45, 57deg)' : 'rotate3d(24, -83, 45, 57deg)'}; + `, + + child: css` + position: relative; + z-index: 1; + `, + }; +}); + +export interface PreviewBannerProps { + children?: React.ReactNode; +} + +export default function PreviewBanner(props: PreviewBannerProps) { + const { children } = props; + + const [locale] = useLocale(locales); + const { styles } = useStyle(); + const { isMobile } = React.useContext(SiteContext); + const token = useTheme(); + const { pathname, search } = useLocation(); + const isZhCN = utils.isZhCN(pathname); + + return ( + + {/* Image Left Top */} + bg + {/* Image Right Top */} + bg + +
+ {/* Mobile not show the component preview */} + {!isMobile && } + + +

Ant Design 5.0

+

{locale.slogan}

+
+ + + + + + + + + + +
{children}
+
+
+ ); +} diff --git a/.dumi/pages/index/components/Theme/index.tsx b/.dumi/pages/index/components/Theme/index.tsx index 436c90cf61c1..7003d1e5fe83 100644 --- a/.dumi/pages/index/components/Theme/index.tsx +++ b/.dumi/pages/index/components/Theme/index.tsx @@ -1,15 +1,12 @@ -import { TinyColor } from '@ctrl/tinycolor'; +import * as React from 'react'; +import { defaultAlgorithm, defaultTheme } from '@ant-design/compatible'; import { BellOutlined, FolderOutlined, HomeOutlined, QuestionCircleOutlined, } from '@ant-design/icons'; -import { createStyles, css, useTheme } from 'antd-style'; -import * as React from 'react'; -import classNames from 'classnames'; -import { defaultTheme, defaultAlgorithm } from '@ant-design/compatible'; -import { useLocation } from 'dumi'; +import { TinyColor } from '@ctrl/tinycolor'; import type { MenuProps } from 'antd'; import { Breadcrumb, @@ -21,24 +18,29 @@ import { Menu, Radio, Space, - Typography, theme, + Typography, } from 'antd'; +import { createStyles, css, useTheme } from 'antd-style'; import type { Color } from 'antd/es/color-picker'; import { generateColor } from 'antd/es/color-picker/util'; -import * as utils from '../../../../theme/utils'; +import classNames from 'classnames'; +import { useLocation } from 'dumi'; + +import useDark from '../../../../hooks/useDark'; import useLocale from '../../../../hooks/useLocale'; +import Link from '../../../../theme/common/Link'; import SiteContext from '../../../../theme/slots/SiteContext'; +import * as utils from '../../../../theme/utils'; import Group from '../Group'; import { getCarouselStyle } from '../util'; import BackgroundImage from './BackgroundImage'; import ColorPicker from './ColorPicker'; +import { DEFAULT_COLOR, getAvatarURL, getClosetColor, PINK_COLOR } from './colorUtil'; import MobileCarousel from './MobileCarousel'; import RadiusPicker from './RadiusPicker'; import type { THEME } from './ThemePicker'; import ThemePicker from './ThemePicker'; -import { DEFAULT_COLOR, PINK_COLOR, getAvatarURL, getClosetColor } from './colorUtil'; -import Link from '../../../../theme/common/Link'; const { Header, Content, Sider } = Layout; @@ -355,6 +357,15 @@ export default function Theme() { form.setFieldsValue(mergedData); }, [themeType]); + const isRootDark = useDark(); + + React.useEffect(() => { + onThemeChange(null, { + ...themeData, + themeType: isRootDark ? 'dark' : 'default', + }); + }, [isRootDark]); + // ================================ Tokens ================================ const closestColor = getClosetColor(colorPrimaryValue); diff --git a/.dumi/pages/index/index.tsx b/.dumi/pages/index/index.tsx index 71e85f2f51d3..8494a508d605 100644 --- a/.dumi/pages/index/index.tsx +++ b/.dumi/pages/index/index.tsx @@ -1,12 +1,14 @@ +import React from 'react'; +import { ConfigProvider, theme } from 'antd'; import { createStyles, css } from 'antd-style'; -import React, { Suspense } from 'react'; -import { ConfigProvider } from 'antd'; + +import useDark from '../../hooks/useDark'; import useLocale from '../../hooks/useLocale'; -import Banner from './components/Banner'; -import BannerRecommends, { BannerRecommendsFallback } from './components/BannerRecommends'; +// import BannerRecommends, { BannerRecommendsFallback } from './components/BannerRecommends'; import ComponentsList from './components/ComponentsList'; import DesignFramework from './components/DesignFramework'; import Group from './components/Group'; +import PreviewBanner from './components/PreviewBanner'; import Theme from './components/Theme'; const useStyle = createStyles(() => ({ @@ -36,43 +38,57 @@ const locales = { const Homepage: React.FC = () => { const [locale] = useLocale(locales); const { styles } = useStyle(); + const { token } = theme.useToken(); + + const isRootDark = useDark(); return ( - -
- - }> - - - -
+
+ + {/* 文档很久没更新了,先藏起来 */} + {/* }> + + */} + + +
+ {/* 定制主题 */} + - - - - - } - > - - -
-
- + + + {/* 组件列表 */} + + + + + {/* 设计语言 */} + + } + > + + +
+
); }; diff --git a/.dumi/pages/theme-editor/index.tsx b/.dumi/pages/theme-editor/index.tsx index d67fa4ca52d6..9b62a90e8014 100644 --- a/.dumi/pages/theme-editor/index.tsx +++ b/.dumi/pages/theme-editor/index.tsx @@ -44,11 +44,6 @@ const CustomTheme = () => { const storedConfig = localStorage.getItem(ANT_DESIGN_V5_THEME_EDITOR_THEME); if (storedConfig) { const themeConfig = JSON.parse(storedConfig); - const originThemeConfig = { - json: themeConfig, - text: undefined, - }; - setThemeConfigContent(originThemeConfig); setTheme(themeConfig); } }, []); diff --git a/.dumi/theme/builtins/IconSearch/Category.tsx b/.dumi/theme/builtins/IconSearch/Category.tsx index 83b70a309eea..1cbbdf4c57c6 100644 --- a/.dumi/theme/builtins/IconSearch/Category.tsx +++ b/.dumi/theme/builtins/IconSearch/Category.tsx @@ -16,7 +16,7 @@ const Category: React.FC = (props) => { const { icons, title, newIcons, theme } = props; const intl = useIntl(); const [justCopied, setJustCopied] = React.useState(null); - const copyId = React.useRef(null); + const copyId = React.useRef | null>(null); const onCopied = React.useCallback((type: string, text: string) => { message.success( diff --git a/.dumi/theme/builtins/Previewer/CodePreviewer.tsx b/.dumi/theme/builtins/Previewer/CodePreviewer.tsx index 9bbd9b08d2dc..66033bec7dde 100644 --- a/.dumi/theme/builtins/Previewer/CodePreviewer.tsx +++ b/.dumi/theme/builtins/Previewer/CodePreviewer.tsx @@ -8,10 +8,7 @@ import type { Project } from '@stackblitz/sdk'; import stackblitzSdk from '@stackblitz/sdk'; import classNames from 'classnames'; import { FormattedMessage, useSiteData } from 'dumi'; -import toReactElement from 'jsonml-to-react-element'; -import JsonML from 'jsonml.js/lib/utils'; import LZString from 'lz-string'; -import Prism from 'prismjs'; import React, { useContext, useEffect, useRef, useState } from 'react'; import CopyToClipboard from 'react-copy-to-clipboard'; import { Alert, Badge, Space, Tooltip } from 'antd'; @@ -31,28 +28,6 @@ import { ping } from '../../utils'; const { ErrorBoundary } = Alert; -function toReactComponent(jsonML: any) { - return toReactElement(jsonML, [ - [ - (node: any) => JsonML.isElement(node) && JsonML.getTagName(node) === 'pre', - (node: any, index: any) => { - // ref: https://github.com/benjycui/bisheng/blob/master/packages/bisheng/src/bisheng-plugin-highlight/lib/browser.js#L7 - const attr = JsonML.getAttributes(node); - return React.createElement( - 'pre', - { - key: index, - className: `language-${attr.lang}`, - }, - React.createElement('code', { - dangerouslySetInnerHTML: { __html: attr.highlighted }, - }), - ); - }, - ], - ]); -} - function compress(string: string): string { return LZString.compressToBase64(string) .replace(/\+/g, '-') // Convert '+' to '-' @@ -130,13 +105,6 @@ const CodePreviewer: React.FC = (props) => { const [showOnlineUrl, setShowOnlineUrl] = useState(false); - const highlightedCodes = { - jsx: Prism.highlight(jsx, Prism.languages.javascript, 'jsx'), - tsx: Prism.highlight(entryCode, Prism.languages.javascript, 'jsx'), - }; - - const highlightedStyle = style ? Prism.highlight(style, Prism.languages.css, 'css') : ''; - useEffect(() => { const regexp = /preview-(\d+)-ant-design/; // matching PR preview addresses setShowOnlineUrl( @@ -538,17 +506,11 @@ createRoot(document.getElementById('container')).render(); {codeExpand && (
setCodeType(type)} /> - {highlightedStyle ? ( -
-
-                
-              
-
- ) : null}
)} @@ -560,7 +522,9 @@ createRoot(document.getElementById('container')).render(); // resulting in some response delays like following issue: // https://github.com/ant-design/ant-design/issues/39995 // So we insert style tag into head tag. - if (!style) return; + if (!style) { + return; + } const styleTag = document.createElement('style'); styleTag.type = 'text/css'; styleTag.innerHTML = style; diff --git a/.dumi/theme/builtins/Previewer/Previewer.tsx b/.dumi/theme/builtins/Previewer/Previewer.tsx new file mode 100644 index 000000000000..fb23c55e0f71 --- /dev/null +++ b/.dumi/theme/builtins/Previewer/Previewer.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import type { IPreviewerProps } from 'dumi'; +import { useTabMeta } from 'dumi'; + +import CodePreviewer from './CodePreviewer'; +import DesignPreviewer from './DesignPreviewer'; + +export interface AntdPreviewerProps extends IPreviewerProps { + originDebug?: IPreviewerProps['debug']; +} + +const Previewer: React.FC = (props) => { + const tab = useTabMeta(); + + if (tab?.frontmatter.title === 'Design') { + return ; + } + + return ; +}; + +export default Previewer; diff --git a/.dumi/theme/builtins/Previewer/index.tsx b/.dumi/theme/builtins/Previewer/index.tsx index a37144956fd6..e36c556112f8 100644 --- a/.dumi/theme/builtins/Previewer/index.tsx +++ b/.dumi/theme/builtins/Previewer/index.tsx @@ -1,21 +1,40 @@ +import React, { Suspense } from 'react'; import type { IPreviewerProps } from 'dumi'; -import { useTabMeta } from 'dumi'; -import React from 'react'; -import CodePreviewer from './CodePreviewer'; -import DesignPreviewer from './DesignPreviewer'; +import { Skeleton, Alert } from 'antd'; +import { createStyles } from 'antd-style'; -export interface AntdPreviewerProps extends IPreviewerProps { - originDebug?: IPreviewerProps['debug']; -} +const { ErrorBoundary } = Alert; -const Previewer: React.FC = (props) => { - const tab = useTabMeta(); +const Previewer = React.lazy(() => import('./Previewer')); - if (tab?.frontmatter.title === 'Design') { - return ; - } +const useStyle = createStyles(({ css }) => ({ + skeletonWrapper: css` + width: 100% !important; + height: 500px; + margin-bottom: 16px; + `, +})); - return ; +export default (props: IPreviewerProps) => { + const { styles } = useStyle(); + return ( + + + {' '} + + } + > + + + + ); }; - -export default Previewer; diff --git a/.dumi/theme/builtins/TokenCompare/index.tsx b/.dumi/theme/builtins/TokenCompare/index.tsx index a44ffcda8da2..9ae37389f0b0 100644 --- a/.dumi/theme/builtins/TokenCompare/index.tsx +++ b/.dumi/theme/builtins/TokenCompare/index.tsx @@ -4,7 +4,7 @@ import classNames from 'classnames'; import { TinyColor } from '@ctrl/tinycolor'; import { createStyles } from 'antd-style'; import tokenMeta from 'antd/es/version/token-meta.json'; -import { theme, Space } from 'antd'; +import { Space, theme } from 'antd'; import useLocale from '../../../hooks/useLocale'; const useStyle = createStyles(({ token, css }) => { @@ -29,7 +29,7 @@ const useStyle = createStyles(({ token, css }) => { display: flex; align-items: center; justify-content: center; - + color: rgba(0,0,0,0.88); border-right: 1px solid rgba(0, 0, 0, 0.1); `, diff --git a/.dumi/theme/common/CodePreview.tsx b/.dumi/theme/common/CodePreview.tsx index 93c9c4580710..b69303dae1fe 100644 --- a/.dumi/theme/common/CodePreview.tsx +++ b/.dumi/theme/common/CodePreview.tsx @@ -1,37 +1,101 @@ -import React from 'react'; +import React, { useEffect, useMemo } from 'react'; +import Prism from 'prismjs'; +import toReactElement from 'jsonml-to-react-element'; +import JsonML from 'jsonml.js/lib/utils'; import { Tabs } from 'antd'; const LANGS = { tsx: 'TypeScript', jsx: 'JavaScript', + style: 'CSS', }; interface CodePreviewProps { - codes?: Record; - toReactComponent?: (node: any) => React.ReactNode; + sourceCode?: string; + jsxCode?: string; + styleCode?: string; onCodeTypeChange?: (activeKey: string) => void; } -const CodePreview: React.FC = ({ toReactComponent, codes, onCodeTypeChange }) => { - const langList = Object.keys(codes).sort().reverse(); +function toReactComponent(jsonML: any) { + return toReactElement(jsonML, [ + [ + (node: any) => JsonML.isElement(node) && JsonML.getTagName(node) === 'pre', + (node: any, index: any) => { + // ref: https://github.com/benjycui/bisheng/blob/master/packages/bisheng/src/bisheng-plugin-highlight/lib/browser.js#L7 + const attr = JsonML.getAttributes(node); + return React.createElement( + 'pre', + { + key: index, + className: `language-${attr.lang}`, + }, + React.createElement('code', { + dangerouslySetInnerHTML: { __html: attr.highlighted }, + }), + ); + }, + ], + ]); +} + +const CodePreview: React.FC = ({ + sourceCode = '', + jsxCode = '', + styleCode = '', + onCodeTypeChange, +}) => { + // 避免 Tabs 数量不稳定的闪动问题 + const initialCodes = {}; + if (sourceCode) { + initialCodes.tsx = ''; + } + if (jsxCode) { + initialCodes.jsx = ''; + } + if (styleCode) { + initialCodes.style = ''; + } + const [highlightedCodes, setHighlightedCodes] = React.useState(initialCodes); + + useEffect(() => { + const codes = { + tsx: Prism.highlight(sourceCode, Prism.languages.javascript, 'jsx'), + jsx: Prism.highlight(jsxCode, Prism.languages.javascript, 'jsx'), + style: Prism.highlight(styleCode, Prism.languages.css, 'css'), + }; + // 去掉空的代码类型 + Object.keys(codes).forEach((key) => { + if (!codes[key]) { + delete codes[key]; + } + }); + setHighlightedCodes(codes); + }, [jsxCode, sourceCode, styleCode]); + + const langList = Object.keys(highlightedCodes); + const items = useMemo( + () => + langList.map((lang) => ({ + label: LANGS[lang], + key: lang, + children: toReactComponent(['pre', { lang, highlighted: highlightedCodes[lang] }]), + })), + [JSON.stringify(highlightedCodes)], + ); + + if (!langList.length) { + return null; + } + if (langList.length === 1) { return toReactComponent([ 'pre', - { lang: langList[0], highlighted: codes[langList[0]], className: 'highlight' }, + { lang: langList[0], highlighted: highlightedCodes[langList[0]], className: 'highlight' }, ]); } - return ( - ({ - label: LANGS[lang], - key: lang, - children: toReactComponent(['pre', { lang, highlighted: codes[lang] }]), - }))} - /> - ); + + return ; }; export default CodePreview; diff --git a/.dumi/theme/common/Color/ColorPaletteTool.tsx b/.dumi/theme/common/Color/ColorPaletteTool.tsx index 5f9b4319eb68..2c2de6d3f9ef 100644 --- a/.dumi/theme/common/Color/ColorPaletteTool.tsx +++ b/.dumi/theme/common/Color/ColorPaletteTool.tsx @@ -3,26 +3,44 @@ import React, { useMemo, useState } from 'react'; import { ColorPicker } from 'antd'; import type { Color } from 'antd/es/color-picker'; import ColorPatterns from './ColorPatterns'; +import useLocale from '../../../hooks/useLocale'; const primaryMinSaturation = 70; // 主色推荐最小饱和度 const primaryMinBrightness = 70; // 主色推荐最小亮度 +const locales = { + cn: { + saturation: (s: string) => `饱和度建议不低于${primaryMinSaturation}(现在${s})`, + brightness: (b: string) => `亮度建议不低于${primaryMinBrightness}(现在${b})`, + }, + en: { + saturation: (s: string) => + `Saturation is recommended not to be lower than ${primaryMinSaturation}(currently${s})`, + brightness: (b: string) => + `Brightness is recommended not to be lower than ${primaryMinBrightness}(currently${b})`, + }, +}; + const ColorPaletteTool: React.FC = () => { const [primaryColor, setPrimaryColor] = useState('#1890ff'); const [primaryColorInstance, setPrimaryColorInstance] = useState(null); + + const [locale] = useLocale(locales); + const handleChangeColor = (color: Color, hex: string) => { setPrimaryColor(hex); setPrimaryColorInstance(color); }; + const colorValidation = useMemo(() => { let text = ''; if (primaryColorInstance) { - const { s, b } = primaryColorInstance.toHsb(); + const { s, b } = primaryColorInstance.toHsb() || {}; if (s * 100 < primaryMinSaturation) { - text += ` 饱和度建议不低于${primaryMinSaturation}(现在 ${(s * 100).toFixed(2)})`; + text += locale.saturation((s * 100).toFixed(2)); } if (b * 100 < primaryMinBrightness) { - text += ` 亮度建议不低于${primaryMinBrightness}(现在 ${(b * 100).toFixed(2)})`; + text += locale.brightness((s * 100).toFixed(2)); } } return {text.trim()}; @@ -32,7 +50,9 @@ const ColorPaletteTool: React.FC = () => {
-
{ColorPatterns({ color: primaryColor })}
+
+ +
diff --git a/.dumi/theme/common/Color/ColorPaletteToolDark.tsx b/.dumi/theme/common/Color/ColorPaletteToolDark.tsx index 2c81fa008a5c..59400cfae121 100644 --- a/.dumi/theme/common/Color/ColorPaletteToolDark.tsx +++ b/.dumi/theme/common/Color/ColorPaletteToolDark.tsx @@ -2,14 +2,30 @@ import { FormattedMessage } from 'dumi'; import React, { useMemo, useState } from 'react'; import { Col, ColorPicker, Row } from 'antd'; import ColorPatterns from './ColorPatterns'; +import useLocale from '../../../hooks/useLocale'; const primaryMinSaturation = 70; // 主色推荐最小饱和度 const primaryMinBrightness = 70; // 主色推荐最小亮度 +const locales = { + cn: { + saturation: (s: string) => `饱和度建议不低于${primaryMinSaturation}(现在${s})`, + brightness: (b: string) => `亮度建议不低于${primaryMinBrightness}(现在${b})`, + }, + en: { + saturation: (s: string) => + `Saturation is recommended not to be lower than ${primaryMinSaturation}(currently${s})`, + brightness: (b: string) => + `Brightness is recommended not to be lower than ${primaryMinBrightness}(currently${b})`, + }, +}; + const ColorPaletteTool: React.FC = () => { const [primaryColor, setPrimaryColor] = useState('#1890ff'); const [backgroundColor, setBackgroundColor] = useState('#141414'); - const [primaryColorInstance, setPrimaryColorInstance] = useState(null); + const [primaryColorInstance, setPrimaryColorInstance] = useState(null); + + const [locale] = useLocale(locales); const handleChangeColor = (color: Color, hex: string) => { setPrimaryColor(hex); @@ -23,12 +39,12 @@ const ColorPaletteTool: React.FC = () => { const colorValidation = useMemo(() => { let text = ''; if (primaryColorInstance) { - const { s, b } = primaryColorInstance.toHsb(); + const { s, b } = primaryColorInstance.toHsb() || {}; if (s * 100 < primaryMinSaturation) { - text += ` 饱和度建议不低于${primaryMinSaturation}(现在 ${(s * 100).toFixed(2)})`; + text += locale.saturation((s * 100).toFixed(2)); } if (b * 100 < primaryMinBrightness) { - text += ` 亮度建议不低于${primaryMinBrightness}(现在 ${(b * 100).toFixed(2)})`; + text += locale.brightness((s * 100).toFixed(2)); } } return ( @@ -41,7 +57,7 @@ const ColorPaletteTool: React.FC = () => { return (
- {ColorPatterns({ color: primaryColor, dark: true, backgroundColor })} +
diff --git a/.dumi/theme/common/Color/ColorPatterns.tsx b/.dumi/theme/common/Color/ColorPatterns.tsx index d40d88a0c997..080b5651cea9 100644 --- a/.dumi/theme/common/Color/ColorPatterns.tsx +++ b/.dumi/theme/common/Color/ColorPatterns.tsx @@ -9,11 +9,15 @@ interface ColorPatternsProps { backgroundColor?: string; } -const ColorPatterns = ({ color, dark, backgroundColor }: ColorPatternsProps) => { +const ColorPatterns: React.FC = ({ color, dark, backgroundColor }) => { const colors = generate(color, dark ? { theme: 'dark', backgroundColor } : {}); - return uniq(colors).map((colorString, i) => ( - - )); + return ( + <> + {uniq(colors).map((colorString, i) => ( + + ))} + + ); }; export default ColorPatterns; diff --git a/.dumi/theme/common/styles/Common.tsx b/.dumi/theme/common/styles/Common.tsx index a805422a238c..6cf0de24b2a7 100644 --- a/.dumi/theme/common/styles/Common.tsx +++ b/.dumi/theme/common/styles/Common.tsx @@ -1,9 +1,13 @@ import { css, Global } from '@emotion/react'; import React from 'react'; +import { useTheme } from 'antd-style'; -export default () => ( - { + const { headerHeight, margin } = useTheme(); + + return ( + ( vertical-align: middle; border-style: none; } + + html { + scroll-padding-top: ${headerHeight + margin}px; + } `} - /> -); + /> + ); +}; diff --git a/.dumi/theme/layouts/DocLayout/index.tsx b/.dumi/theme/layouts/DocLayout/index.tsx index f5af2a0a7876..4e910e6edc9b 100644 --- a/.dumi/theme/layouts/DocLayout/index.tsx +++ b/.dumi/theme/layouts/DocLayout/index.tsx @@ -32,7 +32,7 @@ const DocLayout: React.FC = () => { const location = useLocation(); const { pathname, search, hash } = location; const [locale, lang] = useLocale(locales); - const timerRef = useRef(null); + const timerRef = useRef | null>(null); const { direction } = useContext(SiteContext); const { loading } = useSiteData(); diff --git a/.dumi/theme/layouts/GlobalLayout.tsx b/.dumi/theme/layouts/GlobalLayout.tsx index 828c69771680..2419765ff429 100644 --- a/.dumi/theme/layouts/GlobalLayout.tsx +++ b/.dumi/theme/layouts/GlobalLayout.tsx @@ -1,22 +1,24 @@ +import React, { useCallback, useEffect, useMemo } from 'react'; import { createCache, + extractStyle, legacyNotSelectorLinter, logicalPropertiesLinter, parentSelectorLinter, StyleProvider, - extractStyle, } from '@ant-design/cssinjs'; import { HappyProvider } from '@ant-design/happy-work-theme'; -import React, { useCallback, useEffect, useMemo } from 'react'; -import { createSearchParams, useOutlet, useSearchParams, useServerInsertedHTML } from 'dumi'; import { getSandpackCssText } from '@codesandbox/sandpack-react'; -import { App, theme as antdTheme } from 'antd'; +import { theme as antdTheme, App } from 'antd'; import type { DirectionType } from 'antd/es/config-provider'; +import { createSearchParams, useOutlet, useSearchParams, useServerInsertedHTML } from 'dumi'; + +import { DarkContext } from '../../hooks/useDark'; import useLayoutState from '../../hooks/useLayoutState'; -import SiteThemeProvider from '../SiteThemeProvider'; import useLocation from '../../hooks/useLocation'; import type { ThemeName } from '../common/ThemeSwitch'; import ThemeSwitch from '../common/ThemeSwitch'; +import SiteThemeProvider from '../SiteThemeProvider'; import type { SiteContextProps } from '../slots/SiteContext'; import SiteContext from '../slots/SiteContext'; @@ -144,23 +146,25 @@ const GlobalLayout: React.FC = () => { } return ( - - - - {content} - - - + + + + + {content} + + + + ); }; diff --git a/.dumi/theme/slots/Footer/index.tsx b/.dumi/theme/slots/Footer/index.tsx index f65a6a3e23b2..6b34e18ed5aa 100644 --- a/.dumi/theme/slots/Footer/index.tsx +++ b/.dumi/theme/slots/Footer/index.tsx @@ -102,7 +102,7 @@ const Footer: React.FC = () => { items: [ { title: 'Ant Design Charts', - url: 'https://charts.ant.design', + url: isZhCN ? 'https://ant-design-charts.antgroup.com' : 'https://charts.ant.design', openExternal: true, }, { @@ -117,12 +117,12 @@ const Footer: React.FC = () => { }, { title: 'Ant Design Mobile', - url: 'https://mobile.ant.design', + url: isZhCN ? 'https://ant-design-mobile.antgroup.com/zh' : 'https://mobile.ant.design', openExternal: true, }, { title: 'Ant Design Mini', - url: 'https://mini.ant.design', + url: isZhCN ? 'https://ant-design-mini.antgroup.com/' : 'https://mini.ant.design', openExternal: true, }, { @@ -338,7 +338,7 @@ const Footer: React.FC = () => { /> ), title: 'AntV', - url: 'https://antv.vision', + url: 'https://antv.antgroup.com', description: , openExternal: true, }, diff --git a/.dumi/theme/slots/Header/Navigation.tsx b/.dumi/theme/slots/Header/Navigation.tsx index 6bdb4b5012dc..597dc1fd3101 100644 --- a/.dumi/theme/slots/Header/Navigation.tsx +++ b/.dumi/theme/slots/Header/Navigation.tsx @@ -30,7 +30,8 @@ const locales = { // ============================= Style ============================= const useStyle = createStyles(({ token }) => { - const { antCls, iconCls, fontFamily, headerHeight, menuItemBorder, colorPrimary } = token; + const { antCls, iconCls, fontFamily, headerHeight, menuItemBorder, colorPrimary, colorText } = + token; return { nav: css` @@ -56,6 +57,17 @@ const useStyle = createStyles(({ token }) => { left: 12px; border-width: ${menuItemBorder}px; } + + a { + color: ${colorText}; + } + + a:before { + position: absolute; + inset: 0; + background-color: transparent; + content: ""; + } } & ${antCls}-menu-submenu-title ${iconCls} { @@ -97,7 +109,6 @@ const useStyle = createStyles(({ token }) => { export interface NavigationProps extends SharedProps { isMobile: boolean; - isClient: boolean; responsive: null | 'narrow' | 'crowded'; directionText: string; onLangChange: () => void; @@ -106,7 +117,6 @@ export interface NavigationProps extends SharedProps { export default ({ isZhCN, - isClient, isMobile, responsive, directionText, @@ -224,16 +234,21 @@ export default ({ ), key: 'docs/resources', }, - isZhCN && - isClient && - window.location.host !== 'ant-design.antgroup.com' && - window.location.host !== 'ant-design.gitee.io' + isZhCN ? { - label: '国内镜像', + label: ( + + 国内镜像 + + ), key: 'mirror', children: [ { - label: 官方镜像, + label: ( + + 官方镜像 + + ), icon: ( logoGitee 镜像, + label: ( + + Gitee 镜像 + + ), icon: ( gitee { }; }); -const SHOULD_OPEN_ANT_DESIGN_MIRROR_MODAL = 'ANT_DESIGN_DO_NOT_OPEN_MIRROR_MODAL'; - -function disableAntdMirrorModal() { - window.localStorage.setItem(SHOULD_OPEN_ANT_DESIGN_MIRROR_MODAL, 'true'); -} - -function shouldOpenAntdMirrorModal() { - return !window.localStorage.getItem(SHOULD_OPEN_ANT_DESIGN_MIRROR_MODAL); -} - interface HeaderState { menuVisible: boolean; windowWidth: number; @@ -127,7 +117,6 @@ interface HeaderState { // ================================= Header ================================= const Header: React.FC = () => { - const [isClient, setIsClient] = React.useState(false); const [, lang] = useLocale(); const { pkg } = useSiteData(); @@ -139,7 +128,7 @@ const Header: React.FC = () => { searching: false, }); const { direction, isMobile, updateSiteConfig } = useContext(SiteContext); - const pingTimer = useRef(null); + const pingTimer = useRef | null>(null); const location = useLocation(); const { pathname, search } = location; @@ -166,34 +155,8 @@ const Header: React.FC = () => { }, [location]); useEffect(() => { - setIsClient(typeof window !== 'undefined'); onWindowResize(); window.addEventListener('resize', onWindowResize); - pingTimer.current = ping((status) => { - if (status !== 'timeout' && status !== 'error') { - if ( - // process.env.NODE_ENV === 'production' && - window.location.host !== 'ant-design.antgroup.com' && - shouldOpenAntdMirrorModal() - ) { - Modal.confirm({ - title: '提示', - content: '内网用户推荐访问国内镜像以获得极速体验~', - okText: '🚀 立刻前往', - cancelText: '不再弹出', - closable: true, - zIndex: 99999, - onOk() { - window.open('https://ant-design.antgroup.com', '_self'); - disableAntdMirrorModal(); - }, - onCancel() { - disableAntdMirrorModal(); - }, - }); - } - } - }); return () => { window.removeEventListener('resize', onWindowResize); if (pingTimer.current) { @@ -273,7 +236,6 @@ const Header: React.FC = () => { const sharedProps: SharedProps = { isZhCN, isRTL, - isClient, }; const navigationNode = ( diff --git a/.dumi/theme/slots/Header/interface.ts b/.dumi/theme/slots/Header/interface.ts index 4c8824787310..8a0755c1bf0e 100644 --- a/.dumi/theme/slots/Header/interface.ts +++ b/.dumi/theme/slots/Header/interface.ts @@ -1,5 +1,4 @@ export interface SharedProps { isZhCN: boolean; isRTL: boolean; - isClient: boolean; } diff --git a/.dumirc.ts b/.dumirc.ts index f9aed1f0e5ec..b6aa833febbd 100644 --- a/.dumirc.ts +++ b/.dumirc.ts @@ -3,6 +3,7 @@ import path from 'path'; import rehypeAntd from './.dumi/rehypeAntd'; import remarkAntd from './.dumi/remarkAntd'; import { version } from './package.json'; +import * as fs from 'fs-extra'; export default defineConfig({ conventionRoutes: { @@ -44,42 +45,42 @@ export default defineConfig({ }, links: [ { - rel: 'preload', + rel: 'prefetch', as: 'font', href: '//at.alicdn.com/t/webfont_6e11e43nfj.woff2', type: 'font/woff2', crossorigin: true, }, { - rel: 'preload', + rel: 'prefetch', as: 'font', href: '//at.alicdn.com/t/webfont_6e11e43nfj.woff', type: 'font/woff', crossorigin: true, }, { - rel: 'preload', + rel: 'prefetch', as: 'font', href: '//at.alicdn.com/t/webfont_6e11e43nfj.ttf', type: 'font/ttf', crossorigin: true, }, { - rel: 'preload', + rel: 'prefetch', as: 'font', href: '//at.alicdn.com/t/webfont_exesdog9toj.woff2', type: 'font/woff2', crossorigin: true, }, { - rel: 'preload', + rel: 'prefetch', as: 'font', href: '//at.alicdn.com/t/webfont_exesdog9toj.woff', type: 'font/woff', crossorigin: true, }, { - rel: 'preload', + rel: 'prefetch', as: 'font', href: '//at.alicdn.com/t/webfont_exesdog9toj.ttf', type: 'font/ttf', @@ -158,4 +159,10 @@ export default defineConfig({ })(); `, ], + scripts: [ + { + async: true, + content: fs.readFileSync(path.join(__dirname, '.dumi', 'mirror-modal.js')).toString(), + }, + ], }); diff --git a/.eslintrc.js b/.eslintrc.js index a341d58bb440..1178d08ee021 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -205,6 +205,7 @@ module.exports = { '@typescript-eslint/no-shadow': [2, { ignoreTypeValueShadow: true }], // https://github.com/typescript-eslint/typescript-eslint/issues/2528#issuecomment-689369395 'no-undef': 0, + 'import/order': 0, }, globals: { gtag: true, diff --git a/.github/workflows/pr-contributor-welcome.yml b/.github/workflows/pr-contributor-welcome.yml new file mode 100644 index 000000000000..b8610144b645 --- /dev/null +++ b/.github/workflows/pr-contributor-welcome.yml @@ -0,0 +1,54 @@ +# 当 PR 被合并时,留言欢迎加入共建群 +name: PullRequest Contributor Welcome + +on: + pull_request_target: + types: + - closed + paths: + - 'components/**' + +jobs: + read-file: + runs-on: ubuntu-latest + outputs: + require-result: ${{ steps.contributors.outputs.content }} + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Read contributors.json + id: contributors + uses: juliangruber/read-file-action@v1 + with: + path: ./contributors.json + + output-log: + runs-on: ubuntu-latest + needs: read-file + steps: + - name: contributors.json + run: echo "${{ needs.read-file.outputs.require-result }}" + - name: creator + run: echo "${{ github.event.pull_request.user.login }}" + - name: contains + run: echo "${{ contains(fromJSON(needs.read-file.outputs.require-result), github.event.pull_request.user.login) }}" + - name: merged + run: echo "${{ github.event.pull_request.merged }}" + + check-merged: + runs-on: ubuntu-latest + needs: read-file + if: github.event.pull_request.merged == true + steps: + - uses: actions-cool/maintain-one-comment@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + body: | + 🎉 Thanks for contribution. Please feel free to join DingTalk Social Community (Provide the PR link please). + + 🎉 感谢参与贡献,欢迎扫码加入钉钉社区(进群后请提供 PR 地址)。 + + + + + body-include: '' diff --git a/.prettierrc b/.prettierrc index 9d5b9bf640c1..73e7db810cd9 100644 --- a/.prettierrc +++ b/.prettierrc @@ -4,6 +4,8 @@ "trailingComma": "all", "printWidth": 100, "proseWrap": "never", + "importOrder": ["^(react|react-dom)$", "^([a-z]|@[a-z])", "", ".*"], + "plugins": ["@ianvs/prettier-plugin-sort-imports"], "overrides": [ { "files": ".prettierrc", diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md index dc13f5c553a7..12e8f7c4c226 100644 --- a/CHANGELOG.en-US.md +++ b/CHANGELOG.en-US.md @@ -16,6 +16,23 @@ tag: vVERSION --- +## 5.8.4 + +`2023-08-18` + +- ColorPicker + - 🐞 Fix the cursor jumps when entering lowercase English letters in the ColorPicker color value input box. [#44137](https://github.com/ant-design/ant-design/pull/44137) [@gouge666](https://github.com/gouge666) + - 🐞 Fix the ColorPicker style is deformed under different sizes. [#44273](https://github.com/ant-design/ant-design/pull/44273) [@kouchao](https://github.com/kouchao) +- 🐞 Fix Descriptions throwing `key is not a prop` error message. [#44278](https://github.com/ant-design/ant-design/pull/44278) [@RedJue](https://github.com/RedJue) +- 🐞 Fix the node is still rendered when Pagination `itemRender` is customized to `null`. [#44226](https://github.com/ant-design/ant-design/pull/44226) +- 🐞 Fix Modal in Dropdown `menu.items`, rapid mouse movement when expanding Modal will make Dropdown reopen. [#44204](https://github.com/ant-design/ant-design/pull/44204) +- DatePicker + - 💄 Fix DatePicker content is not centered. [#44245](https://github.com/ant-design/ant-design/pull/44245) [@Zian502](https://github.com/Zian502) + - 💄 Optimize DatePicker selection range style. [#44206](https://github.com/ant-design/ant-design/pull/44206) [@kiner-tang](https://github.com/kiner-tang) +- 💄 Fix clicking on the Tabs area on the mobile terminal triggers a color change. [#44200](https://github.com/ant-design/ant-design/pull/44200) [@yilaikesi](https://github.com/yilaikesi) +- RTL + - 💄 Fix the numbers in the Badge are also RTL when the text direction of the page is RTL. [#43998](https://github.com/ant-design/ant-design/pull/43998) [@NotEvenANeko](https://github.com/NotEvenANeko) + ## 5.8.3 `2023-08-11` diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index 6ba8ef008e4d..9eab1234e88a 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -16,6 +16,23 @@ tag: vVERSION --- +## 5.8.4 + +`2023-08-18` + +- ColorPicker + - 🐞 修复 ColorPicker 色值输入框输入小写英文字母时光标跳动的问题。[#44137](https://github.com/ant-design/ant-design/pull/44137) [@gouge666](https://github.com/gouge666) + - 🐞 修复 ColorPicker 在不同尺寸下样式变形的问题。[#44273](https://github.com/ant-design/ant-design/pull/44273) [@kouchao](https://github.com/kouchao) +- 🐞 修复 Descriptions 抛出 `key is not a prop` 的错误提示。[#44278](https://github.com/ant-design/ant-design/pull/44278) [@RedJue](https://github.com/RedJue) +- 🐞 修复 Pagination `itemRender` 自定义为 `null` 时,仍然渲染节点的问题。[#44226](https://github.com/ant-design/ant-design/pull/44226) +- 🐞 修复 Modal 在 Dropdown `menu.items` 中,展开 Modal 时快速移动鼠标会使 Dropdown 重新打开的问题。[#44204](https://github.com/ant-design/ant-design/pull/44204) +- DatePicker + - 💄 修复 DatePicker 内容不居中问题。[#44245](https://github.com/ant-design/ant-design/pull/44245) [@Zian502](https://github.com/Zian502) + - 💄 优化 DatePicker 中范围选择区域样式。[#44206](https://github.com/ant-design/ant-design/pull/44206) [@kiner-tang](https://github.com/kiner-tang) +- 💄 修复移动端点击 Tabs 区域触发颜色改变的问题。[#44200](https://github.com/ant-design/ant-design/pull/44200) [@yilaikesi](https://github.com/yilaikesi) +- RTL + - 💄 修复了当页面的文字方向为 RTL 时 Badge 里面的数字也是 RTL 的问题。[#43998](https://github.com/ant-design/ant-design/pull/43998) [@NotEvenANeko](https://github.com/NotEvenANeko) + ## 5.8.3 `2023-08-11` diff --git a/components/_util/ActionButton.tsx b/components/_util/ActionButton.tsx index aed3cd743997..18552030c216 100644 --- a/components/_util/ActionButton.tsx +++ b/components/_util/ActionButton.tsx @@ -48,7 +48,7 @@ const ActionButton: React.FC = (props) => { }; React.useEffect(() => { - let timeoutId: NodeJS.Timer | null = null; + let timeoutId: ReturnType | null = null; if (autoFocus) { timeoutId = setTimeout(() => { buttonRef.current?.focus(); diff --git a/components/affix/index.tsx b/components/affix/index.tsx index 9f60cdf6661b..af3c9e3f54c0 100644 --- a/components/affix/index.tsx +++ b/components/affix/index.tsx @@ -1,9 +1,11 @@ 'use client'; +import React, { createRef, forwardRef, useContext } from 'react'; + import classNames from 'classnames'; import ResizeObserver from 'rc-resize-observer'; import omit from 'rc-util/lib/omit'; -import React, { createRef, forwardRef, useContext } from 'react'; + import throttleByAnimationFrame from '../_util/throttleByAnimationFrame'; import type { ConfigConsumerProps } from '../config-provider'; import { ConfigContext } from '../config-provider'; @@ -71,7 +73,7 @@ class InternalAffix extends React.Component { private fixedNodeRef = createRef(); - private timer: NodeJS.Timeout | null; + private timer: ReturnType | null; context: ConfigConsumerProps; diff --git a/components/avatar/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/avatar/__tests__/__snapshots__/demo-extend.test.ts.snap index a0f571456c6a..e59a79d34005 100644 --- a/components/avatar/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/components/avatar/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -39,16 +39,18 @@ exports[`renders components/avatar/demo/badge.tsx extend context correctly 1`] = data-show="true" title="1" > - + - 1 + + 1 + - +
@@ -531,16 +533,18 @@ Array [ data-show="true" title="1" > - + - 1 + + 1 + - +
diff --git a/components/avatar/__tests__/__snapshots__/demo.test.tsx.snap b/components/avatar/__tests__/__snapshots__/demo.test.tsx.snap index 6f87ea0a1eb5..9c10e9e3a429 100644 --- a/components/avatar/__tests__/__snapshots__/demo.test.tsx.snap +++ b/components/avatar/__tests__/__snapshots__/demo.test.tsx.snap @@ -39,16 +39,18 @@ exports[`renders components/avatar/demo/badge.tsx correctly 1`] = ` data-show="true" title="1" > - + - 1 + + 1 + - +
@@ -438,16 +440,18 @@ Array [ data-show="true" title="1" > - + - 1 + + 1 + - +
diff --git a/components/badge/ScrollNumber.tsx b/components/badge/ScrollNumber.tsx index a71762ac2c6e..79414bcde678 100644 --- a/components/badge/ScrollNumber.tsx +++ b/components/badge/ScrollNumber.tsx @@ -51,15 +51,19 @@ const ScrollNumber = React.forwardRef((props, re if (count && Number(count) % 1 === 0) { const numberList = String(count).split(''); - numberNodes = numberList.map((num, i) => ( - - )); + numberNodes = ( + + {numberList.map((num, i) => ( + + ))} + + ); } // allow specify the border diff --git a/components/badge/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/badge/__tests__/__snapshots__/demo-extend.test.ts.snap index 4d00eec5cdd2..698b1c57b2bc 100644 --- a/components/badge/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/components/badge/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -24,16 +24,18 @@ exports[`renders components/badge/demo/basic.tsx extend context correctly 1`] = data-show="true" title="5" > - + - 5 + + 5 + - + @@ -136,16 +138,18 @@ exports[`renders components/badge/demo/change.tsx extend context correctly 1`] = data-show="true" title="5" > - + - 5 + + 5 + - + @@ -204,14 +208,11 @@ exports[`renders components/badge/demo/change.tsx extend context correctly 1`] = viewBox="64 64 896 896" width="1em" > - -