Merge branch 'master' into master

This commit is contained in:
afc163 2024-09-03 23:26:51 +08:00 committed by GitHub
commit bfd62c9390
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1245 changed files with 416127 additions and 402865 deletions

View File

@ -1,5 +0,0 @@
{
"installCommand": "npm-install",
"sandboxes": ["antd-reproduction-template-forked-jyh2k9"],
"node": "18"
}

View File

@ -43,7 +43,7 @@ const useStyle = createStyles(({ token }, markPos: [number, number, number, numb
z-index: 999999;
box-shadow: 0 0 0 1px #fff;
pointer-events: none;
left: ${markPos[0] - MARK_BORDER_SIZE}px;
inset-inline-start: ${markPos[0] - MARK_BORDER_SIZE}px;
top: ${markPos[1] - MARK_BORDER_SIZE}px;
width: ${markPos[2] + MARK_BORDER_SIZE * 2}px;
height: ${markPos[3] + MARK_BORDER_SIZE * 2}px;

View File

@ -9,22 +9,22 @@ function use<T>(promise: PromiseLike<T>): T {
}
if (internal.status === 'rejected') {
throw internal.reason;
} else if (internal.status === 'pending') {
throw internal;
} else {
internal.status = 'pending';
internal.then(
(result) => {
internal.status = 'fulfilled';
internal.value = result;
},
(reason) => {
internal.status = 'rejected';
internal.reason = reason;
},
);
}
if (internal.status === 'pending') {
throw internal;
}
internal.status = 'pending';
internal.then(
(result) => {
internal.status = 'fulfilled';
internal.value = result;
},
(reason) => {
internal.status = 'rejected';
internal.reason = reason;
},
);
throw internal;
}
export default use;

View File

@ -1,4 +1,5 @@
import fetch from 'cross-fetch';
import use from '../use';
import FetchCache from './cache';

View File

@ -1,5 +1,6 @@
import { useLocation as useDumiLocation } from 'dumi';
import * as React from 'react';
import { useLocation as useDumiLocation } from 'dumi';
import useLocale from './useLocale';
function clearPath(path: string) {

View File

@ -1,6 +1,6 @@
import React, { useMemo } from 'react';
import type { MenuProps } from 'antd';
import { Tag, version } from 'antd';
import { Space, Tag, version } from 'antd';
import { createStyles } from 'antd-style';
import classnames from 'classnames';
import { useFullSidebarData, useSidebarData } from 'dumi';
@ -22,7 +22,6 @@ const useStyle = createStyles(({ css, token }) => ({
margin-inline-end: 0;
`,
subtitle: css`
margin-inline-start: ${token.marginXS}px;
font-weight: normal;
font-size: ${token.fontSizeSM}px;
opacity: 0.8;
@ -46,10 +45,10 @@ const MenuItemLabelWithTag: React.FC<MenuItemLabelProps> = (props) => {
if (!before && !after) {
return (
<Link to={`${link}${search}`} className={classnames(className, { [styles.link]: tag })}>
<span>
{title}
<Space>
<span>{title}</span>
{subtitle && <span className={styles.subtitle}>{subtitle}</span>}
</span>
</Space>
{tag && (
<Tag
bordered={false}

View File

@ -1,7 +1,9 @@
import { HomeOutlined } from '@ant-design/icons';
import { Link, useLocation } from 'dumi';
import React, { useEffect } from 'react';
import { HomeOutlined } from '@ant-design/icons';
import { Button, Result } from 'antd';
import { useLocation } from 'dumi';
import Link from '../../theme/common/Link';
import * as utils from '../../theme/utils';
export interface NotFoundProps {

View File

@ -1,183 +0,0 @@
import * as React from 'react';
import { Button, Flex, Typography } from 'antd';
import { createStyles, css, useTheme } from 'antd-style';
import classNames from 'classnames';
import { Link, useLocation } from 'dumi';
import useLocale from '../../../hooks/useLocale';
import SiteContext from '../../../theme/slots/SiteContext';
import * as utils from '../../../theme/utils';
import GroupMaskLayer from './GroupMaskLayer';
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 = () => {
const { isMobile } = React.useContext(SiteContext);
return createStyles(({ token }) => ({
titleBase: css`
h1& {
font-family: AliPuHui, ${token.fontFamily};
}
`,
title: isMobile
? css`
h1& {
margin-bottom: ${token.margin}px;
font-weight: normal;
font-size: ${token.fontSizeHeading1 + 2}px;
line-height: ${token.lineHeightHeading2};
}
`
: css`
h1& {
margin-bottom: ${token.marginMD}px;
font-weight: 900;
font-size: 68px;
}
`,
btnWrap: css`
margin-bottom: ${token.marginXL}px;
`,
layer: css`
text-align: center;
padding-top: ${token.marginFar - 16}px;
padding-bottom: ${token.marginFarSM}px;
`,
mobileBg: css`
width: 100%;
`,
videoWrap: css`
height: 320px;
background-color: #77c6ff;
display: flex;
flex-wrap: nowrap;
justify-content: center;
`,
video: css`
height: 100%;
object-fit: contain;
`,
bg: css`
flex: auto;
background-repeat: repeat-x;
background-size: auto 100%;
`,
bg1: css`
background-image: url(https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*6d50SboraPIAAAAAAAAAAAAAARQnAQ);
background-position: 100% 0;
`,
bg2: css`
background-image: url(https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*8ILtRrQlVDMAAAAAAAAAAAAAARQnAQ);
background-position: 0 0;
margin-inline-start: -1px;
`,
logoWrap: css`
position: relative;
background-color: #fff;
`,
bgImg: css`
position: absolute;
width: 240px;
`,
}))();
};
const Banner: React.FC<React.PropsWithChildren> = ({ children }) => {
const [locale] = useLocale(locales);
const { pathname, search } = useLocation();
const token = useTheme();
const { styles } = useStyle();
const { isMobile } = React.useContext(SiteContext);
const isZhCN = utils.isZhCN(pathname);
return (
<>
{/* Banner Placeholder Motion */}
{isMobile ? (
<img
className={styles.mobileBg}
src="https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*JmlaR5oQn3MAAAAAAAAAAAAADrJ8AQ/original"
alt=""
/>
) : (
<div className={classNames(styles.videoWrap)}>
<div className={classNames(styles.bg, styles.bg1)} />
<video className={styles.video} autoPlay muted loop>
<source
type="video/webm"
src="https://mdn.alipayobjects.com/huamei_iwk9zp/afts/file/A*uYT7SZwhJnUAAAAAAAAAAAAADgCCAQ"
/>
<source
type="video/mp4"
src="https://gw.alipayobjects.com/mdn/rms_08e378/afts/file/A*XYYNQJ3NbmMAAAAAAAAAAAAAARQnAQ"
/>
</video>
<div className={classNames(styles.bg, styles.bg2)} />
</div>
)}
{/* Logo */}
<div className={styles.logoWrap}>
{/* Image Bottom Right */}
<img
className={classNames(styles.bgImg)}
style={{ right: 0, top: 240 }}
src="https://gw.alipayobjects.com/zos/bmw-prod/b3b8dc41-dce8-471f-9d81-9a0204f27d03.svg"
alt="Ant Design"
/>
<GroupMaskLayer className={styles.layer}>
{/* Image Left Top */}
<img
className={classNames(styles.bgImg)}
style={{ left: isMobile ? -120 : 0, top: 0 }}
src="https://gw.alipayobjects.com/zos/bmw-prod/49f963db-b2a8-4f15-857a-270d771a1204.svg"
alt="bg"
/>
{/* Image Right Top */}
<img
className={classNames(styles.bgImg)}
style={{ right: isMobile ? 0 : 120, top: 0 }}
src="https://gw.alipayobjects.com/zos/bmw-prod/e152223c-bcae-4913-8938-54fda9efe330.svg"
alt="bg"
/>
<Typography.Title level={1} className={classNames(styles.titleBase, styles.title)}>
Ant Design 5.0
</Typography.Title>
<Typography.Paragraph
style={{
fontSize: isMobile ? token.fontSizeHeading5 - 2 : token.fontSizeHeading5,
lineHeight: isMobile ? token.lineHeightSM : token.lineHeightHeading5,
marginBottom: token.marginMD * 2,
padding: isMobile ? `0 ${token.paddingLG + 2}px` : 0,
}}
>
<div>{locale.slogan}</div>
</Typography.Paragraph>
<Flex gap="middle" className={styles.btnWrap}>
<Link to={utils.getLocalizedPathname('/components/overview/', isZhCN, search)}>
<Button size="large" type="primary">
{locale.start}
</Button>
</Link>
<Link to={utils.getLocalizedPathname('/docs/spec/introduce/', isZhCN, search)}>
<Button size="large">{locale.designLanguage}</Button>
</Link>
</Flex>
{children}
</GroupMaskLayer>
</div>
</>
);
};
export default Banner;

View File

@ -1,6 +1,6 @@
import React, { useContext } from 'react';
import { Badge, Carousel, Skeleton, Typography } from 'antd';
import { createStyles, useTheme } from 'antd-style';
import { Badge, Carousel, Flex, Skeleton, Typography } from 'antd';
import { createStyles } from 'antd-style';
import classNames from 'classnames';
import useLocale from '../../../hooks/useLocale';
@ -57,6 +57,9 @@ const useStyle = createStyles(({ token, css, cx }) => {
}
`,
carousel,
bannerBg: css`
height: ${token.fontSize}px;
`,
};
});
@ -66,8 +69,8 @@ interface RecommendItemProps {
icons: Icon[];
className?: string;
}
const RecommendItem: React.FC<RecommendItemProps> = ({ extra, index, icons, className }) => {
const token = useTheme();
const { styles } = useStyle();
if (!extra) {
@ -87,10 +90,10 @@ const RecommendItem: React.FC<RecommendItemProps> = ({ extra, index, icons, clas
<Typography.Paragraph type="secondary" style={{ flex: 'auto' }}>
{extra.description}
</Typography.Paragraph>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Flex justify="space-between" align="center">
<Typography.Text>{extra.date}</Typography.Text>
{icon && <img src={icon.href} style={{ height: token.fontSize }} alt="banner" />}
</div>
{icon && <img src={icon.href} draggable={false} className={styles.bannerBg} alt="banner" />}
</Flex>
</a>
);

View File

@ -12,7 +12,7 @@ import {
Tour,
Typography,
} from 'antd';
import { createStyles, css, useTheme } from 'antd-style';
import { createStyles, css } from 'antd-style';
import classNames from 'classnames';
import dayjs from 'dayjs';
@ -104,6 +104,13 @@ const useStyle = () => {
justify-content: center;
`,
carousel,
componentsList: css`
width: 100%;
overflow: hidden;
`,
mobileComponentsList: css`
margin: 0 ${token.margin}px;
`,
};
})();
};
@ -119,7 +126,7 @@ const ComponentItem: React.FC<ComponentItemProps> = ({ title, node, type, index
{/* Decorator */}
<div
className={styles.cardCircle}
style={{ right: (index % 2) * -20 - 20, bottom: (index % 3) * -40 - 20 }}
style={{ insetInlineEnd: (index % 2) * -20 - 20, bottom: (index % 3) * -40 - 20 }}
/>
{/* Title */}
@ -142,7 +149,6 @@ interface ComponentItemProps {
}
const ComponentsList: React.FC = () => {
const token = useTheme();
const { styles } = useStyle();
const [locale] = useLocale(locales);
const { isMobile } = useContext(SiteContext);
@ -260,21 +266,33 @@ const ComponentsList: React.FC = () => {
);
return isMobile ? (
<div style={{ margin: '0 16px' }}>
<div className={styles.mobileComponentsList}>
<Carousel className={styles.carousel}>
{COMPONENTS.map<React.ReactNode>(({ title, node, type }, index) => (
<ComponentItem title={title} node={node} type={type} index={index} key={index} />
<ComponentItem
title={title}
node={node}
type={type}
index={index}
key={`mobile-item-${index}`}
/>
))}
</Carousel>
</div>
) : (
<div style={{ width: '100%', overflow: 'hidden', display: 'flex', justifyContent: 'center' }}>
<div style={{ display: 'flex', alignItems: 'stretch', columnGap: token.paddingLG }}>
<Flex justify="center" className={styles.componentsList}>
<Flex align="stretch" gap="large">
{COMPONENTS.map<React.ReactNode>(({ title, node, type }, index) => (
<ComponentItem title={title} node={node} type={type} index={index} key={index} />
<ComponentItem
title={title}
node={node}
type={type}
index={index}
key={`desktop-item-${index}`}
/>
))}
</div>
</div>
</Flex>
</Flex>
);
};

View File

@ -1,10 +1,11 @@
import React, { useContext } from 'react';
import { Col, Row, Typography } from 'antd';
import { createStyles, useTheme } from 'antd-style';
import { Link, useLocation } from 'dumi';
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';
@ -133,7 +134,7 @@ const DesignFramework: React.FC = () => {
<Col key={index} span={colSpan}>
<Link to={path}>
<div className={styles.card}>
<img alt={title} src={img} />
<img draggable={false} alt={title} src={img} />
<Typography.Title
level={4}
@ -157,7 +158,12 @@ const DesignFramework: React.FC = () => {
return (
<Col key={index} span={colSpan}>
<a className={styles.cardMini} target="_blank" href={url} rel="noreferrer">
<img alt={title} src={img} style={{ transform: `scale(${imgScale})` }} />
<img
draggable={false}
alt={title}
src={img}
style={{ transform: `scale(${imgScale})` }}
/>
<Typography.Title
level={4}

View File

@ -1,9 +1,11 @@
import React, { Suspense } from 'react';
import { Button, ConfigProvider, Flex, Typography } from 'antd';
import { ConfigProvider, Flex, Typography } from 'antd';
import { createStyles } from 'antd-style';
import { Link, useLocation } from 'dumi';
import classNames from 'classnames';
import { useLocation } from 'dumi';
import useLocale from '../../../../hooks/useLocale';
import LinkButton from '../../../../theme/common/LinkButton';
import SiteContext from '../../../../theme/slots/SiteContext';
import * as utils from '../../../../theme/utils';
import GroupMaskLayer from '../GroupMaskLayer';
@ -26,6 +28,7 @@ const locales = {
const useStyle = () => {
const { direction } = React.useContext(ConfigProvider.ConfigContext);
const { isMobile } = React.useContext(SiteContext);
const isRTL = direction === 'rtl';
return createStyles(({ token, css, cx }) => {
const textShadow = `0 0 4px ${token.colorBgContainer}`;
@ -101,11 +104,23 @@ const useStyle = () => {
btnWrap: css`
margin-bottom: ${token.marginXL}px;
`,
bgImg: css`
position: absolute;
width: 240px;
`,
bgImgTop: css`
top: 0;
inset-inline-start: ${isMobile ? '-120px' : 0};
`,
bgImgBottom: css`
bottom: 120px;
inset-inline-end: ${isMobile ? 0 : '40%'};
`,
};
})();
};
const PreviewBanner: React.FC<React.PropsWithChildren> = (props) => {
const PreviewBanner: React.FC<Readonly<React.PropsWithChildren>> = (props) => {
const { children } = props;
const [locale] = useLocale(locales);
const { styles } = useStyle();
@ -117,15 +132,17 @@ const PreviewBanner: React.FC<React.PropsWithChildren> = (props) => {
<GroupMaskLayer>
{/* Image Left Top */}
<img
style={{ position: 'absolute', left: isMobile ? -120 : 0, top: 0, width: 240 }}
src="https://gw.alipayobjects.com/zos/bmw-prod/49f963db-b2a8-4f15-857a-270d771a1204.svg"
alt="bg"
src="https://gw.alipayobjects.com/zos/bmw-prod/49f963db-b2a8-4f15-857a-270d771a1204.svg"
draggable={false}
className={classNames(styles.bgImg, styles.bgImgTop)}
/>
{/* Image Right Top */}
<img
style={{ position: 'absolute', right: isMobile ? 0 : '40%', bottom: 120, width: 240 }}
src="https://gw.alipayobjects.com/zos/bmw-prod/e152223c-bcae-4913-8938-54fda9efe330.svg"
alt="bg"
src="https://gw.alipayobjects.com/zos/bmw-prod/e152223c-bcae-4913-8938-54fda9efe330.svg"
draggable={false}
className={classNames(styles.bgImg, styles.bgImgBottom)}
/>
<div className={styles.holder}>
@ -143,14 +160,19 @@ const PreviewBanner: React.FC<React.PropsWithChildren> = (props) => {
<p>{locale.slogan}</p>
</Typography>
<Flex gap="middle" className={styles.btnWrap}>
<Link to={utils.getLocalizedPathname('/components/overview/', isZhCN, search)}>
<Button size="large" type="primary">
{locale.start}
</Button>
</Link>
<Link to={utils.getLocalizedPathname('/docs/spec/introduce/', isZhCN, search)}>
<Button size="large">{locale.designLanguage}</Button>
</Link>
<LinkButton
size="large"
type="primary"
to={utils.getLocalizedPathname('/components/overview/', isZhCN, search)}
>
{locale.start}
</LinkButton>
<LinkButton
size="large"
to={utils.getLocalizedPathname('/docs/spec/introduce/', isZhCN, search)}
>
{locale.designLanguage}
</LinkButton>
</Flex>
<div className={styles.child}>{children}</div>
</div>

View File

@ -14,7 +14,7 @@ const useStyle = createStyles(({ token }) => ({
image: css`
transition: all ${token.motionDurationSlow};
position: absolute;
left: 0;
inset-inline-start: 0;
top: 0;
height: 100%;
width: 100%;
@ -64,10 +64,11 @@ const BackgroundImage: React.FC<BackgroundImageProps> = ({ colorPrimary, isLight
<source srcSet={entity.webp} type="image/webp" />
<source srcSet={entity.url} type="image/jpeg" />
<img
draggable={false}
className={cls}
style={{ ...style, opacity: isLight ? opacity : 0 }}
src={entity.url}
alt=""
alt="bg"
/>
</picture>
);

View File

@ -1,12 +1,14 @@
import React, { useEffect, useState } from 'react';
import { ColorPicker, Flex, Input } from 'antd';
import { createStyles } from 'antd-style';
import type { Color } from 'antd/es/color-picker';
import type { ColorPickerProps, GetProp } from 'antd';
import { generateColor } from 'antd/es/color-picker/util';
import classNames from 'classnames';
import { PRESET_COLORS } from './colorUtil';
type Color = Extract<GetProp<ColorPickerProps, 'value'>, string | { cleared: any }>;
const useStyle = createStyles(({ token, css }) => ({
color: css`
width: ${token.controlHeightLG / 2}px;
@ -34,13 +36,13 @@ const useStyle = createStyles(({ token, css }) => ({
`,
}));
export interface ColorPickerProps {
export interface ThemeColorPickerProps {
id?: string;
value?: string | Color;
onChange?: (value?: Color | string) => void;
}
const DebouncedColorPicker: React.FC<React.PropsWithChildren<ColorPickerProps>> = (props) => {
const DebouncedColorPicker: React.FC<React.PropsWithChildren<ThemeColorPickerProps>> = (props) => {
const { value: color, children, onChange } = props;
const [value, setValue] = useState(color);
@ -66,7 +68,7 @@ const DebouncedColorPicker: React.FC<React.PropsWithChildren<ColorPickerProps>>
);
};
const ThemeColorPicker: React.FC<ColorPickerProps> = ({ value, onChange, id }) => {
const ThemeColorPicker: React.FC<ThemeColorPickerProps> = ({ value, onChange, id }) => {
const { styles } = useStyle();
const matchColors = React.useMemo(() => {
@ -114,6 +116,7 @@ const ThemeColorPicker: React.FC<ColorPickerProps> = ({ value, onChange, id }) =
<input
type="radio"
name={picker ? 'picker' : 'color'}
aria-label={color}
tabIndex={picker ? -1 : 0}
onClick={(e) => e.stopPropagation()}
/>

View File

@ -113,7 +113,7 @@ const MobileCarousel: React.FC<MobileCarouselProps> = (props) => {
<Carousel className={styles.carousel} afterChange={setCurrentSlider}>
{mobileImageConfigList.map((item, index) => (
<div key={index}>
<img src={item.imageSrc} className={styles.img} alt="" />
<img draggable={false} src={item.imageSrc} className={styles.img} alt="carousel" />
</div>
))}
</Carousel>

View File

@ -15,7 +15,7 @@ const RadiusPicker: React.FC<RadiusPickerProps> = ({ id, value, onChange }) => (
style={{ width: 120 }}
min={0}
formatter={(val) => `${val}px`}
parser={(str) => (str ? parseFloat(str) : (str as any))}
parser={(str) => str?.replace('px', '') as unknown as number}
id={id}
/>
<Slider

View File

@ -1,6 +1,8 @@
import type { Color } from 'antd/es/color-picker';
import type { ColorPickerProps, GetProp } from 'antd';
import { generateColor } from 'antd/es/color-picker/util';
type Color = GetProp<ColorPickerProps, 'value'>;
export const DEFAULT_COLOR = '#1677FF';
export const PINK_COLOR = '#ED4192';

View File

@ -7,10 +7,9 @@ import {
QuestionCircleOutlined,
} from '@ant-design/icons';
import { TinyColor } from '@ctrl/tinycolor';
import type { MenuProps, ThemeConfig } from 'antd';
import type { ColorPickerProps, GetProp, MenuProps, ThemeConfig } from 'antd';
import {
Breadcrumb,
Button,
Card,
ConfigProvider,
Flex,
@ -22,14 +21,13 @@ import {
Typography,
} from 'antd';
import { createStyles } from 'antd-style';
import type { Color } from 'antd/es/color-picker';
import { generateColor } from 'antd/es/color-picker/util';
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 LinkButton from '../../../../theme/common/LinkButton';
import SiteContext from '../../../../theme/slots/SiteContext';
import { getLocalizedPathname } from '../../../../theme/utils';
import Group from '../Group';
@ -42,6 +40,8 @@ import RadiusPicker from './RadiusPicker';
import type { THEME } from './ThemePicker';
import ThemePicker from './ThemePicker';
type Color = Extract<GetProp<ColorPickerProps, 'value'>, string | { cleared: any }>;
const { Header, Content, Sider } = Layout;
const TokenChecker: React.FC = () => {
@ -195,23 +195,23 @@ const useStyle = createStyles(({ token, css, cx }) => {
position: absolute;
`,
leftTopImagePos: css`
left: 0;
inset-inline-start: 0;
top: -100px;
height: 500px;
`,
rightBottomPos: css`
right: 0;
inset-inline-end: 0;
bottom: -100px;
height: 287px;
`,
leftTopImage: css`
left: 50%;
inset-inline-start: 50%;
transform: translate3d(-900px, 0, 0);
top: -100px;
height: 500px;
`,
rightBottomImage: css`
right: 50%;
inset-inline-end: 50%;
transform: translate3d(750px, 0, 0);
bottom: -100px;
height: 287px;
@ -265,7 +265,7 @@ const sideMenuItems: MenuProps['items'] = [
// ============================= Theme =============================
function getTitleColor(colorPrimary: string | Color, isLight?: boolean) {
function getTitleColor(colorPrimary: Color, isLight?: boolean) {
if (!isLight) {
return '#FFF';
}
@ -290,7 +290,7 @@ function getTitleColor(colorPrimary: string | Color, isLight?: boolean) {
interface ThemeData {
themeType: THEME;
colorPrimary: string | Color;
colorPrimary: Color;
borderRadius: number;
compact: 'default' | 'compact';
}
@ -474,7 +474,7 @@ const Theme: React.FC = () => {
filter:
closestColor === DEFAULT_COLOR ? undefined : rgbToColorMatrix(logoColor),
}}
alt=""
alt="antd logo"
/>
</div>
<h1>Ant Design 5.0</h1>
@ -518,14 +518,15 @@ const Theme: React.FC = () => {
title={locale.myTheme}
extra={
<Flex gap="small">
<Link to={getLocalizedPathname('/theme-editor', isZhCN, search)}>
<Button type="default">{locale.toDef}</Button>
</Link>
<Link
<LinkButton to={getLocalizedPathname('/theme-editor', isZhCN, search)}>
{locale.toDef}
</LinkButton>
<LinkButton
type="primary"
to={getLocalizedPathname('/docs/react/customize-theme', isZhCN, search)}
>
<Button type="primary">{locale.toUse}</Button>
</Link>
{locale.toUse}
</LinkButton>
</Flex>
}
>

View File

@ -5,8 +5,8 @@ import { createStyles, css } from 'antd-style';
import useDark from '../../hooks/useDark';
import useLocale from '../../hooks/useLocale';
import BannerRecommends from './components/BannerRecommends';
import PreviewBanner from './components/PreviewBanner';
import Group from './components/Group';
import PreviewBanner from './components/PreviewBanner';
const ComponentsList = React.lazy(() => import('./components/ComponentsList'));
const DesignFramework = React.lazy(() => import('./components/DesignFramework'));
@ -15,7 +15,7 @@ const Theme = React.lazy(() => import('./components/Theme'));
const useStyle = createStyles(() => ({
image: css`
position: absolute;
left: 0;
inset-inline-start: 0;
top: -50px;
height: 160px;
`,
@ -78,12 +78,13 @@ const Homepage: React.FC = () => {
<Group
title={locale.designTitle}
description={locale.designDesc}
background={isRootDark ? 'rgb(57, 63, 74)' : '#F5F8FF'}
background={isRootDark ? '#393F4A' : '#F5F8FF'}
decoration={
<img
draggable={false}
className={styles.image}
src="https://gw.alipayobjects.com/zos/bmw-prod/ba37a413-28e6-4be4-b1c5-01be1a0ebb1c.svg"
alt=""
alt="bg"
/>
}
>

View File

@ -1,5 +1,5 @@
import React, { Suspense, useEffect } from 'react';
import { Button, message, Skeleton } from 'antd';
import { Button, App, Skeleton } from 'antd';
import { enUS, zhCN } from 'antd-token-previewer';
import type { ThemeConfig } from 'antd/es/config-provider/context';
import { Helmet } from 'dumi';
@ -36,7 +36,7 @@ const locales = {
const ANT_DESIGN_V5_THEME_EDITOR_THEME = 'ant-design-v5-theme-editor-theme';
const CustomTheme: React.FC = () => {
const [messageApi, contextHolder] = message.useMessage();
const { message } = App.useApp();
const [locale, lang] = useLocale(locales);
const [theme, setTheme] = React.useState<ThemeConfig>({});
@ -51,7 +51,7 @@ const CustomTheme: React.FC = () => {
const handleSave = () => {
localStorage.setItem(ANT_DESIGN_V5_THEME_EDITOR_THEME, JSON.stringify(theme));
messageApi.success(locale.saveSuccessfully);
message.success(locale.saveSuccessfully);
};
return (
@ -60,7 +60,6 @@ const CustomTheme: React.FC = () => {
<title>{`${locale.title} - Ant Design`}</title>
<meta property="og:title" content={`${locale.title} - Ant Design`} />
</Helmet>
{contextHolder}
<Suspense fallback={<Skeleton style={{ margin: 24 }} />}>
<ThemeEditor
advanced

View File

@ -74,7 +74,7 @@ function rehypeAntd(): UnifiedTransformer<HastRoot> {
const code = (node.children[0] as any).value as string;
const configRegx = /^const sandpackConfig = ([\S\s]*?});/;
const [configString] = code.match(configRegx) || [];
// eslint-disable-next-line no-eval
/* biome-ignore lint/security/noGlobalEval: used in documentation */ /* eslint-disable-next-line no-eval */
const config = configString && eval(`(${configString.replace(configRegx, '$1')})`);
Object.keys(config || {}).forEach((key) => {
if (typeof config[key] === 'object') {

14
.dumi/scripts/clarity.js Normal file
View File

@ -0,0 +1,14 @@
/* eslint-disable */
// https://clarity.microsoft.com
(function (c, l, a, r, i, t, y) {
c[a] =
c[a] ||
function () {
(c[a].q = c[a].q || []).push(arguments);
};
t = l.createElement(r);
t.async = 1;
t.src = 'https://www.clarity.ms/tag/' + i;
y = l.getElementsByTagName(r)[0];
y.parentNode.insertBefore(t, y);
})(window, document, 'clarity', 'script', 'lyia7jfwui');

View File

@ -3,7 +3,8 @@
(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) &&
!window.location.host.includes('surge')
!window.location.host.includes('surge') &&
window.location.hostname !== 'localhost'
) {
const ANTD_DOT_NOT_SHOW_MIRROR_MODAL = 'ANT_DESIGN_DO_NOT_OPEN_MIRROR_MODAL';
@ -49,8 +50,8 @@
.mirror-modal-dialog {
position: fixed;
top: 120px;
left: 0;
right: 0;
inset-inline-start: 0;
inset-inline-end: 0;
margin: 0 auto;
width: 420px;
display: flex;

View File

@ -116,12 +116,21 @@ const ComponentMeta: React.FC<ComponentMetaProps> = (props) => {
return [source, source];
}, [component, source]);
const transformComponentName = (componentName: string) => {
if (componentName === 'Notifiction' || componentName === 'Message') {
return componentName.toLowerCase();
}
return componentName;
};
// ======================== Render ========================
const importList = [
<span key="import" className={styles.import}>
import
</span>,
<span key="component" className={styles.component}>{`{ ${component} }`}</span>,
<span key="component" className={styles.component}>{`{ ${transformComponentName(
component,
)} }`}</span>,
<span key="from" className={styles.from}>
from
</span>,
@ -139,7 +148,7 @@ const ComponentMeta: React.FC<ComponentMetaProps> = (props) => {
colon={false}
column={1}
style={{ marginTop: token.margin }}
labelStyle={{ paddingInlineEnd: token.padding, width: 54 }}
labelStyle={{ paddingInlineEnd: token.padding, width: 56 }}
items={
[
{

View File

@ -198,46 +198,59 @@ const Overview: React.FC = () => {
</Title>
<Row gutter={[24, 24]}>
{components.map((component) => {
let url = component.link;
/** 是否是外链 */
const isExternalLink = component.link.startsWith('http');
let url = `${component.link}`;
const isExternalLink = url.startsWith('http');
if (!isExternalLink) {
url += urlSearch;
}
return (
<Col xs={24} sm={12} lg={8} xl={6} key={component?.title}>
<Link to={url}>
<Card
onClick={() => onClickCard(url)}
styles={{
body: {
backgroundRepeat: 'no-repeat',
backgroundPosition: 'bottom right',
backgroundImage: `url(${component?.tag || ''})`,
},
}}
size="small"
className={styles.componentsOverviewCard}
title={
<div className={styles.componentsOverviewTitle}>
{component?.title} {component.subtitle}
</div>
const cardContent = (
<Card
key={component.title}
onClick={() => onClickCard(url)}
styles={{
body: {
backgroundRepeat: 'no-repeat',
backgroundPosition: 'bottom right',
backgroundImage: `url(${component.tag || ''})`,
},
}}
size="small"
className={styles.componentsOverviewCard}
title={
<div className={styles.componentsOverviewTitle}>
{component.title} {component.subtitle}
</div>
}
>
<div className={styles.componentsOverviewImg}>
<img
src={
theme.includes('dark') && component.coverDark
? component.coverDark
: component.cover
}
>
<div className={styles.componentsOverviewImg}>
<img
src={
theme.includes('dark') && component.coverDark
? component.coverDark
: component.cover
}
alt={component?.title}
/>
</div>
</Card>
</Link>
alt={component.title}
/>
</div>
</Card>
);
const linkContent = isExternalLink ? (
<a href={url} key={component.title}>
{cardContent}
</a>
) : (
<Link to={url} prefetch key={component.title}>
{cardContent}
</Link>
);
return (
<Col xs={24} sm={12} lg={8} xl={6} key={component.title}>
{linkContent}
</Col>
);
})}

View File

@ -156,7 +156,7 @@ const SubTokenTable: React.FC<SubTokenTableProps> = (props) => {
{title}
<Popover
title={null}
popupStyle={{ width: 400 }}
overlayStyle={{ width: 400 }}
content={
<Typography>
{/* <SourceCode lang="jsx">{code}</SourceCode> */}

View File

@ -51,32 +51,27 @@ const DemoWrapper: typeof DumiDemoGrid = ({ items }) => {
const demos = React.useMemo(
() =>
items.reduce(
(acc, item) => {
const { previewerProps } = item;
const { debug } = previewerProps;
if (debug && !showDebug) {
return acc;
}
return acc.concat({
...item,
previewerProps: {
...previewerProps,
expand: expandAll,
// always override debug property, because dumi will hide debug demo in production
debug: false,
/**
* antd extra marker for the original debug
* @see https://github.com/ant-design/ant-design/pull/40130#issuecomment-1380208762
*/
originDebug: debug,
},
});
},
[] as typeof items,
),
items.reduce<typeof items>((acc, item) => {
const { previewerProps } = item;
const { debug } = previewerProps;
if (debug && !showDebug) {
return acc;
}
return acc.concat({
...item,
previewerProps: {
...previewerProps,
expand: expandAll,
// always override debug property, because dumi will hide debug demo in production
debug: false,
/**
* antd extra marker for the original debug
* @see https://github.com/ant-design/ant-design/pull/40130#issuecomment-1380208762
*/
originDebug: debug,
},
});
}, []),
[expandAll, showDebug],
);

View File

@ -1,9 +1,30 @@
import * as React from 'react';
import { App } from 'antd';
import { createStyles } from 'antd-style';
import { useIntl } from 'dumi';
import { message } from 'antd';
import CopyableIcon from './CopyableIcon';
import type { ThemeType } from './index';
import type { CategoriesKeys } from './fields';
import type { ThemeType } from './IconSearch';
const useStyle = createStyles(({ token, css }) => ({
anticonsList: css`
margin: ${token.margin}px 0;
overflow: hidden;
direction: ltr;
list-style: none;
display: grid;
grid-gap: ${token.margin}px;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
padding: 0;
`,
copiedCode: css`
padding: 0 ${token.paddingXXS}px;
font-size: ${token.fontSizeSM}px;
background-color: ${token.colorBgLayout};
border-radius: ${token.borderRadiusXS}px;
`,
}));
interface CategoryProps {
title: CategoriesKeys;
@ -13,14 +34,16 @@ interface CategoryProps {
}
const Category: React.FC<CategoryProps> = (props) => {
const { message } = App.useApp();
const { icons, title, newIcons, theme } = props;
const { styles } = useStyle();
const intl = useIntl();
const [justCopied, setJustCopied] = React.useState<string | null>(null);
const copyId = React.useRef<ReturnType<typeof setTimeout> | null>(null);
const onCopied = React.useCallback((type: string, text: string) => {
message.success(
<span>
<code className="copied-code">{text}</code> copied 🎉
<code className={styles.copiedCode}>{text}</code> copied 🎉
</span>,
);
setJustCopied(type);
@ -39,7 +62,7 @@ const Category: React.FC<CategoryProps> = (props) => {
return (
<div>
<h3>{intl.formatMessage({ id: `app.docs.components.icon.category.${title}` })}</h3>
<ul className="anticons-list">
<ul className={styles.anticonsList}>
{icons.map((name) => (
<CopyableIcon
key={name}

View File

@ -1,33 +1,102 @@
import * as React from 'react';
import CopyToClipboard from 'react-copy-to-clipboard';
import classNames from 'classnames';
import React from 'react';
import * as AntdIcons from '@ant-design/icons';
import { Badge, message } from 'antd';
import type { ThemeType } from './index';
import { App, Badge } from 'antd';
import { createStyles } from 'antd-style';
import classNames from 'classnames';
import CopyToClipboard from 'react-copy-to-clipboard';
const allIcons: {
[key: string]: any;
} = AntdIcons;
import type { ThemeType } from './IconSearch';
const allIcons: { [key: PropertyKey]: any } = AntdIcons;
const useStyle = createStyles(({ token, css }) => {
const { antCls, iconCls } = token;
return {
iconItem: css`
display: inline-flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-inline-start: 0 !important;
margin-inline-end: 0 !important;
padding-inline-start: 0 !important;
padding-inline-end: 0 !important;
position: relative;
width: 200px;
height: 100px;
overflow: hidden;
color: #555;
text-align: center;
list-style: none;
background-color: inherit;
border-radius: ${token.borderRadiusSM}px;
cursor: pointer;
transition: all ${token.motionDurationSlow} ease-in-out;
${token.iconCls} {
margin: ${token.marginXS}px 0;
font-size: 36px;
transition: transform ${token.motionDurationSlow} ease-in-out;
will-change: transform;
}
&:hover {
color: ${token.colorWhite};
background-color: ${token.colorPrimary};
${iconCls} {
transform: scale(1.3);
}
${antCls}-badge {
color: ${token.colorWhite};
}
}
&.TwoTone:hover {
background-color: #8ecafe;
}
&.copied:hover {
color: rgba(255, 255, 255, 0.2);
}
&::after {
content: 'Copied!';
position: absolute;
top: 0;
inset-inline-start: 0;
width: 100%;
height: 100%;
line-height: 100px;
color: ${token.colorTextLightSolid};
text-align: center;
background-color: ${token.colorPrimary};
opacity: 0;
transition: all ${token.motionDurationSlow} cubic-bezier(0.18, 0.89, 0.32, 1.28);
}
&.copied::after {
opacity: 1;
}
`,
anticonCls: css`
display: block;
font-family: 'Lucida Console', Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
white-space: nowrap;
text-align: center;
transform: scale(0.8);
${antCls}-badge {
transition: color ${token.motionDurationSlow} ease-in-out;
}
`,
};
});
export interface CopyableIconProps {
name: string;
isNew: boolean;
theme: ThemeType;
justCopied: string | null;
onCopied: (type: string, text: string) => any;
onCopied: (type: string, text: string) => void;
}
const CopyableIcon: React.FC<CopyableIconProps> = ({
name,
isNew,
justCopied,
onCopied,
theme,
}) => {
const className = classNames({
copied: justCopied === name,
[theme]: !!theme,
});
const CopyableIcon: React.FC<CopyableIconProps> = (props) => {
const { message } = App.useApp();
const { name, isNew, justCopied, theme, onCopied } = props;
const { styles } = useStyle();
const onCopy = (text: string, result: boolean) => {
if (result) {
onCopied(name, text);
@ -37,9 +106,9 @@ const CopyableIcon: React.FC<CopyableIconProps> = ({
};
return (
<CopyToClipboard text={`<${name} />`} onCopy={onCopy}>
<li className={className}>
<li className={classNames(theme, styles.iconItem, { copied: justCopied === name })}>
{React.createElement(allIcons[name])}
<span className="anticon-class">
<span className={styles.anticonCls}>
<Badge dot={isNew}>{name}</Badge>
</span>
</li>

View File

@ -81,7 +81,7 @@ const IconSearch: React.FC = () => {
if (searchKey) {
const matchKey = searchKey
// eslint-disable-next-line prefer-regex-literals
.replace(new RegExp(`^<([a-zA-Z]*)\\s/>$`, 'gi'), (_, name) => name)
.replace(/^<([a-z]*)\s\/>$/gi, (_, name) => name)
.replace(/(Filled|Outlined|TwoTone)$/, '')
.toLowerCase();
iconList = iconList.filter((iconName) => iconName.toLowerCase().includes(matchKey));

View File

@ -12,6 +12,7 @@ export const FilledIcon: CustomIconComponent = (props) => {
'0c0-53-43-96-96-96z';
return (
<svg {...props} viewBox="0 0 1024 1024">
<title>Filled Icon</title>
<path d={path} />
</svg>
);
@ -26,6 +27,7 @@ export const OutlinedIcon: CustomIconComponent = (props) => {
' 12 12v680c0 6.6-5.4 12-12 12z';
return (
<svg {...props} viewBox="0 0 1024 1024">
<title>Outlined Icon</title>
<path d={path} />
</svg>
);
@ -39,6 +41,7 @@ export const TwoToneIcon: CustomIconComponent = (props) => {
'68 368 0 203.41-164.622 368-368 368z';
return (
<svg {...props} viewBox="0 0 1024 1024">
<title>TwoTone Icon</title>
<path d={path} />
</svg>
);

View File

@ -117,6 +117,7 @@ const ImagePreview: React.FC<React.PropsWithChildren<ImagePreviewProps>> = (prop
<div className="preview-image-title">{coverMeta.alt}</div>
<div
className="preview-image-description"
// biome-ignore lint/security/noDangerouslySetInnerHtml: it's for markdown
dangerouslySetInnerHTML={{ __html: coverMeta.description ?? '' }}
/>
</div>

View File

@ -1,6 +1,7 @@
import { PictureOutlined } from '@ant-design/icons';
import React from 'react';
import { PictureOutlined } from '@ant-design/icons';
import { Image, Tooltip, Typography } from 'antd';
import useLocale from '../../../hooks/useLocale';
const locales = {

View File

@ -3,10 +3,10 @@ import { ConfigProvider, Tabs } from 'antd';
import SourceCode from 'dumi/theme-default/builtins/SourceCode';
import type { Tab } from 'rc-tabs/lib/interface';
import BunLogo from './bun';
import NpmLogo from './npm';
import PnpmLogo from './pnpm';
import YarnLogo from './yarn';
import BunLogo from './bun';
interface InstallProps {
npm?: string;

View File

@ -31,6 +31,7 @@ const NpmIcon: React.FC<IconProps> = (props) => {
viewBox="0 0 16 16"
width="1em"
>
<title>npm icon</title>
<path d="M0 0v16h16v-16h-16zM13 13h-2v-8h-3v8h-5v-10h10v10z" />
</svg>
</span>

View File

@ -33,6 +33,7 @@ const PnpmIcon: React.FC<IconProps> = (props) => {
viewBox="0 0 24 24"
width="1em"
>
<title>pnpm icon</title>
<path d="M0 0v7.5h7.5V0zm8.25 0v7.5h7.498V0zm8.25 0v7.5H24V0zM8.25 8.25v7.5h7.498v-7.5zm8.25 0v7.5H24v-7.5zM0 16.5V24h7.5v-7.5zm8.25 0V24h7.498v-7.5zm8.25 0V24H24v-7.5z" />
</svg>
</span>

View File

@ -32,6 +32,7 @@ const YarnIcon: React.FC<IconProps> = (props) => {
viewBox="0 0 496 512"
width="1em"
>
<title>yarn icon</title>
<path d="M393.9 345.2c-39 9.3-48.4 32.1-104 47.4 0 0-2.7 4-10.4 5.8-13.4 3.3-63.9 6-68.5 6.1-12.4.1-19.9-3.2-22-8.2-6.4-15.3 9.2-22 9.2-22-8.1-5-9-9.9-9.8-8.1-2.4 5.8-3.6 20.1-10.1 26.5-8.8 8.9-25.5 5.9-35.3.8-10.8-5.7.8-19.2.8-19.2s-5.8 3.4-10.5-3.6c-6-9.3-17.1-37.3 11.5-62-1.3-10.1-4.6-53.7 40.6-85.6 0 0-20.6-22.8-12.9-43.3 5-13.4 7-13.3 8.6-13.9 5.7-2.2 11.3-4.6 15.4-9.1 20.6-22.2 46.8-18 46.8-18s12.4-37.8 23.9-30.4c3.5 2.3 16.3 30.6 16.3 30.6s13.6-7.9 15.1-5c8.2 16 9.2 46.5 5.6 65.1-6.1 30.6-21.4 47.1-27.6 57.5-1.4 2.4 16.5 10 27.8 41.3 10.4 28.6 1.1 52.7 2.8 55.3.8 1.4 13.7.8 36.4-13.2 12.8-7.9 28.1-16.9 45.4-17 16.7-.5 17.6 19.2 4.9 22.2zM496 256c0 136.9-111.1 248-248 248S0 392.9 0 256 111.1 8 248 8s248 111.1 248 248zm-79.3 75.2c-1.7-13.6-13.2-23-28-22.8-22 .3-40.5 11.7-52.8 19.2-4.8 3-8.9 5.2-12.4 6.8 3.1-44.5-22.5-73.1-28.7-79.4 7.8-11.3 18.4-27.8 23.4-53.2 4.3-21.7 3-55.5-6.9-74.5-1.6-3.1-7.4-11.2-21-7.4-9.7-20-13-22.1-15.6-23.8-1.1-.7-23.6-16.4-41.4 28-12.2.9-31.3 5.3-47.5 22.8-2 2.2-5.9 3.8-10.1 5.4h.1c-8.4 3-12.3 9.9-16.9 22.3-6.5 17.4.2 34.6 6.8 45.7-17.8 15.9-37 39.8-35.7 82.5-34 36-11.8 73-5.6 79.6-1.6 11.1 3.7 19.4 12 23.8 12.6 6.7 30.3 9.6 43.9 2.8 4.9 5.2 13.8 10.1 30 10.1 6.8 0 58-2.9 72.6-6.5 6.8-1.6 11.5-4.5 14.6-7.1 9.8-3.1 36.8-12.3 62.2-28.7 18-11.7 24.2-14.2 37.6-17.4 12.9-3.2 21-15.1 19.4-28.2z" />
</svg>
</span>

View File

@ -11,12 +11,12 @@ import LZString from 'lz-string';
import useLocation from '../../../hooks/useLocation';
import BrowserFrame from '../../common/BrowserFrame';
import ClientOnly from '../../common/ClientOnly';
import CodePenIcon from '../../common/CodePenIcon';
import CodePreview from '../../common/CodePreview';
import CodeSandboxIcon from '../../common/CodeSandboxIcon';
import EditButton from '../../common/EditButton';
import ExternalLinkIcon from '../../common/ExternalLinkIcon';
import RiddleIcon from '../../common/RiddleIcon';
import CodePenIcon from '../../icons/CodePenIcon';
import CodeSandboxIcon from '../../icons/CodeSandboxIcon';
import ExternalLinkIcon from '../../icons/ExternalLinkIcon';
import RiddleIcon from '../../icons/RiddleIcon';
import DemoContext from '../../slots/DemoContext';
import type { SiteContextProps } from '../../slots/SiteContext';
import SiteContext from '../../slots/SiteContext';
@ -308,7 +308,9 @@ ${parsedSourceCode}
.trim()
.replace(new RegExp(`#${asset.id}\\s*`, 'g'), '')
.replace('</style>', '')
.replace('<style>', '');
.replace('<style>', '')
.replace('```css', '')
.replace('```', '');
const indexJsContent = `import React from 'react';
import { createRoot } from 'react-dom/client';
@ -401,6 +403,7 @@ createRoot(document.getElementById('container')).render(<Demo />);
{description && (
<div
className="code-box-description"
// biome-ignore lint/security/noDangerouslySetInnerHtml: it's for markdown
dangerouslySetInnerHTML={{ __html: description }}
/>
)}

View File

@ -80,6 +80,7 @@ const DesignPreviewer: FC<AntdPreviewerProps> = ({ children, title, description,
{title}
</a>
{description && (
// biome-ignore lint/security/noDangerouslySetInnerHtml: description is from markdown
<div className={styles.description} dangerouslySetInnerHTML={{ __html: description }} />
)}
<div className={styles.copy}>
@ -89,10 +90,10 @@ const DesignPreviewer: FC<AntdPreviewerProps> = ({ children, title, description,
<span style={{ marginInlineStart: 8 }}>使 Kitchen </span>
</div>
) : (
<div onClick={handleCopy} className={styles.copyTip}>
<button type="button" onClick={handleCopy} className={styles.copyTip}>
<SketchOutlined />
<span style={{ marginInlineStart: 8 }}> Sketch JSON</span>
</div>
</button>
)}
</div>
<div className={styles.demo} ref={demoRef}>

View File

@ -1,40 +1,41 @@
import React from 'react';
import { ExclamationCircleOutlined } from '@ant-design/icons';
import { Col, Row, Tooltip, Card, Typography } from 'antd';
import { Card, Col, Row, Tooltip, Typography } from 'antd';
import { createStyles } from 'antd-style';
import useLocale from '../../../hooks/useLocale';
const { Paragraph } = Typography;
const useStyle = createStyles(({ token, css }) => ({
card: css`
position: relative;
position: relative;
overflow: hidden;
.${token.antCls}-card-cover {
overflow: hidden;
.ant-card-cover {
overflow: hidden;
}
img {
transition: all ${token.motionDurationSlow} ease-out;
}
&:hover img {
transform: scale(1.3);
`,
}
img {
display: block;
transition: all ${token.motionDurationSlow} ease-out;
}
&:hover img {
transform: scale(1.3);
}
`,
badge: css`
position: absolute;
top: 8px;
right: 8px;
padding: ${token.paddingXXS}px ${token.paddingXS}px;
color: #fff;
font-size: ${token.fontSizeSM}px;
line-height: 1;
background: rgba(0, 0, 0, 0.65);
border-radius: ${token.borderRadiusLG}px;
box-shadow: 0 0 2px rgba(255, 255, 255, 0.2);
display: inline-flex;
column-gap: ${token.paddingXXS}px;
`,
position: absolute;
top: 8px;
inset-inline-end: 8px;
padding: ${token.paddingXXS}px ${token.paddingXS}px;
color: #fff;
font-size: ${token.fontSizeSM}px;
line-height: 1;
background: rgba(0, 0, 0, 0.65);
border-radius: ${token.borderRadiusLG}px;
box-shadow: 0 0 2px rgba(255, 255, 255, 0.2);
display: inline-flex;
column-gap: ${token.paddingXXS}px;
`,
}));
export type Resource = {

View File

@ -6,7 +6,7 @@ import classNames from 'classnames';
const useStyles = createStyles(({ cx, token }) => {
const play = css`
position: absolute;
right: ${token.paddingLG}px;
inset-inline-end: ${token.paddingLG}px;
bottom: ${token.paddingLG}px;
font-size: 64px;
display: flex;

View File

@ -227,14 +227,14 @@ const useStyle = createStyles(({ token }) => ({
title: css`
position: absolute;
top: 20px;
left: 20px;
inset-inline-start: 20px;
font-size: ${token.fontSizeLG}px;
`,
tips: css`
display: flex;
position: absolute;
bottom: 20px;
right: 20px;
inset-inline-end: 20px;
`,
mvp: css`
margin-inline-end: ${token.marginMD}px;

View File

@ -1,7 +1,47 @@
import React from 'react';
import { createStyles } from 'antd-style';
const BrowserFrame: React.FC<React.PropsWithChildren> = ({ children }) => (
<div className="browser-mockup with-url">{children}</div>
);
const useStyle = createStyles(({ token, css }) => ({
browserMockup: css`
position: relative;
border-top: 2em solid rgba(230, 230, 230, 0.7);
border-radius: ${token.borderRadiusSM}px ${token.borderRadiusSM}px 0 0;
box-shadow: 0 0.1em 0.5em 0 rgba(0, 0, 0, 0.28);
&::before {
position: absolute;
top: -1.25em;
inset-inline-start: 1em;
display: block;
width: 0.5em;
height: 0.5em;
background-color: #f44;
border-radius: 50%;
box-shadow:
0 0 0 2px #f44,
1.5em 0 0 2px #9b3,
3em 0 0 2px #fb5;
content: '';
}
&::after {
content: '';
display: block;
position: absolute;
top: -1.6em;
inset-inline-start: 5.5em;
width: calc(100% - 6em);
height: 1.2em;
background-color: #fff;
border-radius: ${token.borderRadiusSM}px;
}
& > * {
display: block;
}
`,
}));
const BrowserFrame: React.FC<React.PropsWithChildren> = ({ children }) => {
const { styles } = useStyle();
return <div className={styles.browserMockup}>{children}</div>;
};
export default BrowserFrame;

View File

@ -40,9 +40,9 @@ const useStyle = createStyles(({ token, css }) => {
display: block;
position: absolute;
top: -5px;
left: -9px;
inset-inline-start: -9px;
bottom: -5px;
right: -9px;
inset-inline-end: -9px;
}
}
${antCls}-typography-copy:not(${antCls}-typography-copy-success) {
@ -79,6 +79,7 @@ function toReactComponent(jsonML: any[]) {
const attr = JsonML.getAttributes(node);
return (
<pre key={index} className={`language-${attr.lang}`}>
{/* biome-ignore lint/security/noDangerouslySetInnerHtml: it's for markdown */}
<code dangerouslySetInnerHTML={{ __html: attr.highlighted }} />
</pre>
);

View File

@ -1,6 +1,6 @@
import React, { useMemo } from 'react';
import { App } from 'antd';
import CopyToClipboard from 'react-copy-to-clipboard';
import { message } from 'antd';
interface ColorBlockProps {
color: string;
@ -9,6 +9,7 @@ interface ColorBlockProps {
}
const ColorBlock: React.FC<ColorBlockProps> = (props) => {
const { message } = App.useApp();
const { color, index, dark } = props;
const textStyle = useMemo<React.CSSProperties>(() => {
const colorMap = { default: ['#fff', 'unset'], dark: ['#314659', '#fff'] };

View File

@ -11,14 +11,14 @@ const primaryMinBrightness = 70; // 主色推荐最小亮度
const locales = {
cn: {
saturation: (s: string) => `饱和度建议不低于${primaryMinSaturation}现在${s}`,
brightness: (b: string) => `亮度建议不低于${primaryMinBrightness}现在${b}`,
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}`,
`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}`,
`Brightness is recommended not to be lower than ${primaryMinBrightness} (currently ${b})`,
},
};
@ -41,7 +41,7 @@ const ColorPaletteTool: React.FC = () => {
text += locale.saturation((s * 100).toFixed(2));
}
if (b * 100 < primaryMinBrightness) {
text += locale.brightness((s * 100).toFixed(2));
text += locale.brightness((b * 100).toFixed(2));
}
}
return <span className="color-palette-picker-validation">{text.trim()}</span>;

View File

@ -1,8 +1,10 @@
import { FormattedMessage } from 'dumi';
import React, { useMemo, useState } from 'react';
import { Col, ColorPicker, Row } from 'antd';
import ColorPatterns from './ColorPatterns';
import { FormattedMessage } from 'dumi';
import type { Color } from 'antd/es/color-picker';
import useLocale from '../../../hooks/useLocale';
import ColorPatterns from './ColorPatterns';
const primaryMinSaturation = 70; // 主色推荐最小饱和度
const primaryMinBrightness = 70; // 主色推荐最小亮度
@ -23,7 +25,7 @@ const locales = {
const ColorPaletteTool: React.FC = () => {
const [primaryColor, setPrimaryColor] = useState<string>('#1890ff');
const [backgroundColor, setBackgroundColor] = useState<string>('#141414');
const [primaryColorInstance, setPrimaryColorInstance] = useState<Color>(null);
const [primaryColorInstance, setPrimaryColorInstance] = useState<Color | null>(null);
const [locale] = useLocale(locales);

View File

@ -1,5 +1,6 @@
import classNames from 'classnames';
import React from 'react';
import classNames from 'classnames';
import Palette from './Palette';
const colors = [

View File

@ -1,6 +1,7 @@
import React from 'react';
import { generate } from '@ant-design/colors';
import uniq from 'lodash/uniq';
import ColorBlock from './ColorBlock';
interface ColorPatternsProps {

View File

@ -21,7 +21,7 @@ const gray: { [key: number]: string } = {
const ColorStyle: React.FC = () => {
const token = useTheme();
const makePalette = (color: string, index: number = 1): string => {
const makePalette = (color: string, index = 1): string => {
if (index <= 10) {
return `
.palette-${color}-${index} {
@ -33,7 +33,7 @@ ${makePalette(color, index + 1)}
return '';
};
const makeGrayPalette = (index: number = 1): string => {
const makeGrayPalette = (index = 1): string => {
if (index <= 13) {
return `
.palette-gray-${index} {
@ -130,17 +130,17 @@ ${makeGrayPalette(index + 1)}
.main-color {
${makePalette('blue')}
${makePalette('purple')}
${makePalette('cyan')}
${makePalette('green')}
${makePalette('magenta')}
${makePalette('red')}
${makePalette('volcano')}
${makePalette('orange')}
${makePalette('gold')}
${makePalette('yellow')}
${makePalette('lime')}
${makePalette('geekblue')}
${makeGrayPalette()}
${makePalette('cyan')}
${makePalette('green')}
${makePalette('magenta')}
${makePalette('red')}
${makePalette('volcano')}
${makePalette('orange')}
${makePalette('gold')}
${makePalette('yellow')}
${makePalette('lime')}
${makePalette('geekblue')}
${makeGrayPalette()}
text-align: left;
@ -176,7 +176,7 @@ ${makeGrayPalette(index + 1)}
&-item &-value {
position: relative;
left: 3px;
inset-inline-start: ${token.marginXXS}px;
float: right;
transform: scale(0.85);
transform-origin: 100% 50%;
@ -203,7 +203,7 @@ ${makeGrayPalette(index + 1)}
.main-color:hover {
.main-color-value {
left: 0;
inset-inline-start: 0;
opacity: 0.7;
}
}
@ -264,7 +264,7 @@ ${makeGrayPalette(index + 1)}
&-value {
position: absolute;
bottom: 0;
left: 0;
inset-inline-start: 0;
width: 100%;
text-align: center;
transform-origin: unset;

View File

@ -1,6 +1,6 @@
import React, { useEffect } from 'react';
import { presetDarkPalettes } from '@ant-design/colors';
import { message } from 'antd';
import { App } from 'antd';
import CopyToClipboard from 'react-copy-to-clipboard';
const rgbToHex = (rgbString: string): string => {
@ -36,9 +36,10 @@ const Palette: React.FC<PaletteProps> = (props) => {
} = props;
const [hexColors, setHexColors] = React.useState<Record<PropertyKey, string>>({});
const colorNodesRef = React.useRef<Record<PropertyKey, HTMLDivElement>>({});
const { message } = App.useApp();
useEffect(() => {
const colors = {};
const colors: Record<string, string> = {};
Object.keys(colorNodesRef.current || {}).forEach((key) => {
const computedColor = getComputedStyle(colorNodesRef.current[key])['background-color'];
if (computedColor.includes('rgba')) {
@ -70,7 +71,9 @@ const Palette: React.FC<PaletteProps> = (props) => {
<div
key={i}
ref={(node) => {
colorNodesRef.current[`${name}-${i}`] = node;
if (node) {
colorNodesRef.current[`${name}-${i}`] = node;
}
}}
className={`main-color-item palette-${name}-${i}`}
style={{

View File

@ -1,4 +1,4 @@
/* eslint-disable global-require */
/* eslint-disable global-require, import/no-unresolved */
import React from 'react';
import { BugOutlined, HistoryOutlined } from '@ant-design/icons';
import { Button, Drawer, Grid, Popover, Timeline, Typography } from 'antd';
@ -28,17 +28,7 @@ function matchDeprecated(v: string): MatchDeprecatedResult {
}
const useStyle = createStyles(({ token, css }) => ({
history: css`
position: absolute;
top: 0;
inset-inline-end: 0;
`,
li: css`
// white-space: pre;
`,
ref: css`
linkRef: css`
margin-inline-start: ${token.marginXS}px;
`,
bug: css`
@ -67,6 +57,16 @@ const useStyle = createStyles(({ token, css }) => ({
}
}
`,
extraLink: css`
font-size: ${token.fontSize}px;
`,
drawerContent: {
position: 'relative',
[`> ${token.antCls}-drawer-body`]: {
scrollbarWidth: 'thin',
scrollbarColor: 'unset',
},
},
}));
export interface ComponentChangelogProps {
@ -75,7 +75,7 @@ export interface ComponentChangelogProps {
const locales = {
cn: {
full: '完整更新日志',
full: '查看完整日志',
changelog: '更新日志',
loading: '加载中...',
empty: '暂无更新',
@ -127,7 +127,7 @@ const ParseChangelog: React.FC<{ changelog: string; refs: string[]; styles: any
<span>{parsedChangelog}</span>
{/* Refs */}
{refs?.map((ref) => (
<a className={styles.ref} key={ref} href={ref} target="_blank" rel="noreferrer">
<a className={styles.linkRef} key={ref} href={ref} target="_blank" rel="noreferrer">
#{ref.match(/^.*\/(\d+)$/)?.[1]}
</a>
))}
@ -142,9 +142,11 @@ interface ChangelogInfo {
}
const useChangelog = (componentPath: string, lang: 'cn' | 'en'): ChangelogInfo[] => {
const logFileName = `components-changelog-${lang}.json`;
const data = useFetch({
key: `component-changelog-${lang}`,
request: () => import(`../../../preset/components-changelog-${lang}.json`),
request: () => import(`../../../preset/${logFileName}`),
});
return React.useMemo(() => {
const component = componentPath.replace(/-/g, '');
@ -211,7 +213,7 @@ const ComponentChangelog: React.FC<ComponentChangelogProps> = (props) => {
</Typography.Title>
<ul>
{changelogList.map<React.ReactNode>((info, index) => (
<li key={index} className={styles.li}>
<li key={index}>
<ParseChangelog {...info} styles={styles} />
</li>
))}
@ -231,28 +233,21 @@ const ComponentChangelog: React.FC<ComponentChangelogProps> = (props) => {
return (
<>
<Button
className={styles.history}
icon={<HistoryOutlined />}
onClick={() => {
setShow(true);
}}
>
<Button icon={<HistoryOutlined />} onClick={() => setShow(true)}>
{locale.changelog}
</Button>
<Drawer
destroyOnClose
className={styles.drawerContent}
title={locale.changelog}
extra={
<Link style={{ fontSize: 14 }} to={`/changelog${lang === 'cn' ? '-cn' : ''}`}>
<Link className={styles.extraLink} to={`/changelog${lang === 'cn' ? '-cn' : ''}`}>
{locale.full}
</Link>
}
open={show}
width={width}
onClose={() => {
setShow(false);
}}
destroyOnClose
onClose={() => setShow(false)}
>
<Timeline items={timelineItems} />
</Drawer>

View File

@ -1,4 +1,5 @@
import React from 'react';
import type { ComponentChangelogProps } from './ComponentChangelog';
import ComponentChangelog from './ComponentChangelog';

View File

@ -2,13 +2,10 @@ import React from 'react';
import ColorStyle from './Color/ColorStyle';
import {
BrowserMockup,
Common,
Demo,
HeadingAnchor,
Highlight,
Icon,
IconPickSearcher,
Markdown,
NProgress,
PreviewImage,
@ -25,9 +22,6 @@ const GlobalStyles: React.FC = () => (
<Markdown />
<Highlight />
<Demo />
<Icon />
<IconPickSearcher />
<BrowserMockup />
<Responsive />
<NProgress />
<PreviewImage />

View File

@ -1,6 +1,6 @@
import type { MouseEvent, MouseEventHandler } from 'react';
import React, { forwardRef, useLayoutEffect, useTransition } from 'react';
import { useLocation, useNavigate } from 'dumi';
import { Link as DumiLink, useLocation, useNavigate } from 'dumi';
import nprogress from 'nprogress';
export interface LinkProps {
@ -8,10 +8,13 @@ export interface LinkProps {
style?: React.CSSProperties;
className?: string;
onClick?: MouseEventHandler;
component?: React.ComponentType<any>;
}
nprogress.configure({ showSpinner: false });
const Link = forwardRef<HTMLAnchorElement, React.PropsWithChildren<LinkProps>>((props, ref) => {
const { to, children, ...rest } = props;
const { to, children, component, ...rest } = props;
const [isPending, startTransition] = useTransition();
const navigate = useNavigate();
const { pathname } = useLocation();
@ -46,10 +49,23 @@ const Link = forwardRef<HTMLAnchorElement, React.PropsWithChildren<LinkProps>>((
}
}, [isPending]);
if (component) {
return React.createElement(
component,
{
...rest,
ref,
onClick: handleClick,
href,
},
children,
);
}
return (
<a ref={ref} onClick={handleClick} {...rest} href={href}>
<DumiLink ref={ref} onClick={handleClick} {...rest} to={href} prefetch>
{children}
</a>
</DumiLink>
);
});

View File

@ -0,0 +1,12 @@
import React from 'react';
import { Button } from 'antd';
import type { ButtonProps } from 'antd';
import Link from './Link';
import type { LinkProps } from './Link';
type LinkButtonProps = LinkProps &
Readonly<React.PropsWithChildren<Pick<ButtonProps, 'type' | 'size'>>>;
const LinkButton: React.FC<LinkButtonProps> = (props) => <Link component={Button} {...props} />;
export default LinkButton;

View File

@ -1,24 +1,23 @@
import type { ComponentProps, FC } from 'react';
import React, { useEffect, useState } from 'react';
import { EditFilled } from '@ant-design/icons';
import { Tooltip } from 'antd';
import React from 'react';
import { createStyles } from 'antd-style';
import SourceCodeEditor from 'dumi/theme-default/slots/SourceCodeEditor';
import useLocale from '../../hooks/useLocale';
import LiveError from '../slots/LiveError';
const useStyle = createStyles(({ token, css }) => {
const { colorBgContainer, colorIcon } = token;
const { colorBgContainer } = token;
return {
editor: css`
// override dumi editor styles
.dumi-default-source-code-editor {
.dumi-default-source-code {
background: ${colorBgContainer};
&-scroll-container {
scrollbar-width: thin;
scrollbar-color: unset;
}
}
.dumi-default-source-code > pre,
.dumi-default-source-code-scroll-content > pre,
.dumi-default-source-code-editor-textarea {
@ -48,70 +47,24 @@ const useStyle = createStyles(({ token, css }) => {
}
}
`,
editableIcon: css`
position: absolute;
z-index: 2;
height: 32px;
width: 32px;
display: flex;
align-items: center;
justify-content: center;
top: 16px;
inset-inline-end: 56px;
color: ${colorIcon};
`,
};
});
const locales = {
cn: {
demoEditable: '编辑 Demo 可实时预览',
},
en: {
demoEditable: 'Edit demo with real-time preview',
},
};
const HIDE_LIVE_DEMO_TIP = 'hide-live-demo-tip';
const LiveCode: FC<
{
error: Error | null;
} & Pick<ComponentProps<typeof SourceCodeEditor>, 'lang' | 'initialValue' | 'onChange'>
> = (props) => {
const [open, setOpen] = useState(false);
const { styles } = useStyle();
const [locale] = useLocale(locales);
useEffect(() => {
const shouldOpen = !localStorage.getItem(HIDE_LIVE_DEMO_TIP);
if (shouldOpen) {
setOpen(true);
}
}, []);
const handleOpenChange = (newOpen: boolean) => {
setOpen(newOpen);
if (!newOpen) {
localStorage.setItem(HIDE_LIVE_DEMO_TIP, 'true');
}
};
return (
<>
<div className={styles.editor}>
<SourceCodeEditor
lang={props.lang}
initialValue={props.initialValue}
onChange={props.onChange}
/>
<LiveError error={props.error} />
</div>
<Tooltip title={locale.demoEditable} open={open} onOpenChange={handleOpenChange}>
<EditFilled className={styles.editableIcon} />
</Tooltip>
</>
<div className={styles.editor}>
<SourceCodeEditor
lang={props.lang}
initialValue={props.initialValue}
onChange={props.onChange}
/>
<LiveError error={props.error} />
</div>
);
};

View File

@ -1,15 +1,16 @@
import type { ReactElement } from 'react';
import React, { useContext, useMemo } from 'react';
import { LeftOutlined, RightOutlined } from '@ant-design/icons';
import type { MenuProps } from 'antd';
import type { GetProp, MenuProps } from 'antd';
import { createStyles } from 'antd-style';
import type { MenuItemType } from 'antd/es/menu/hooks/useItems';
import classNames from 'classnames';
import useMenu from '../../hooks/useMenu';
import SiteContext from '../slots/SiteContext';
import type { SiteContextProps } from '../slots/SiteContext';
type MenuItemType = Extract<GetProp<MenuProps, 'items'>[number], { type?: 'item' }>;
const useStyle = createStyles(({ token, css }) => {
const { colorSplit, iconCls, fontSizeIcon } = token;

View File

@ -5,6 +5,7 @@ const ThemeIcon: React.FC<{ className?: string }> = (props) => {
const SVGIcon = React.useCallback(
() => (
<svg width={20} height={20} viewBox="0 0 24 24" fill="currentColor" {...props}>
<title>Theme icon</title>
<g fillRule="evenodd">
<g fillRule="nonzero">
<path d="M7.02 3.635l12.518 12.518a1.863 1.863 0 010 2.635l-1.317 1.318a1.863 1.863 0 01-2.635 0L3.068 7.588A2.795 2.795 0 117.02 3.635zm2.09 14.428a.932.932 0 110 1.864.932.932 0 010-1.864zm-.043-9.747L7.75 9.635l9.154 9.153 1.318-1.317-9.154-9.155zM3.52 12.473c.514 0 .931.417.931.931v.932h.932a.932.932 0 110 1.864h-.932v.931a.932.932 0 01-1.863 0l-.001-.931h-.93a.932.932 0 010-1.864h.93v-.932c0-.514.418-.931.933-.931zm15.374-3.727a1.398 1.398 0 110 2.795 1.398 1.398 0 010-2.795zM4.385 4.953a.932.932 0 000 1.317l2.046 2.047L7.75 7 5.703 4.953a.932.932 0 00-1.318 0zM14.701.36a.932.932 0 01.931.932v.931h.932a.932.932 0 010 1.864h-.933l.001.932a.932.932 0 11-1.863 0l-.001-.932h-.93a.932.932 0 110-1.864h.93v-.931a.932.932 0 01.933-.932z" />

View File

@ -4,10 +4,11 @@ import { FloatButton } from 'antd';
import { useTheme } from 'antd-style';
import { CompactTheme, DarkTheme } from 'antd-token-previewer/es/icons';
// import { Motion } from 'antd-token-previewer/es/icons';
import { FormattedMessage, Link, useLocation } from 'dumi';
import { FormattedMessage, useLocation } from 'dumi';
import useThemeAnimation from '../../../hooks/useThemeAnimation';
import { getLocalizedPathname, isZhCN } from '../../utils';
import Link from '../Link';
import ThemeIcon from './ThemeIcon';
export type ThemeName = 'light' | 'dark' | 'compact' | 'motion-off' | 'happy-work';
@ -34,7 +35,6 @@ const ThemeSwitch: React.FC<ThemeSwitchProps> = (props) => {
icon={<ThemeIcon />}
aria-label="Theme Switcher"
badge={{ dot: true }}
style={{ zIndex: 1010 }}
>
<Link
to={getLocalizedPathname('/theme-editor', isZhCN(pathname), search)}

View File

@ -1,68 +0,0 @@
import React from 'react';
import { css, Global } from '@emotion/react';
import { useTheme } from 'antd-style';
export default () => {
const token = useTheme();
return (
<Global
styles={css`
/* Browser mockup code
* Contribute: https://gist.github.com/jarthod/8719db9fef8deb937f4f
* Live example: https://updown.io
*/
.browser-mockup {
position: relative;
border-top: 2em solid rgba(230, 230, 230, 0.7);
border-radius: ${token.borderRadiusSM}px ${token.borderRadiusSM}px 0 0;
box-shadow: 0 0.1em 0.5em 0 rgba(0, 0, 0, 0.28);
}
.browser-mockup::before {
position: absolute;
top: -1.25em;
left: 1em;
display: block;
width: 0.5em;
height: 0.5em;
background-color: #f44;
border-radius: 50%;
box-shadow:
0 0 0 2px #f44,
1.5em 0 0 2px #9b3,
3em 0 0 2px #fb5;
content: '';
}
.browser-mockup.with-tab::after {
position: absolute;
top: -2em;
left: 5.5em;
display: block;
width: 20%;
height: 0;
border-right: 0.8em solid transparent;
border-bottom: 2em solid white;
border-left: 0.8em solid transparent;
content: '';
}
.browser-mockup.with-url::after {
position: absolute;
top: -1.6em;
left: 5.5em;
display: block;
width: calc(100% - 6em);
height: 1.2em;
background-color: #fff;
border-radius: ${token.borderRadiusXS}px;
content: '';
}
.browser-mockup > * {
display: block;
}
`}
/>
);
};

View File

@ -108,7 +108,7 @@ const GlobalDemoStyles: React.FC = () => {
a.edit-button {
position: absolute;
top: 7px;
right: -16px;
inset-inline-end: -16px;
font-size: ${token.fontSizeSM}px;
text-decoration: none;
background: inherit;
@ -125,8 +125,8 @@ const GlobalDemoStyles: React.FC = () => {
}
${antCls}-row${antCls}-row-rtl & {
right: auto;
left: -22px;
inset-inline-end: auto;
inset-inline-start: -22px;
}
}
@ -184,7 +184,7 @@ const GlobalDemoStyles: React.FC = () => {
.code-expand-icon-hide {
position: absolute;
top: 0;
left: 0;
inset-inline-start: 0;
width: 100%;
max-width: 100%;
margin: 0;
@ -193,8 +193,8 @@ const GlobalDemoStyles: React.FC = () => {
user-select: none;
${antCls}-row-rtl & {
right: 0;
left: auto;
inset-inline-end: 0;
inset-inline-start: auto;
}
}
@ -357,9 +357,9 @@ const GlobalDemoStyles: React.FC = () => {
}
${antCls}-row-rtl {
#components-tooltip-demo-placement,
#components-popover-demo-placement,
#components-popconfirm-demo-placement {
#tooltip-demo-placement,
#popover-demo-placement,
#popconfirm-demo-placement {
.code-box-demo {
direction: ltr;
}

View File

@ -1,113 +0,0 @@
import React from 'react';
import { css, Global } from '@emotion/react';
import { useTheme } from 'antd-style';
export default () => {
const token = useTheme();
const { antCls, iconCls } = token;
return (
<Global
styles={css`
ul.anticons-list {
margin: 10px 0;
overflow: hidden;
direction: ltr;
list-style: none;
li {
position: relative;
float: left;
width: 16.66%;
height: 100px;
margin: 3px 0;
padding: 10px 0 0;
overflow: hidden;
color: #555;
text-align: center;
list-style: none;
background-color: inherit;
border-radius: ${token.borderRadiusSM}px;
cursor: pointer;
transition:
color ${token.motionDurationSlow} ease-in-out,
background-color ${token.motionDurationSlow} ease-in-out;
.rtl & {
margin: 3px 0;
padding: 10px 0 0;
}
${iconCls} {
margin: ${token.marginSM}px 0 ${token.marginXS}px;
font-size: 36px;
transition: transform ${token.motionDurationSlow} ease-in-out;
will-change: transform;
}
.anticon-class {
display: block;
font-family: 'Lucida Console', Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono',
monospace;
white-space: nowrap;
text-align: center;
transform: scale(0.83);
${antCls}-badge {
transition: color ${token.motionDurationSlow} ease-in-out;
}
}
&:hover {
color: #fff;
background-color: ${token.colorPrimary};
${iconCls} {
transform: scale(1.4);
}
${antCls}-badge {
color: #fff;
}
}
&.TwoTone:hover {
background-color: #8ecafe;
}
&.copied:hover {
color: rgba(255, 255, 255, 0.2);
}
&::after {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
color: #fff;
line-height: 110px;
text-align: center;
background: #1677ff;
opacity: 0;
transition: all ${token.motionDurationSlow} cubic-bezier(0.18, 0.89, 0.32, 1.28);
content: 'Copied!';
}
&.copied::after {
opacity: 1;
}
}
}
.copied-code {
padding: 2px 4px;
font-size: ${token.fontSizeSM}px;
background: ${token.colorBgLayout};
border-radius: ${token.borderRadiusXS}px;
}
`}
/>
);
};

View File

@ -1,71 +0,0 @@
import React from 'react';
import { css, Global } from '@emotion/react';
import { useTheme } from 'antd-style';
export default () => {
const token = useTheme();
const { iconCls } = token;
return (
<Global
styles={css`
.icon-pic-searcher {
display: inline-block;
margin: 0 ${token.marginXS}px;
.icon-pic-btn {
color: ${token.colorIcon};
cursor: pointer;
transition: all ${token.motionDurationSlow};
&:hover {
color: ${token.colorIconHover};
}
}
}
.icon-pic-preview {
width: 66px;
height: 66px;
margin-top: 10px;
padding: ${token.paddingXS}px;
text-align: center;
border: 1px solid ${token.colorBorder};
border-radius: ${token.borderRadiusSM}px;
> img {
max-width: 50px;
max-height: 50px;
}
}
.icon-pic-search-result {
min-height: 50px;
padding: 0 10px;
> .result-tip {
padding: 10px 0;
color: ${token.colorTextSecondary};
}
> table {
width: 100%;
.col-icon {
width: 80px;
padding: 10px 0;
> ${iconCls} {
font-size: ${token.fontSizeHeading2}px;
:hover {
color: ${token.colorLinkHover};
}
}
}
}
}
`}
/>
);
};

View File

@ -37,7 +37,6 @@ const GlobalStyle: React.FC = () => {
.markdown p > img {
margin: 34px auto;
box-shadow: 0 8px 20px rgba(143, 168, 191, 0.35);
max-width: 1024px;
display: block;
}
@ -111,39 +110,24 @@ const GlobalStyle: React.FC = () => {
}
}
.markdown ul > li {
margin-inline-start: ${token.marginMD}px;
.markdown ul > li,
.markdown ol > li {
padding-inline-start: ${token.paddingXXS}px;
list-style-type: circle;
.rtl & {
margin-inline-end: ${token.marginMD}px;
margin-inline-start: 0;
padding-inline-end: ${token.paddingXXS}px;
padding-inline-start: 0;
margin-inline-start: ${token.marginMD}px;
> p {
margin: 0.2em 0;
}
&:empty {
display: none;
}
}
.markdown ol > li {
margin-inline-start: ${token.marginMD}px;
padding-inline-start: ${token.paddingXXS}px;
list-style-type: decimal;
${antCls}-row-rtl & {
margin-inline-end: ${token.marginMD}px;
margin-inline-start: 0;
padding-inline-end: ${token.paddingXXS}px;
padding-inline-start: 0;
}
.markdown ul > li {
list-style-type: circle;
}
.markdown ul > li > p,
.markdown ol > li > p {
margin: 0.2em 0;
.markdown ol > li {
list-style-type: decimal;
}
.markdown code {
@ -183,6 +167,8 @@ const GlobalStyle: React.FC = () => {
background-color: ${token.siteMarkdownCodeBg};
border-radius: ${token.borderRadius}px;
> pre.prism-code {
scrollbar-width: thin;
scrollbar-color: unset;
padding: ${token.paddingSM}px ${token.paddingMD}px;
font-size: ${token.fontSize}px;
line-height: 2;
@ -197,6 +183,7 @@ const GlobalStyle: React.FC = () => {
margin: 0 ${token.marginMD}px;
color: #aaa;
font-size: 30px;
user-select: none;
}
}
@ -282,6 +269,10 @@ const GlobalStyle: React.FC = () => {
}
.markdown .dumi-default-table {
&-content {
scrollbar-width: thin;
scrollbar-color: unset;
}
table {
margin: 0;
overflow-x: auto;
@ -384,7 +375,7 @@ const GlobalStyle: React.FC = () => {
}
.grid-demo,
[id^='components-grid-demo-'] {
[id^='grid-demo-'] {
${antCls}-row > div,
.code-box-demo ${antCls}-row > div {
min-height: 30px;
@ -462,8 +453,8 @@ const GlobalStyle: React.FC = () => {
}
}
[id='components-grid-demo-playground'],
[id='components-grid-demo-gutter'] {
[id='grid-demo-playground'],
[id='grid-demo-gutter'] {
> .code-box-demo ${antCls}-row > div {
margin-top: 0;
margin-bottom: 0;

View File

@ -1,5 +1,5 @@
import { css, Global } from '@emotion/react';
import React from 'react';
import { css, Global } from '@emotion/react';
import { useTheme } from 'antd-style';
export default () => {
@ -13,7 +13,9 @@ export default () => {
}
.peg {
box-shadow: 0 0 10px ${token.colorPrimary}, 0 0 5px ${token.colorPrimary};
box-shadow:
0 0 10px ${token.colorPrimary},
0 0 5px ${token.colorPrimary};
}
.spinner-icon {

View File

@ -70,14 +70,14 @@ export default () => {
+ svg {
position: absolute;
top: 0;
left: 0;
inset-inline-start: 0;
}
}
.preview-image-wrapper.good::after {
position: absolute;
bottom: 0;
left: 0;
inset-inline-start: 0;
display: block;
width: 100%;
height: 3px;
@ -88,7 +88,7 @@ export default () => {
.preview-image-wrapper.bad::after {
position: absolute;
bottom: 0;
left: 0;
inset-inline-start: 0;
display: block;
width: 100%;
height: 3px;

View File

@ -1,5 +1,5 @@
import { css, Global } from '@emotion/react';
import React from 'react';
import { css, Global } from '@emotion/react';
import { useTheme } from 'antd-style';
export default () => {
@ -11,7 +11,8 @@ export default () => {
@font-face {
font-weight: normal;
font-family: AlibabaPuHuiTi;
src: url('//at.alicdn.com/t/webfont_6e11e43nfj.woff2') format('woff2'),
src:
url('//at.alicdn.com/t/webfont_6e11e43nfj.woff2') format('woff2'),
url('//at.alicdn.com/t/webfont_6e11e43nfj.woff') format('woff'),
/* chrome、firefox */ url('//at.alicdn.com/t/webfont_6e11e43nfj.ttf') format('truetype'); /* chrome、firefox、opera、Safari, Android, iOS 4.2+ */
font-display: swap;
@ -20,7 +21,8 @@ export default () => {
@font-face {
font-weight: bold;
font-family: AlibabaPuHuiTi;
src: url('//at.alicdn.com/t/webfont_exesdog9toj.woff2') format('woff2'),
src:
url('//at.alicdn.com/t/webfont_exesdog9toj.woff2') format('woff2'),
url('//at.alicdn.com/t/webfont_exesdog9toj.woff') format('woff'),
/* chrome、firefox */ url('//at.alicdn.com/t/webfont_exesdog9toj.ttf')
format('truetype'); /* chrome、firefox、opera、Safari, Android, iOS 4.2+ */
@ -32,7 +34,8 @@ export default () => {
@font-face {
font-weight: 900;
font-family: 'AliPuHui';
src: url('//at.alicdn.com/wf/webfont/exMpJIukiCms/Gsw2PSKrftc1yNWMNlXgw.woff2')
src:
url('//at.alicdn.com/wf/webfont/exMpJIukiCms/Gsw2PSKrftc1yNWMNlXgw.woff2')
format('woff2'),
url('//at.alicdn.com/wf/webfont/exMpJIukiCms/vtu73by4O2gEBcvBuLgeu.woff') format('woff');
font-display: swap;

View File

@ -11,7 +11,7 @@ export default () => {
.nav-phone-icon {
position: absolute;
bottom: 17px;
right: 30px;
inset-inline-end: 30px;
z-index: 1;
display: none;
width: 16px;
@ -98,8 +98,8 @@ export default () => {
.drawer {
.ant-menu-inline .ant-menu-item::after,
.ant-menu-vertical .ant-menu-item::after {
right: auto;
left: 0;
inset-inline-end: auto;
inset-inline-start: 0;
}
}

View File

@ -4,9 +4,6 @@ export { default as Common } from './Common';
export { default as Markdown } from './Markdown';
export { default as Highlight } from './Highlight';
export { default as Demo } from './Demo';
export { default as Icon } from './Icon';
export { default as IconPickSearcher } from './IconPickSearcher';
export { default as BrowserMockup } from './BrowserMockup';
export { default as Responsive } from './Responsive';
export { default as NProgress } from './NProgress';
export { default as PreviewImage } from './PreviewImage';

View File

@ -3,12 +3,13 @@ import Icon from '@ant-design/icons';
const SVGIcon: React.FC = () => (
<svg viewBox="0 0 15 15" fill="currentColor">
<title>codepen icon</title>
<path d="M14.777304,4.75062256 L7.77734505,0.0839936563 C7.60939924,-0.0279665065 7.39060662,-0.0279665065 7.22266081,0.0839936563 L0.222701813,4.75062256 C0.0836082937,4.84334851 5.66973453e-05,4.99945222 4.6875e-05,5.16662013 L4.6875e-05,9.83324903 C4.6875e-05,10.0004355 0.0836088906,10.1565596 0.222701812,10.2492466 L7.22266081,14.9158755 C7.30662908,14.9718752 7.403316,14.999875 7.50000292,14.999875 C7.59668984,14.999875 7.69337678,14.9718752 7.77734505,14.9158755 L14.777304,10.2492466 C14.9163976,10.1565206 14.9999492,10.0004169 14.999959,9.83324903 L14.999959,5.16662013 C14.9999492,4.99945222 14.9163976,4.84334851 14.777304,4.75062256 Z M7.50000292,9.23237755 L4.90139316,7.4999502 L7.50000292,5.76755409 L10.0986127,7.4999502 L7.50000292,9.23237755 Z M8,4.89905919 L8,1.43423573 L13.598561,5.16665138 L10.9999824,6.89904747 L8,4.89905919 Z M7.00000586,4.89905919 L4.00002344,6.89904747 L1.40141366,5.16665138 L7.00000586,1.43423573 L7.00000586,4.89905919 Z M3.09865372,7.4999502 L1.00004102,8.89903575 L1.00004102,6.10089589 L3.09865372,7.4999502 Z M4.00002344,8.10085292 L7.00000586,10.1008412 L7.00000586,13.5656334 L1.40141366,9.83328028 L4.00002344,8.10085292 Z M8,10.1008412 L10.9999824,8.10085292 L13.5985922,9.83328028 L8,13.5656647 L8,10.1008412 L8,10.1008412 Z M11.9013521,7.4999502 L13.9999648,6.10089589 L13.9999648,8.899067 L11.9013521,7.4999502 Z" />
</svg>
);
const CodePenIcon: React.FC<{ className?: string }> = (props) => (
<Icon component={SVGIcon} {...props} />
);
const CodePenIcon = React.forwardRef<HTMLSpanElement, { className?: string }>((props, ref) => (
<Icon component={SVGIcon} ref={ref} {...props} />
));
export default CodePenIcon;

View File

@ -3,12 +3,13 @@ import Icon from '@ant-design/icons';
const SVGIcon: React.FC = () => (
<svg viewBox="0 0 1024 1024" fill="currentColor">
<title>CodeSandbox Icon</title>
<path d="M755 140.3l0.5-0.3h0.3L512 0 268.3 140h-0.3l0.8 0.4L68.6 256v512L512 1024l443.4-256V256L755 140.3z m-30 506.4v171.2L548 920.1V534.7L883.4 341v215.7l-158.4 90z m-584.4-90.6V340.8L476 534.4v385.7L300 818.5V646.7l-159.4-90.6zM511.7 280l171.1-98.3 166.3 96-336.9 194.5-337-194.6 165.7-95.7L511.7 280z" />
</svg>
);
const CodeSandboxIcon: React.FC<{ className?: string }> = (props) => (
<Icon component={SVGIcon} {...props} />
);
const CodeSandboxIcon = React.forwardRef<HTMLSpanElement, { className?: string }>((props, ref) => (
<Icon component={SVGIcon} ref={ref} {...props} />
));
export default CodeSandboxIcon;

View File

@ -15,12 +15,13 @@ const DirectionSvg: React.FC<DirectionIconProps> = ({ direction }) => (
fill="currentColor"
style={{ transform: `scaleX(${direction === 'ltr' ? '1' : '-1'})` }}
>
<title>Direction Icon</title>
<path d="m14.6961816 11.6470802.0841184.0726198 2 2c.2662727.2662727.2904793.682876.0726198.9764816l-.0726198.0841184-2 2c-.2929.2929-.7677.2929-1.0606 0-.2662727-.2662727-.2904793-.682876-.0726198-.9764816l.0726198-.0841184.7196-.7197h-10.6893c-.41421 0-.75-.3358-.75-.75 0-.3796833.28215688-.6934889.64823019-.7431531l.10176981-.0068469h10.6893l-.7196-.7197c-.2929-.2929-.2929-.7677 0-1.0606.2662727-.2662727.682876-.2904793.9764816-.0726198zm-8.1961616-8.6470802c.30667 0 .58246.18671.69635.47146l3.00003 7.50004c.1538.3845-.0333.821-.41784.9749-.38459.1538-.82107-.0333-.9749-.4179l-.81142-2.0285h-2.98445l-.81142 2.0285c-.15383.3846-.59031.5717-.9749.4179-.38458-.1539-.57165-.5904-.41781-.9749l3-7.50004c.1139-.28475.38968-.47146.69636-.47146zm8.1961616 1.14705264.0841184.07261736 2 2c.2662727.26626364.2904793.68293223.0726198.97654222l-.0726198.08411778-2 2c-.2929.29289-.7677.29289-1.0606 0-.2662727-.26626364-.2904793-.68293223-.0726198-.97654222l.0726198-.08411778.7196-.7196675h-3.6893c-.4142 0-.75-.3357925-.75-.7500025 0-.3796925.2821653-.69348832.6482323-.74315087l.1017677-.00684663h3.6893l-.7196-.7196725c-.2929-.29289-.2929-.76777 0-1.06066.2662727-.26626364.682876-.29046942.9764816-.07261736zm-8.1961616 1.62238736-.89223 2.23056h1.78445z" />
</svg>
);
const DirectionIcon: React.FC<DirectionIconProps> = (props) => (
<Icon {...props} component={() => <DirectionSvg direction={props.direction} />} />
);
const DirectionIcon = React.forwardRef<HTMLSpanElement, DirectionIconProps>((props, ref) => (
<Icon ref={ref} component={() => <DirectionSvg direction={props.direction} />} {...props} />
));
export default DirectionIcon;

View File

@ -1,15 +1,21 @@
import React from 'react';
import Icon from '@ant-design/icons';
interface ExternalIconProps {
className?: string;
color?: string;
}
const SVGIcon: React.FC<{ color?: string }> = ({ color = 'currentColor' }) => (
<svg viewBox="0 0 1024 1024" width="1em" height="1em" fill={color}>
<title>External Link Icon</title>
<path d="M853.333 469.333A42.667 42.667 0 0 0 810.667 512v256A42.667 42.667 0 0 1 768 810.667H256A42.667 42.667 0 0 1 213.333 768V256A42.667 42.667 0 0 1 256 213.333h256A42.667 42.667 0 0 0 512 128H256a128 128 0 0 0-128 128v512a128 128 0 0 0 128 128h512a128 128 0 0 0 128-128V512a42.667 42.667 0 0 0-42.667-42.667z" />
<path d="M682.667 213.333h67.413L481.707 481.28a42.667 42.667 0 0 0 0 60.587 42.667 42.667 0 0 0 60.586 0L810.667 273.92v67.413A42.667 42.667 0 0 0 853.333 384 42.667 42.667 0 0 0 896 341.333V170.667A42.667 42.667 0 0 0 853.333 128H682.667a42.667 42.667 0 0 0 0 85.333z" />
</svg>
);
const ExternalLinkIcon: React.FC<{ className?: string }> = (props) => (
<Icon component={SVGIcon} {...props} />
);
const ExternalLinkIcon = React.forwardRef<HTMLSpanElement, ExternalIconProps>((props, ref) => (
<Icon component={() => <SVGIcon color={props.color} />} ref={ref} {...props} />
));
export default ExternalLinkIcon;

View File

@ -3,12 +3,13 @@ import Icon from '@ant-design/icons';
const SVGIcon: React.FC = () => (
<svg viewBox="0 0 14 14" fill="currentColor">
<title>Riddle logo</title>
<path d="M13.8875145,13.1234844 C13.8687399,13.0691875 13.8499977,13.0329687 13.8312555,12.9786562 L11.3687445,8.83296875 C12.9187468,8.05754687 13.9640694,6.49009375 13.9640694,4.68728125 C13.9624994,2.09095312 11.7968694,0 9.10938728,0 L3.86404855,0 C3.04217572,0 2.37028902,0.648703125 2.37028902,1.44223437 L2.37028902,1.82090625 L0.746871676,1.82090625 C0.33593526,1.82090625 0,2.14526562 0,2.54203125 L0,13.4478437 C0,13.7540937 0.242191908,13.9879375 0.559368786,13.9879375 C0.615627746,13.9879375 0.67187052,13.9698281 0.72812948,13.9517187 L13.440615,13.9517187 C13.7578081,13.9517187 14,13.7178906 14,13.4116406 C14,13.321125 13.9624994,13.2125 13.8875145,13.1234844 Z M3.49061272,8.0394375 L3.49061272,2.9206875 L8.71719306,2.9206875 C9.74375723,2.9206875 10.5843723,3.73232812 10.5843723,4.7235 C10.5843723,5.71465625 9.76249942,6.5081875 8.71719306,6.5081875 L6.53280462,6.5081875 L6.53280462,6.52629688 C6.45781965,6.52629688 6.3828185,6.5625 6.3093711,6.59870313 C6.04843699,6.74354688 5.95469364,7.08598438 6.10467977,7.33792188 L8.3078104,11.0325469 L3.4906289,11.0325469 L3.4906289,8.0394375 L3.49061272,8.0394375 Z M1.1203237,12.8881406 L1.1203237,2.9206875 L2.3703052,2.9206875 L2.3703052,11.5545313 C2.3703052,11.8607813 2.61249711,12.0946094 2.92969017,12.0946094 L2.94843237,12.0946094 C2.98593295,12.1127188 3.04219191,12.1127188 3.09843468,12.1127188 L9.16563006,12.1127188 C9.48280694,12.1127188 9.72499884,11.878875 9.72499884,11.572625 L9.72499884,11.5364219 C9.76249942,11.3915938 9.74375723,11.2482813 9.66875607,11.1215469 L7.5593526,7.58835938 L8.6984185,7.58835938 C10.3406104,7.58835938 11.6843514,6.29095313 11.6843514,4.703875 C11.6843514,3.1168125 10.3406104,1.81939063 8.6984185,1.81939063 L3.4906289,1.81939063 L3.4906289,1.44073437 C3.4906289,1.24310937 3.65937341,1.08017187 3.86406474,1.08017187 L9.09061272,1.08017187 C11.143741,1.08017187 12.8234173,2.7019375 12.8234173,4.68578125 C12.8234173,6.21853125 11.8343538,7.5340625 10.4343538,8.05603125 C10.378111,8.07414063 10.3406104,8.09223438 10.2843514,8.11034375 C10.0234173,8.25517188 9.92967399,8.597625 10.0796763,8.8495625 L12.5062405,12.8881563 L1.12030751,12.8881563 L1.1203237,12.8881406 Z" />
</svg>
);
const RiddleIcon: React.FC<{ className?: string }> = (props) => (
<Icon component={SVGIcon} {...props} />
);
const RiddleIcon = React.forwardRef<HTMLSpanElement, { className?: string }>((props, ref) => (
<Icon component={SVGIcon} ref={ref} {...props} />
));
export default RiddleIcon;

View File

@ -37,7 +37,7 @@ const DocLayout: React.FC = () => {
const location = useLocation();
const { pathname, search, hash } = location;
const [locale, lang] = useLocale(locales);
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const timerRef = useRef<ReturnType<typeof setTimeout>>();
const { direction } = useContext(SiteContext);
const { loading } = useSiteData();
@ -51,11 +51,10 @@ const DocLayout: React.FC = () => {
useEffect(() => {
const nprogressHiddenStyle = document.getElementById('nprogress-style');
if (nprogressHiddenStyle) {
timerRef.current = setTimeout(() => {
nprogressHiddenStyle.parentNode?.removeChild(nprogressHiddenStyle);
}, 0);
}
timerRef.current = setTimeout(() => {
nprogressHiddenStyle?.remove();
}, 0);
return () => clearTimeout(timerRef.current);
}, []);
// handle hash change or visit page hash from Link component, and jump after async chunk loaded

View File

@ -35,6 +35,16 @@ export const ANT_DESIGN_NOT_SHOW_BANNER = 'ANT_DESIGN_NOT_SHOW_BANNER';
// (global as any).styleCache = styleCache;
// }
// Compatible with old anchors
if (typeof window !== 'undefined') {
const hashId = location.hash.slice(1);
if (hashId.startsWith('components-')) {
if (!document.querySelector(`#${hashId}`)) {
location.hash = `#${hashId.replace(/^components-/, '')}`;
}
}
}
const getAlgorithm = (themes: ThemeName[] = []) =>
themes
.map((theme) => {
@ -153,6 +163,7 @@ const GlobalLayout: React.FC = () => {
plain: true,
types: 'style',
});
// biome-ignore lint/security/noDangerouslySetInnerHtml: only used in .dumi
return <style data-type="antd-cssinjs" dangerouslySetInnerHTML={{ __html: styleText }} />;
});
@ -166,6 +177,7 @@ const GlobalLayout: React.FC = () => {
data-type="antd-css-var"
data-rc-order="prepend"
data-rc-priority="-9999"
// biome-ignore lint/security/noDangerouslySetInnerHtml: only used in .dumi
dangerouslySetInnerHTML={{ __html: styleText }}
/>
);
@ -175,6 +187,7 @@ const GlobalLayout: React.FC = () => {
<style
data-sandpack="true"
id="sandpack"
// biome-ignore lint/security/noDangerouslySetInnerHtml: only used in .dumi
dangerouslySetInnerHTML={{ __html: getSandpackCssText() }}
/>
));

View File

@ -6,7 +6,7 @@ import throttle from 'lodash/throttle';
import scrollTo from '../../../../components/_util/scrollTo';
const listenerEvents = ['scroll', 'resize'] as const;
const listenerEvents: (keyof WindowEventMap)[] = ['scroll', 'resize'];
const useStyle = createStyles(({ token, css }) => {
const { boxShadowSecondary, antCls } = token;
@ -15,8 +15,8 @@ const useStyle = createStyles(({ token, css }) => {
affixTabs: css`
position: fixed;
top: 0;
right: 0;
left: 0;
inset-inline-end: 0;
inset-inline-start: 0;
z-index: 1001;
padding: 0 40px;
background: #fff;

View File

@ -10,7 +10,7 @@ import EditButton from '../../common/EditButton';
import Footer from '../../slots/Footer';
import AffixTabs from './AffixTabs';
export type ResourceLayoutProps = PropsWithChildren<{}>;
export type ResourceLayoutProps = PropsWithChildren<NonNullable<any>>;
const resourcePadding = 40;
const articleMaxWidth = 1208;

View File

@ -39,6 +39,13 @@ export const getHash = (str: string, length = 8) =>
class AntdReactTechStack extends ReactTechStack {
// eslint-disable-next-line class-methods-use-this
generatePreviewerProps(...[props, opts]: any) {
props.pkgDependencyList = { ...devDependencies, ...dependencies };
props.jsx ??= '';
if (opts.type === 'code-block') {
props.jsx = opts?.entryPointCode ? sylvanas.parseText(opts.entryPointCode) : '';
}
if (opts.type === 'external') {
// try to find md file with the same name as the demo tsx file
const locale = opts.mdAbsPath.match(/index\.([a-z-]+)\.md$/i)?.[1];
@ -48,7 +55,6 @@ class AntdReactTechStack extends ReactTechStack {
const codePath = opts.fileAbsPath!.replace(/\.\w+$/, '.tsx');
const code = fs.existsSync(codePath) ? fs.readFileSync(codePath, 'utf-8') : '';
props.pkgDependencyList = { ...devDependencies, ...dependencies };
props.jsx = sylvanas.parseText(code);
if (md) {
@ -181,26 +187,6 @@ const RoutesPlugin = (api: IApi) => {
// exclude dynamic route path, to avoid deploy failed by `:id` directory
.filter((f) => !f.path.includes(':'))
.map((file) => {
let globalStyles = '';
// Debug for file content: uncomment this if need check raw out
// const tmpFileName = `_${file.path.replace(/\//g, '-')}`;
// const tmpFilePath = path.join(api.paths.absOutputPath, tmpFileName);
// fs.writeFileSync(tmpFilePath, file.content, 'utf8');
// extract all emotion style tags from body
file.content = file.content.replace(
/<style (data-emotion|data-sandpack)[\S\s]+?<\/style>/g,
(s) => {
globalStyles += s;
return '';
},
);
// insert emotion style tags to head
file.content = file.content.replace('</head>', `${globalStyles}</head>`);
// 1. 提取 antd-style 样式
const styles = extractEmotionStyle(file.content);
@ -217,30 +203,6 @@ const RoutesPlugin = (api: IApi) => {
file.content = addLinkStyle(file.content, cssFile);
});
// Insert antd style to head
const matchRegex = /<style data-type="antd-cssinjs">([\S\s]+?)<\/style>/;
const matchList = file.content.match(matchRegex) || [];
// Init to order the `@layer`
let antdStyle = '@layer global, antd;';
matchList.forEach((text) => {
file.content = file.content.replace(text, '');
antdStyle += text.replace(matchRegex, '$1');
});
const cssFile = writeCSSFile('antd', antdStyle, antdStyle);
file.content = addLinkStyle(file.content, cssFile, true);
// Insert antd cssVar to head
const cssVarMatchRegex = /<style data-type="antd-css-var"[\S\s]+?<\/style>/;
const cssVarMatchList = file.content.match(cssVarMatchRegex) || [];
cssVarMatchList.forEach((text) => {
file.content = file.content.replace(text, '');
file.content = file.content.replace('<head>', `<head>${text}`);
});
return file;
}),
);

View File

@ -2,6 +2,7 @@ import React from 'react';
import { RightOutlined, YuqueOutlined, ZhihuOutlined } from '@ant-design/icons';
import { Button, Card, Divider } from 'antd';
import { createStyles } from 'antd-style';
import classNames from 'classnames';
import useLocale from '../../../hooks/useLocale';
import JuejinLogo from './JuejinLogo';
@ -27,7 +28,7 @@ const useStyle = createStyles(({ token, css }) => ({
justify-content: space-between;
align-items: center;
`,
left: css`
leftCard: css`
display: flex;
justify-content: flex-start;
align-items: center;
@ -42,6 +43,7 @@ const useStyle = createStyles(({ token, css }) => ({
color: #444;
font-size: ${token.fontSizeLG}px;
font-weight: ${token.fontWeightStrong};
user-select: none;
`,
subTitle: css`
display: flex;
@ -54,37 +56,34 @@ const useStyle = createStyles(({ token, css }) => ({
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.logo {
width: 24px;
height: 24px;
font-size: 24px;
&.zhihu-logo {
color: #056de8;
}
&.yuque-logo {
color: #00b96b;
}
&.juejin-logo {
color: #1e80ff;
}
`,
logo: css`
width: 24px;
height: 24px;
font-size: 24px;
&.zhihu-logo {
color: #056de8;
}
.arrowIcon {
color: #8a8f8d;
margin: 0 ${token.marginXS}px;
font-size: ${token.fontSizeSM}px;
&.yuque-logo {
color: #00b96b;
}
.zl-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 0;
color: #646464;
&.juejin-logo {
color: #1e80ff;
}
`,
btn: css`
display: flex;
justify-content: center;
align-items: center;
arrowIcon: css`
color: #8a8f8d;
margin: 0 ${token.marginXS}px;
font-size: ${token.fontSizeSM}px;
`,
zlBtn: css`
padding: 0;
color: #646464;
`,
discussLogo: css`
width: 16px;
height: 16px;
font-size: 16px;
`,
}));
@ -114,7 +113,18 @@ interface Props {
const ColumnCard: React.FC<Props> = ({ zhihuLink, yuqueLink, juejinLink }) => {
const [locale] = useLocale(locales);
const {
styles: { card, bigTitle, cardBody, left, title, subTitle, btn },
styles: {
card,
bigTitle,
cardBody,
leftCard,
title,
subTitle,
logo,
arrowIcon,
zlBtn,
discussLogo,
},
} = useStyle();
if (!zhihuLink && !yuqueLink && !juejinLink) {
return null;
@ -123,52 +133,54 @@ const ColumnCard: React.FC<Props> = ({ zhihuLink, yuqueLink, juejinLink }) => {
<Card className={card} bordered={false}>
<h3 className={bigTitle}>{locale.bigTitle}</h3>
{zhihuLink && (
<div className={cardBody}>
<div className={left}>
<img src={ANTD_IMG_URL} alt="antd" />
<div>
<p className={title}>Ant Design</p>
<div className={subTitle}>
<ZhihuOutlined className="logo zhihu-logo" />
<RightOutlined className="arrowIcon" />
<Button
target="_blank"
href="https://www.zhihu.com/column/c_1564262000561106944"
className="zl-btn"
type="link"
>
{locale.zhiHu}
</Button>
<>
<Divider />
<div className={cardBody}>
<div className={leftCard}>
<img draggable={false} src={ANTD_IMG_URL} alt="antd" />
<div>
<p className={title}>Ant Design</p>
<div className={subTitle}>
<ZhihuOutlined className={classNames(logo, 'zhihu-logo')} />
<RightOutlined className={arrowIcon} />
<Button
target="_blank"
href="https://www.zhihu.com/column/c_1564262000561106944"
className={zlBtn}
type="link"
>
{locale.zhiHu}
</Button>
</div>
</div>
</div>
<Button
ghost
type="primary"
icon={<ZhihuOutlined className={discussLogo} />}
target="_blank"
href={zhihuLink}
>
{locale.buttonText}
</Button>
</div>
<Button
type="primary"
className={btn}
icon={<ZhihuOutlined style={{ fontSize: 16 }} />}
ghost
target="_blank"
href={zhihuLink}
>
{locale.buttonText}
</Button>
</div>
</>
)}
{yuqueLink && (
<>
<Divider />
<div className={cardBody}>
<div className={left}>
<img src={ANTD_IMG_URL} alt="antd" />
<div className={leftCard}>
<img draggable={false} src={ANTD_IMG_URL} alt="antd" />
<div>
<p className={title}>Ant Design</p>
<div className={subTitle}>
<YuqueOutlined className="logo yuque-logo" />
<RightOutlined className="arrowIcon" />
<YuqueOutlined className={classNames(logo, 'yuque-logo')} />
<RightOutlined className={arrowIcon} />
<Button
target="_blank"
href="https://www.yuque.com/ant-design/ant-design"
className="zl-btn"
className={zlBtn}
type="link"
>
{locale.yuQue}
@ -177,10 +189,9 @@ const ColumnCard: React.FC<Props> = ({ zhihuLink, yuqueLink, juejinLink }) => {
</div>
</div>
<Button
type="primary"
className={btn}
icon={<YuqueOutlined style={{ fontSize: 16 }} />}
ghost
type="primary"
icon={<YuqueOutlined className={discussLogo} />}
target="_blank"
href={yuqueLink}
>
@ -193,17 +204,17 @@ const ColumnCard: React.FC<Props> = ({ zhihuLink, yuqueLink, juejinLink }) => {
<>
<Divider />
<div className={cardBody}>
<div className={left}>
<img src={ANTD_IMG_URL} alt="antd" />
<div className={leftCard}>
<img draggable={false} src={ANTD_IMG_URL} alt="antd" />
<div>
<p className={title}>Ant Design</p>
<div className={subTitle}>
<JuejinLogo className="logo juejin-logo" />
<RightOutlined className="arrowIcon" />
<JuejinLogo className={classNames(logo, 'juejin-logo')} />
<RightOutlined className={arrowIcon} />
<Button
target="_blank"
href="https://juejin.cn/column/7247354308258054200"
className="zl-btn"
className={zlBtn}
type="link"
>
{locale.junjin}
@ -212,10 +223,9 @@ const ColumnCard: React.FC<Props> = ({ zhihuLink, yuqueLink, juejinLink }) => {
</div>
</div>
<Button
type="primary"
className={btn}
icon={<JuejinLogo style={{ fontSize: 16, width: 16, height: 16 }} />}
ghost
type="primary"
icon={<JuejinLogo className={discussLogo} />}
target="_blank"
href={juejinLink}
>

View File

@ -5,7 +5,7 @@ import type { AnchorLinkItemProps } from 'antd/es/anchor/Anchor';
import classNames from 'classnames';
import { useRouteMeta, useTabMeta } from 'dumi';
const useStyle = createStyles(({ token, css }) => {
export const useStyle = createStyles(({ token, css }) => {
const { antCls } = token;
return {
anchorToc: css`
@ -19,13 +19,13 @@ const useStyle = createStyles(({ token, css }) => {
`,
tocWrapper: css`
position: fixed;
top: ${token.headerHeight + token.contentMarginTop - 8}px;
top: ${token.headerHeight + token.contentMarginTop - 4}px;
inset-inline-end: 0;
width: 160px;
padding: ${token.paddingXS}px;
width: 148px;
padding: 0;
border-radius: ${token.borderRadius}px;
box-sizing: border-box;
margin-inline-end: calc(16px - 100vw + 100%);
margin-inline-end: calc(8px - 100vw + 100%);
z-index: 10;
.toc-debug {
color: ${token.purple6};
@ -48,15 +48,11 @@ const useStyle = createStyles(({ token, css }) => {
}
`,
articleWrapper: css`
padding: 0 170px 32px 64px;
&.rtl {
padding: 0 64px 144px 170px;
}
padding-inline: 48px 164px;
padding-block: 0 32px;
@media only screen and (max-width: ${token.screenLG}px) {
&,
&.rtl {
& {
padding: 0 ${token.paddingLG * 2}px;
}
}

View File

@ -17,6 +17,7 @@ const JuejinLogo: React.FC<Props> = (props) => {
viewBox="0 0 36 28"
fill="none"
>
<title>Juejin logo</title>
<path
fillRule="evenodd"
clipRule="evenodd"

View File

@ -1,6 +1,5 @@
import React, { useContext, useLayoutEffect, useMemo, useState } from 'react';
import { Col, Flex, Typography } from 'antd';
import { createStyles } from 'antd-style';
import { Col, Flex, Space, Typography } from 'antd';
import classNames from 'classnames';
import { FormattedMessage, useRouteMeta } from 'dumi';
@ -11,6 +10,7 @@ import type { DemoContextProps } from '../DemoContext';
import DemoContext from '../DemoContext';
import SiteContext from '../SiteContext';
import InViewSuspense from './InViewSuspense';
import { useStyle } from './DocAnchor';
const Contributors = React.lazy(() => import('./Contributors'));
const ColumnCard = React.lazy(() => import('./ColumnCard'));
@ -21,21 +21,6 @@ const PrevAndNext = React.lazy(() => import('../../common/PrevAndNext'));
const ComponentChangelog = React.lazy(() => import('../../common/ComponentChangelog'));
const EditButton = React.lazy(() => import('../../common/EditButton'));
const useStyle = createStyles(({ token, css }) => ({
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 ${token.paddingLG * 2}px;
}
}
`,
}));
const Content: React.FC<React.PropsWithChildren> = ({ children }) => {
const meta = useRouteMeta();
const { pathname, hash } = useLocation();
@ -70,25 +55,27 @@ const Content: React.FC<React.PropsWithChildren> = ({ children }) => {
</InViewSuspense>
<article className={classNames(styles.articleWrapper, { rtl: isRTL })}>
{meta.frontmatter?.title ? (
<Typography.Title style={{ fontSize: 30, position: 'relative' }}>
<Flex gap="small">
<div>{meta.frontmatter?.title}</div>
<div>{meta.frontmatter?.subtitle}</div>
{!pathname.startsWith('/components/overview') && (
<InViewSuspense fallback={null}>
<EditButton
title={<FormattedMessage id="app.content.edit-page" />}
filename={meta.frontmatter.filename}
/>
</InViewSuspense>
)}
</Flex>
<Flex justify="space-between">
<Typography.Title style={{ fontSize: 32, position: 'relative' }}>
<Space>
<span>{meta.frontmatter?.title}</span>
<span>{meta.frontmatter?.subtitle}</span>
{!pathname.startsWith('/components/overview') && (
<InViewSuspense fallback={null}>
<EditButton
title={<FormattedMessage id="app.content.edit-page" />}
filename={meta.frontmatter.filename}
/>
</InViewSuspense>
)}
</Space>
</Typography.Title>
{pathname.startsWith('/components/') && (
<InViewSuspense fallback={null}>
<ComponentChangelog pathname={pathname} />
</InViewSuspense>
)}
</Typography.Title>
</Flex>
) : null}
<InViewSuspense fallback={null}>
<DocMeta />
@ -105,9 +92,7 @@ const Content: React.FC<React.PropsWithChildren> = ({ children }) => {
version={meta.frontmatter.tag}
/>
)}
<div style={{ minHeight: 'calc(100vh - 64px)', width: 'calc(100% - 10px)' }}>
{children}
</div>
<div style={{ minHeight: 'calc(100vh - 64px)' }}>{children}</div>
<InViewSuspense>
<ColumnCard
zhihuLink={meta.frontmatter.zhihu_url}

View File

@ -1,4 +1,5 @@
import * as React from 'react';
import { createStyles } from 'antd-style';
import { removeCSS, updateCSS } from 'rc-util/lib/Dom/dynamicCSS';
import useLocale from '../../../hooks/useLocale';
@ -19,9 +20,38 @@ const locales = {
},
};
const useStyle = createStyles(({ css, token }) => ({
container: css`
position: fixed;
inset-inline-start: 0;
inset-inline-end: 0;
top: 0;
bottom: 0;
z-index: 99999999;
background-color: ${token.colorTextSecondary};
display: flex;
justify-content: center;
align-items: center;
`,
alertBox: css`
border: 1px solid ${token.colorWarningBorder};
background-color: ${token.colorWarningBg};
color: ${token.colorTextHeading};
padding: ${token.paddingXS}px ${token.paddingSM}px;
border-radius: ${token.borderRadiusLG}px;
z-index: 9999999999;
line-height: 22px;
width: 520px;
a {
color: ${token.colorPrimary};
text-decoration-line: none;
}
`,
}));
// Check for browser support `:where` or not
// Warning user if not support to modern browser
function InfoNewVersion() {
const InfoNewVersion: React.FC = () => {
const [location] = useLocale(locales);
const [supportWhere, setSupportWhere] = React.useState(true);
@ -50,40 +80,19 @@ function InfoNewVersion() {
removeCSS(whereCls);
}, []);
return supportWhere ? null : (
<div
style={{
position: 'fixed',
left: 0,
right: 0,
top: 0,
bottom: 0,
zIndex: 99999999,
background: 'rgba(0,0,0,0.65)',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
>
<div
style={{
border: `1px solid #ffe58f`,
background: '#fffbe6',
color: 'rgba(0,0,0,0.88)',
padding: '8px 12px',
borderRadius: '8px',
zIndex: 9999999999,
lineHeight: '22px',
width: 520,
}}
>
{location.whereNotSupport}{' '}
<a style={{ color: '#1677ff', textDecoration: 'none' }} href={location.whereDocUrl}>
{location.whereDocTitle}
</a>
const { styles } = useStyle();
if (supportWhere) {
return null;
}
return (
<div className={styles.container}>
<div className={styles.alertBox}>
{location.whereNotSupport} <a href={location.whereDocUrl}>{location.whereDocTitle}</a>
</div>
</div>
);
}
};
export default InfoNewVersion;

View File

@ -202,7 +202,7 @@ const Footer: React.FC = () => {
src="https://gw.alipayobjects.com/zos/rmsportal/XuVpGqBFxXplzvLjJBZB.svg"
width={16}
height={16}
alt="yuque"
alt="yuque logo"
/>
),
title: <FormattedMessage id="app.footer.yuque.repo" />,
@ -227,7 +227,7 @@ const Footer: React.FC = () => {
src="https://gw.alipayobjects.com/zos/rmsportal/mZBWtboYbnMkTBaRIuWQ.png"
width={16}
height={16}
alt="seeconf"
alt="seeconf logo"
/>
),
title: 'SEE Conf',
@ -310,7 +310,7 @@ const Footer: React.FC = () => {
src="https://gw.alipayobjects.com/zos/rmsportal/nBVXkrFdWHxbZlmMbsaH.svg"
width={22}
height={22}
alt="Ant XTech"
alt="Ant XTech logo"
/>
),
title: <FormattedMessage id="app.footer.more-product" />,
@ -321,7 +321,7 @@ const Footer: React.FC = () => {
src="https://gw.alipayobjects.com/zos/rmsportal/XuVpGqBFxXplzvLjJBZB.svg"
width={16}
height={16}
alt="yuque"
alt="yuque logo"
/>
),
title: <FormattedMessage id="app.footer.yuque" />,
@ -335,7 +335,7 @@ const Footer: React.FC = () => {
src="https://gw.alipayobjects.com/zos/antfincdn/nc7Fc0XBg5/8a6844f5-a6ed-4630-9177-4fa5d0b7dd47.png"
width={16}
height={16}
alt="AntV"
alt="AntV logo"
/>
),
title: 'AntV',
@ -344,7 +344,7 @@ const Footer: React.FC = () => {
openExternal: true,
},
{
icon: <img src="https://www.eggjs.org/logo.svg" alt="Egg" width={16} height={16} />,
icon: <img src="https://www.eggjs.org/logo.svg" alt="Egg logo" width={16} height={16} />,
title: 'Egg',
url: 'https://eggjs.org',
description: <FormattedMessage id="app.footer.egg.slogan" />,
@ -356,7 +356,7 @@ const Footer: React.FC = () => {
src="https://gw.alipayobjects.com/zos/rmsportal/DMDOlAUhmktLyEODCMBR.ico"
width={16}
height={16}
alt="kitchen"
alt="Kitchen logo"
/>
),
title: 'Kitchen',
@ -370,7 +370,7 @@ const Footer: React.FC = () => {
src="https://mdn.alipayobjects.com/huamei_j9rjmc/afts/img/A*3ittT5OEo2gAAAAAAAAAAAAADvGmAQ/original"
width={16}
height={16}
alt="Galacean"
alt="Galacean logo"
/>
),
title: <FormattedMessage id="app.footer.galacean" />,
@ -384,7 +384,7 @@ const Footer: React.FC = () => {
src="https://gw.alipayobjects.com/zos/rmsportal/nBVXkrFdWHxbZlmMbsaH.svg"
width={16}
height={16}
alt="xtech"
alt="xtech logo"
/>
),
title: <FormattedMessage id="app.footer.xtech" />,

View File

@ -1,7 +1,8 @@
import * as React from 'react';
import { createStyles } from 'antd-style';
import { Link, useLocation } from 'dumi';
import { useLocation } from 'dumi';
import Link from '../../common/Link';
import * as utils from '../../utils';
const useStyle = createStyles(({ token, css }) => {
@ -22,15 +23,17 @@ const useStyle = createStyles(({ token, css }) => {
text-decoration: none;
display: inline-flex;
align-items: center;
column-gap: ${token.marginSM}px;
&:hover {
color: ${colorTextHeading};
}
img {
width: 32px;
height: 32px;
display: inline-block;
vertical-align: middle;
margin-inline-end: ${token.marginSM}px;
}
@media only screen and (max-width: ${mobileMaxWidth}px) {
@ -49,18 +52,15 @@ export interface LogoProps {
location: any;
}
const logoSrc = 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg';
const Logo: React.FC<LogoProps> = ({ isZhCN }) => {
const { search } = useLocation();
const { styles } = useStyle();
return (
<h1>
<Link to={utils.getLocalizedPathname('/', isZhCN, search)} className={styles.logo}>
<img
src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg"
height={32}
width={32}
alt="logo"
/>
<img src={logoSrc} draggable={false} alt="logo" />
<span className={styles.title}>Ant Design</span>
</Link>
</h1>

View File

@ -30,16 +30,7 @@ const locales = {
// ============================= Style =============================
const useStyle = createStyles(({ token }) => {
const {
antCls,
iconCls,
fontFamily,
fontSize,
headerHeight,
menuItemBorder,
colorPrimary,
colorText,
} = token;
const { antCls, iconCls, fontFamily, fontSize, headerHeight, colorPrimary } = token;
return {
nav: css`
@ -57,25 +48,6 @@ const useStyle = createStyles(({ token }) => {
padding-inline-end: ${token.paddingSM}px;
padding-inline-start: ${token.paddingSM}px;
line-height: ${headerHeight}px;
&::after {
top: 0;
right: 12px;
bottom: auto;
left: 12px;
border-width: ${menuItemBorder}px;
}
a {
color: ${colorText};
}
a:before {
position: absolute;
inset: 0;
background-color: transparent;
content: '';
}
}
& ${antCls}-menu-submenu-title ${iconCls} {
@ -124,7 +96,7 @@ const HeaderNavigation: React.FC<NavigationProps> = (props) => {
activeMenuItem = 'docs/resources';
}
let additional: MenuProps['items'];
let additional: MenuProps['items'] = [];
const additionalItems: MenuProps['items'] = [
{
@ -215,50 +187,16 @@ const HeaderNavigation: React.FC<NavigationProps> = (props) => {
},
isZhCN
? {
key: 'mirror',
label: (
<a href="https://ant-design.antgroup.com" target="_blank" rel="noreferrer">
</a>
),
key: 'mirror',
children: [
{
label: (
<a href="https://ant-design.antgroup.com" target="_blank" rel="noreferrer">
</a>
),
icon: (
<img
alt="logo"
src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg"
width={16}
style={{ verticalAlign: 'text-bottom' }}
/>
),
key: 'antgroup',
},
{
label: (
<a href="https://ant-design.gitee.io" target="_blank" rel="noreferrer">
Gitee
</a>
),
icon: (
<img
alt="gitee"
src="https://gw.alipayobjects.com/zos/bmw-prod/9e91e124-9bab-4113-b500-301412f6b370.svg"
width={16}
style={{ verticalAlign: 'text-bottom' }}
/>
),
key: 'gitee',
},
],
}
: null,
...(additional ?? []),
];
].filter(Boolean);
return (
<Menu

View File

@ -11,7 +11,7 @@ export interface LangBtnProps {
value: 1 | 2;
pure?: boolean;
onClick?: React.MouseEventHandler;
['aria-label']?: string;
'aria-label'?: string;
}
const BASE_SIZE = '1.2em';
@ -69,7 +69,7 @@ const useStyle = createStyles(({ token, css }) => {
color: ${colorText};
`,
label1Style: css`
left: -5%;
inset-inline-start: -5%;
top: 0;
z-index: 1;
background-color: ${colorText};
@ -78,7 +78,7 @@ const useStyle = createStyles(({ token, css }) => {
transform-origin: 0 0;
`,
label2Style: css`
right: -5%;
inset-inline-end: -5%;
bottom: 0;
z-index: 0;
transform: scale(0.5);
@ -95,7 +95,13 @@ const LangBtn: React.FC<LangBtnProps> = (props) => {
} = useStyle();
const node = (
<button onClick={onClick} className={btn} key="lang-button" aria-label={props['aria-label']}>
<button
type="button"
onClick={onClick}
className={btn}
key="lang-button"
aria-label={props['aria-label']}
>
<div className="btn-inner">
{pure && (value === 1 ? label1 : label2)}
{!pure && (

View File

@ -8,7 +8,7 @@ import { useLocation, useSiteData } from 'dumi';
import DumiSearchBar from 'dumi/theme-default/slots/SearchBar';
import useLocale from '../../../hooks/useLocale';
import DirectionIcon from '../../common/DirectionIcon';
import DirectionIcon from '../../icons/DirectionIcon';
import { ANT_DESIGN_NOT_SHOW_BANNER } from '../../layouts/GlobalLayout';
import * as utils from '../../utils';
import { getThemeConfig } from '../../utils';
@ -54,12 +54,11 @@ const useStyle = createStyles(({ token, css }) => {
border: none;
}
.nav-search-wrapper {
display: flex;
flex: auto;
}
.dumi-default-search-bar {
display: inline-flex;
align-items: center;
flex: auto;
margin: 0;
border-inline-start: 1px solid rgba(0, 0, 0, 0.06);
> svg {
@ -70,6 +69,7 @@ const useStyle = createStyles(({ token, css }) => {
> input {
height: 22px;
border: 0;
max-width: calc(100vw - 768px);
&:focus {
box-shadow: none;
@ -85,16 +85,22 @@ const useStyle = createStyles(({ token, css }) => {
background-color: rgba(150, 150, 150, 0.06);
border-color: rgba(100, 100, 100, 0.2);
border-radius: ${token.borderRadiusSM}px;
position: static;
top: unset;
transform: unset;
}
.dumi-default-search-popover {
inset-inline-start: 11px;
inset-inline-start: ${token.paddingSM}px;
inset-inline-end: unset;
&::before {
inset-inline-start: 100px;
inset-inline-end: unset;
}
& > section {
scrollbar-width: thin;
scrollbar-color: unset;
}
}
}
`,
@ -103,12 +109,11 @@ const useStyle = createStyles(({ token, css }) => {
align-items: center;
margin: 0;
column-gap: ${token.paddingSM}px;
padding-inline-end: ${token.padding}px;
> * {
flex: none;
margin: 0;
&:last-child {
margin-inline-end: 40px;
}
}
`,
dataDirectionIcon: css`
@ -405,11 +410,11 @@ const Header: React.FC = () => {
<Col {...colProps[0]}>
<Logo {...sharedProps} location={location} />
</Col>
<Col {...colProps[1]} className={styles.menuRow}>
<div className="nav-search-wrapper">
<Col {...colProps[1]}>
<div className={styles.menuRow}>
<DumiSearchBar />
{!isMobile && menu}
</div>
{!isMobile && menu}
</Col>
</Row>
</header>

View File

@ -53,12 +53,7 @@ const useStyle = createStyles(({ token, css }) => {
> ${antCls}-menu-item-group
> ${antCls}-menu-item-group-list
> ${antCls}-menu-item {
padding-inline-start: 40px !important;
${antCls}-row-rtl & {
padding-inline-end: 40px !important;
padding-inline-start: ${token.padding}px !important;
}
padding-inline: 36px 12px !important;
}
// Nest Category > Type > Article
@ -96,8 +91,6 @@ const useStyle = createStyles(({ token, css }) => {
`,
mainMenu: css`
z-index: 1;
.main-menu-inner {
position: sticky;
top: ${token.headerHeight + token.contentMarginTop}px;
width: 100%;
@ -106,9 +99,12 @@ const useStyle = createStyles(({ token, css }) => {
overflow: hidden;
scrollbar-width: thin;
scrollbar-color: unset;
}
&:hover .main-menu-inner {
.ant-menu {
padding: 0 4px;
}
&:hover {
overflow-y: auto;
}
`,
@ -144,7 +140,7 @@ const Sidebar: React.FC = () => {
<MobileMenu key="Mobile-menu">{menuChild}</MobileMenu>
) : (
<Col xxl={4} xl={5} lg={6} md={6} sm={24} xs={24} className={styles.mainMenu}>
<section className="main-menu-inner">{menuChild}</section>
{menuChild}
</Col>
);
};

View File

@ -1,5 +1,6 @@
import * as React from 'react';
import type { DirectionType } from 'antd/es/config-provider';
import type { ThemeName } from '../common/ThemeSwitch';
export interface SiteContextProps {

View File

@ -181,6 +181,7 @@ export function isLocalStorageNameSupported() {
storage.removeItem(testKey);
return true;
} catch (error) {
console.error('Your web browser does not support storing settings locally.', error);
return false;
}
}

View File

@ -8,14 +8,22 @@ import { version } from './package.json';
export default defineConfig({
plugins: ['dumi-plugin-color-chunk'],
manifest: {},
conventionRoutes: {
// to avoid generate routes for .dumi/pages/index/components/xx
exclude: [new RegExp('index/components/')],
exclude: [/index\/components\//],
},
ssr: process.env.NODE_ENV === 'production' ? {} : false,
ssr:
process.env.NODE_ENV === 'production'
? {
builder: 'mako',
}
: false,
hash: true,
mfsu: false,
mako: {},
crossorigin: {},
runtimePublicPath: {},
outputPath: '_site',
favicons: ['https://gw.alipayobjects.com/zos/rmsportal/rlpTLlbMzTNYuZGGCVYM.png'],
resolve: {
@ -40,11 +48,16 @@ export default defineConfig({
},
extraRehypePlugins: [rehypeAntd],
extraRemarkPlugins: [remarkAntd],
metas: [{ name: 'theme-color', content: '#1677ff' }],
metas: [
{ name: 'theme-color', content: '#1677ff' },
{ name: 'build-time', content: Date.now().toString() },
// https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables
{ name: 'build-hash', content: process.env.GITHUB_SHA ?? 'unknown' },
],
analytics: {
ga_v2: 'UA-72788897-1',
},
analyze: {
analyze: process.env.NODE_ENV === 'production' ? false : {
analyzerPort: 'auto',
},
links: [
@ -53,56 +66,56 @@ export default defineConfig({
as: 'font',
href: '//at.alicdn.com/t/webfont_6e11e43nfj.woff2',
type: 'font/woff2',
crossorigin: true,
crossorigin: 'anonymous',
},
{
rel: 'prefetch',
as: 'font',
href: '//at.alicdn.com/t/webfont_6e11e43nfj.woff',
type: 'font/woff',
crossorigin: true,
crossorigin: 'anonymous',
},
{
rel: 'prefetch',
as: 'font',
href: '//at.alicdn.com/t/webfont_6e11e43nfj.ttf',
type: 'font/ttf',
crossorigin: true,
crossorigin: 'anonymous',
},
{
rel: 'prefetch',
as: 'font',
href: '//at.alicdn.com/t/webfont_exesdog9toj.woff2',
type: 'font/woff2',
crossorigin: true,
crossorigin: 'anonymous',
},
{
rel: 'prefetch',
as: 'font',
href: '//at.alicdn.com/t/webfont_exesdog9toj.woff',
type: 'font/woff',
crossorigin: true,
crossorigin: 'anonymous',
},
{
rel: 'prefetch',
as: 'font',
href: '//at.alicdn.com/t/webfont_exesdog9toj.ttf',
type: 'font/ttf',
crossorigin: true,
crossorigin: 'anonymous',
},
{
rel: 'preload',
as: 'font',
href: '//at.alicdn.com/wf/webfont/exMpJIukiCms/Gsw2PSKrftc1yNWMNlXgw.woff2',
type: 'font/woff2',
crossorigin: true,
crossorigin: 'anonymous',
},
{
rel: 'preload',
as: 'font',
href: '//at.alicdn.com/wf/webfont/exMpJIukiCms/vtu73by4O2gEBcvBuLgeu.woff',
type: 'font/woff2',
crossorigin: true,
crossorigin: 'anonymous',
},
],
headScripts: [
@ -166,7 +179,13 @@ export default defineConfig({
scripts: [
{
async: true,
content: fs.readFileSync(path.join(__dirname, '.dumi', 'mirror-modal.js')).toString(),
content: fs
.readFileSync(path.join(__dirname, '.dumi', 'scripts', 'mirror-modal.js'))
.toString(),
},
{
async: true,
content: fs.readFileSync(path.join(__dirname, '.dumi', 'scripts', 'clarity.js')).toString(),
},
],
});

1
.github/FUNDING.yml vendored
View File

@ -2,5 +2,6 @@
github: ant-design
open_collective: ant-design
ko_fi: ant_design
issuehunt: ant-design/ant-design
buy_me_a_coffee: antdesign

View File

@ -6,58 +6,45 @@ Your pull requests will be merged after one of the collaborators approve.
Thank you!
-->
[中文版模板 / Chinese template](https://github.com/ant-design/ant-design/blob/master/.github/PULL_REQUEST_TEMPLATE/pr_cn.md?plain=1)
[中文版模板 / Chinese template](https://github.com/ant-design/ant-design/blob/master/.github/PULL_REQUEST_TEMPLATE_CN.md?plain=1)
### 🤔 This is a ...
- [ ] New feature
- [ ] Bug fix
- [ ] Site / documentation update
- [ ] Demo update
- [ ] Component style update
- [ ] TypeScript definition update
- [ ] Bundle size optimization
- [ ] Performance optimization
- [ ] Enhancement feature
- [ ] Internationalization
- [ ] Refactoring
- [ ] Code style optimization
- [ ] Test Case
- [ ] Branch merge
- [ ] Workflow
- [ ] Other (about what?)
- [ ] 🆕 New feature
- [ ] 🐞 Bug fix
- [ ] 📝 Site / documentation improvement
- [ ] 📽️ Demo improvement
- [ ] 💄 Component style improvement
- [ ] 🤖 TypeScript definition improvement
- [ ] 📦 Bundle size optimization
- [ ] ⚡️ Performance optimization
- [ ] ⭐️ Feature enhancement
- [ ] 🌐 Internationalization
- [ ] 🛠 Refactoring
- [ ] 🎨 Code style optimization
- [ ] Test Case
- [ ] 🔀 Branch merge
- [ ] Workflow
- [ ] Other (about what?)
### 🔗 Related issue link
### 🔗 Related Issues
<!--
1. Put the related issue or discussion links here.
2. close #xxxx or fix #xxxx for instance.
-->
> - Describe the source of related requirements, such as links to relevant issue discussions.
> - For example: close #xxxx, fix #xxxx
### 💡 Background and solution
### 💡 Background and Solution
<!--
1. Describe the problem and the scenario.
2. GIF or snapshot should be provided if includes UI/interactive modification.
3. How to fix the problem, and list the final API implementation and usage sample if that is a new feature.
-->
> - The specific problem to be addressed.
> - List the final API implementation and usage if needed.
> - If there are UI/interaction changes, consider providing screenshots or GIFs.
### 📝 Changelog
### 📝 Change Log
<!--
Describe changes from the user side, and list all potential break changes or other risks.
--->
> - Read [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) like a cat tracks a laser pointer.
> - Describe the impact of the changes on developers, not the solution approach.
> - Reference: https://ant.design/changelog
| Language | Changelog |
| ---------- | --------- |
| 🇺🇸 English | |
| 🇨🇳 Chinese | |
### ☑️ Self-Check before Merge
⚠️ Please check all items below before requesting a reviewing. ⚠️
- [ ] Doc is updated/provided or not needed
- [ ] Demo is updated/provided or not needed
- [ ] TypeScript definition is updated/provided or not needed
- [ ] Changelog is provided or not needed

View File

@ -1,63 +0,0 @@
<!--
首先,感谢你的贡献!😄
新特性请提交至 feature 分支,其余可提交至 master 分支。
在维护者审核通过后会合并。
请确保填写以下 pull request 的信息,谢谢!~
-->
[English Template / 英文模板](https://github.com/ant-design/ant-design/blob/master/.github/PULL_REQUEST_TEMPLATE.md?plain=1)
### 🤔 这个变动的性质是?
- [ ] 新特性提交
- [ ] 日常 bug 修复
- [ ] 站点、文档改进
- [ ] 演示代码改进
- [ ] 组件样式/交互改进
- [ ] TypeScript 定义更新
- [ ] 包体积优化
- [ ] 性能优化
- [ ] 功能增强
- [ ] 国际化改进
- [ ] 重构
- [ ] 代码风格优化
- [ ] 测试用例
- [ ] 分支合并
- [ ] 工作流程
- [ ] 其他改动(是关于什么的改动?)
### 🔗 相关 Issue
<!--
1. 描述相关需求的来源,如相关的 issue 讨论链接。
2. 例如 close #xxxx、 fix #xxxx
-->
### 💡 需求背景和解决方案
<!--
1. 要解决的具体问题。
2. 列出最终的 API 实现和用法。
3. 涉及UI/交互变动需要有截图或 GIF。
-->
### 📝 更新日志
<!--
从用户角度描述具体变化,以及可能的 breaking change 和其他风险。
-->
| 语言 | 更新描述 |
| ------- | -------- |
| 🇺🇸 英文 | |
| 🇨🇳 中文 | |
### ☑️ 请求合并前的自查清单
⚠️ 请自检并全部**勾选全部选项**。⚠️
- [ ] 文档已补充或无须补充
- [ ] 代码演示已提供或无须提供
- [ ] TypeScript 定义已补充或无须补充
- [ ] Changelog 已提供或无须提供

50
.github/PULL_REQUEST_TEMPLATE_CN.md vendored Normal file
View File

@ -0,0 +1,50 @@
<!--
首先,感谢你的贡献!😄
新特性请提交至 feature 分支,其余可提交至 master 分支。
在维护者审核通过后会合并。
请确保填写以下 pull request 的信息,谢谢!~
-->
[English Template / 英文模板](https://github.com/ant-design/ant-design/blob/master/.github/PULL_REQUEST_TEMPLATE.md?plain=1)
### 🤔 这个变动的性质是?
- [ ] 🆕 新特性提交
- [ ] 🐞 Bug 修复
- [ ] 📝 站点、文档改进
- [ ] 📽️ 演示代码改进
- [ ] 💄 组件样式/交互改进
- [ ] 🤖 TypeScript 定义更新
- [ ] 📦 包体积优化
- [ ] ⚡️ 性能优化
- [ ] ⭐️ 功能增强
- [ ] 🌐 国际化改进
- [ ] 🛠 重构
- [ ] 🎨 代码风格优化
- [ ] ✅ 测试用例
- [ ] 🔀 分支合并
- [ ] ⏩ 工作流程
- [ ] ❓ 其他改动(是关于什么的改动?)
### 🔗 相关 Issue
> 1. 描述相关需求的来源,如相关的 issue 讨论链接。
> 2. 例如 close #xxxx、 fix #xxxx
### 💡 需求背景和解决方案
> 1. 要解决的具体问题。
> 2. 列出最终的 API 实现和用法。
> 3. 涉及UI/交互变动建议提供截图或 GIF。
### 📝 更新日志
> - 郑重地阅读 [如何维护更新日志](https://keepachangelog.com/zh-CN/1.1.0/)
> - 描述改动对开发者有哪些影响,而非解决方式
> - 可参考https://ant.design/changelog-cn
| 语言 | 更新描述 |
| ------- | -------- |
| 🇺🇸 英文 | |
| 🇨🇳 中文 | |

View File

@ -38,9 +38,9 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
Hello @${{ github.event.issue.user.login }}. Please provide a online reproduction by forking codesandbox of [antd@5.x](https://u.ant.design/codesandbox-repro) or [antd@4.x](https://u.ant.design/codesandbox-repro-4x), or provide a minimal GitHub repository. Issues labeled by `Need Reproduce` will be closed if no activities in 3 days.
Hello @${{ github.event.issue.user.login }}. Please provide a online reproduction by forking [this one](https://u.ant.design/repro) or provide a minimal GitHub repository like [create-react-app-antd](https://github.com/ant-design/create-react-app-antd). Issues labeled by `Need Reproduce` will be closed if no activities in 3 days.
你好 @${{ github.event.issue.user.login }},我们需要你提供一个在线的重现实例以便于我们帮你排查问题。你可以通过点击这里创建一个 [antd@5.x](https://u.ant.design/codesandbox-repro) 或 [antd@4.x](https://u.ant.design/codesandbox-repro-4x) 的 codesandbox或者提供一个最小化的 GitHub 仓库。3 天内未跟进此 issue 将会被自动关闭。
你好 @${{ github.event.issue.user.login }},我们需要你提供一个在线的重现实例以便于我们帮你排查问题。你可以通过 fork 这个[在线重现案例](https://u.ant.design/repro) ,或者提供一个最小化的 GitHub 仓库(类似 [create-react-app-antd](https://github.com/ant-design/create-react-app-antd)。3 天内未跟进此 issue 将会被自动关闭。
> [什么是最小化重现,为什么这是必需的?](https://github.com/ant-design/ant-design/wiki/%E4%BB%80%E4%B9%88%E6%98%AF%E6%9C%80%E5%B0%8F%E5%8C%96%E9%87%8D%E7%8E%B0%EF%BC%8C%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E6%98%AF%E5%BF%85%E9%9C%80%E7%9A%84%EF%BC%9F)
@ -58,17 +58,17 @@ jobs:
你好 @${{ github.event.issue.user.login }}Ant Design Issue 板块是用于 bug 反馈与需求讨论的地方。请[勿询问如何使用的问题](https://github.com/ant-design/ant-design/issues/2320),你可以试着在 [antd discussions](https://github.com/ant-design/ant-design/discussions) 新开一个 discussion选择 `Q&A` 类别进行提问,也可以在 [Stack Overflow](http://stackoverflow.com/questions/tagged/antd) 或者 [Segment Fault](https://segmentfault.com/t/antd) 中提问(记得添加 `antd` 和 `react` 标签哦~)。
- name: 3.x
if: github.event.label.name == '3.x'
- name: 3.x or 4.x
if: github.event.label.name == '3.x' || github.event.label.name == '4.x'
uses: actions-cool/issues-helper@v3
with:
actions: 'create-comment,close-issue'
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
Hi @${{ github.event.issue.user.login }}. Current version (3.x) is off the maintenance period. We may not accept pull request or fix bug with it anymore. This topic will be auto closed.
Hi @${{ github.event.issue.user.login }}. Current version (${{ github.event.label.name }}) is off the maintenance period. We may not accept pull request or fix bug with it anymore. This topic will be auto closed.
你好 @${{ github.event.issue.user.login }},当前版本(3.x)已经过了维护期。我们不会再接受对其的相关 PR 与 issue。当前 topic 会被自动关闭。
你好 @${{ github.event.issue.user.login }},当前版本(${{ github.event.label.name }})已经过了维护期。我们不会再接受对其的相关 PR 与 issue。当前 topic 会被自动关闭。
- name: invalid
if: github.event.label.name == 'Invalid'

View File

@ -20,9 +20,7 @@ jobs:
- name: checkout
uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- uses: oven-sh/setup-bun@v2
- uses: actions/cache@v4
with:
@ -54,11 +52,15 @@ jobs:
- name: 🎨 Diff Report
if: ${{ failure() }}
run: npx diff-yarn-lock --source=~tmpProj/yarn.lock --target=~tmpProj/yarn.lock.failed
run: bunx diff-yarn-lock --source=~tmpProj/yarn.lock --target=~tmpProj/yarn.lock.failed
- uses: actions-cool/ci-notice@v1
if: ${{ failure() }}
with:
notice-types: 'dingding'
# Exit directly with non-zero to trigger the failure logic of ci-notice.
ci: |
echo "❌ CI Mock Project Build Failed"
exit 1
dingding-token: ${{ secrets.DINGDING_BOT_COLLABORATOR_TOKEN }}
notice-title: 'CI Mock Project Build Failed'

Some files were not shown because too many files have changed in this diff Show More