Compare commits

...

17 Commits

Author SHA1 Message Date
afc163
4aadae9bf6 Merge branch 'master' into docs/themer 2026-02-12 00:45:17 +08:00
afc163
7868764b90 fix: resolve AI bot review feedback
This commit fixes issues reported by coderabbitai and gemini-code-assist:

- Group.tsx: Move flex container outside Typography.Title for valid HTML, use token.paddingXS instead of hardcoded gap
- ComponentsBlock.tsx: Fix Select defaultValue to use string array, localize Segmented labels
- ThemePreview/index.tsx: Add missing aiGenerateItem class, replace hardcoded padding with cssVar
- PromptDrawer.tsx: Change size to width prop, useCallback for handlers, type-safe property checking, stable cache key
- usePromptRecommend.ts: Add error handling for JSON.parse, consolidate fallback arrays, fix abortController race condition
- index.tsx: Use proper SiteContextProps type, remove commented code

Co-Authored-By: Claude (GLM-4.7) <noreply@anthropic.com>
2026-02-12 00:43:07 +08:00
Copilot
bf8822b817 fix: TreeSelect selected value visible when searching (#56946)
* Initial plan

* Fix TreeSelect selected value visibility when searching

- Fixed CSS specificity issue where .ant-select-open .ant-select-content was overriding the transparent color
- Added nested rule for has-search-value within open state to ensure selected text is hidden during search
- Added test case to verify the fix

Co-authored-by: afc163 <507615+afc163@users.noreply.github.com>

* Add test for Select component search value hiding

- Added test case to ensure Select component also properly hides selected value when searching
- Verified fix works for both Select and TreeSelect components

Co-authored-by: afc163 <507615+afc163@users.noreply.github.com>

* Fix code formatting per Biome requirements

- Simplified JSX formatting in test files
- All linting checks pass
- Tests still passing

Co-authored-by: afc163 <507615+afc163@users.noreply.github.com>

* Remove test cases per feedback

- Removed "should hide selected value when searching" test from Select
- Removed "should hide selected value when searching" test from TreeSelect
- CSS fix in select-input.ts remains (the actual fix)

Co-authored-by: afc163 <507615+afc163@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: afc163 <507615+afc163@users.noreply.github.com>
Co-authored-by: afc163 <afc163@gmail.com>
2026-02-12 00:31:36 +08:00
afc163
276a8921cf commit 2026-02-12 00:22:52 +08:00
afc163
fde6fa9795 fix: resolve TypeScript errors in PromptDrawer
- Replace BubbleProps with BubbleItemType for correct typing
- Add ExtendedPromptsItemType interface for additional properties
- Add missing 'role' property to bubble items
- Add missing 'content' property to bubble item with contentRender
Co-Authored-By: Claude (GLM-4.7) <noreply@anthropic.com>
2026-02-12 00:22:52 +08:00
afc163
3dbba0e639 Merge branch 'master' into docs/themer 2026-02-11 23:56:07 +08:00
afc163
23e2c4a472 commit 2026-02-11 23:52:50 +08:00
afc163
73c6168e3f chore: remove unused AI theme prompts zh-CN doc 2026-02-11 23:38:34 +08:00
afc163
87f59266e5 chore: remove unused AI theme prompts en-US doc 2026-02-11 23:38:22 +08:00
afc163
caf62f4e56 feat: add AI theme recommendation feature
- Add AI theme generator documentation (zh-CN/en-US)
- Add usePromptRecommend hook for AI-powered theme recommendations
- Enhance PromptDrawer with Welcome/Prompts components and refresh button
- Add "AI Generate Theme" trigger in theme preview list
- Add "Reset to default theme" button in drawer header
- Support skeleton loading state
- Use Bubble.System round shape for completion message

Co-Authored-By: Claude (GLM-4.7) <noreply@anthropic.com>
2026-02-11 23:31:27 +08:00
afc163
8f95529432 commit 2026-02-11 18:52:17 +08:00
xbsheng
5a07a38c4b fix: fix Upload picture-card list overflow when items wrap to multiple lines (#56945)
* fix(upload): remove height property from picture card style

* fix(upload): add minHeight to picture card style for better layout
2026-02-11 18:46:02 +08:00
二货爱吃白萝卜
cd9e9aecf5 fix: bump @rc-component/motion to fix image preview flicker (#56937)
* fix: bump @rc-component/motion to fix image preview flicker

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: bump @rc-component/motion to ^1.3.1 to fix image preview flicker

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: update snapshots for back-top, color-picker and float-button

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-02-11 17:49:54 +08:00
afc163
bf370c4af5 docs: improve documentation links and security policy (#56927)
* docs: improve documentation links and security policy

- Update SECURITY.md with actual security policy content instead of placeholder text
- Convert HTTP links to HTTPS for better security:
  - react-flexbox-grid link in Grid component
  - unpkg.com link in ConfigProvider component
  - iconfont.cn links in Icon component
  - ant.design links in Tooltip FAQ docs
- Update all iconfont.cn references from HTTP to HTTPS across component docs

Co-Authored-By: Claude (GLM-4.7) <noreply@anthropic.com>

* docs: fix grammar, remove invalid attributes, and update more HTTP links

- Fix English grammar in Affix and Input component docs
- Fix Chinese characters in English migration guide
- Remove invalid `target` attribute from img tag in README
- Fix heading capitalization in contributing guide
- Convert remaining HTTP links to HTTPS:
  - iconfont.cn links in Icon demo files
  - day.js.org links in TimePicker component

Co-Authored-By: Claude (GLM-4.7) <noreply@anthropic.com>

* Update docs/react/migration-v6.en-US.md

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: afc163 <afc163@gmail.com>

* Apply suggestions from code review

Signed-off-by: afc163 <afc163@gmail.com>

* Apply suggestions from code review

Signed-off-by: afc163 <afc163@gmail.com>

* Apply suggestions from code review

Signed-off-by: afc163 <afc163@gmail.com>

---------

Signed-off-by: afc163 <afc163@gmail.com>
Co-authored-by: Claude (GLM-4.7) <noreply@anthropic.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-02-11 13:17:39 +08:00
afc163
79eb03d314 docs(agents): update and simplify AGENTS.md (#56922)
* docs(agents): update and simplify AGENTS.md with latest project information

- Update React version requirement to 18+ and Node.js to 18.12.0+
- Add Biome code formatting tool section
- Expand CSS-in-JS architecture and Token system documentation
- Add component development template
- Add troubleshooting section
- Simplify frequently used commands
- Duplicate references section removed
- Merge duplicate Token naming sections

Co-Authored-By: Claude (GLM-4.7) <noreply@anthropic.com>

* Update AGENTS.md

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: afc163 <afc163@gmail.com>

---------

Signed-off-by: afc163 <afc163@gmail.com>
Co-authored-by: Claude (GLM-4.7) <noreply@anthropic.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-02-11 13:15:58 +08:00
dependabot[bot]
0cfcd0873a chore: bump @eslint-react/eslint-plugin from 2.12.2 to 2.12.4 (#56931)
Bumps [@eslint-react/eslint-plugin](https://github.com/Rel1cx/eslint-react/tree/HEAD/packages/plugins/eslint-plugin) from 2.12.2 to 2.12.4.
- [Release notes](https://github.com/Rel1cx/eslint-react/releases)
- [Changelog](https://github.com/Rel1cx/eslint-react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Rel1cx/eslint-react/commits/v2.12.4/packages/plugins/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@eslint-react/eslint-plugin"
  dependency-version: 2.12.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-11 09:35:37 +08:00
高艳兵
2ab7472d97 docs(Pagination): correct component size naming (#56928)
* docs: correct component size naming

* Update components/pagination/index.zh-CN.md

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by:  高艳兵 <66234749+QDyanbing@users.noreply.github.com>

* Update components/pagination/index.en-US.md

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by:  高艳兵 <66234749+QDyanbing@users.noreply.github.com>

* fix: remove deprecated tip

---------

Signed-off-by: 高艳兵 <66234749+QDyanbing@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-02-10 21:18:45 +08:00
33 changed files with 1260 additions and 701 deletions

View File

@@ -46,6 +46,8 @@ export interface GroupProps {
decoration?: React.ReactNode;
/** 预加载的背景图片列表 */
backgroundPrefetchList?: string[];
/** 标题右侧的操作按钮 */
extra?: React.ReactNode;
}
const Group: React.FC<React.PropsWithChildren<GroupProps>> = (props) => {
@@ -59,6 +61,7 @@ const Group: React.FC<React.PropsWithChildren<GroupProps>> = (props) => {
background,
collapse,
backgroundPrefetchList,
extra,
} = props;
// 预加载背景图片
@@ -87,18 +90,29 @@ const Group: React.FC<React.PropsWithChildren<GroupProps>> = (props) => {
<div className={styles.container}>{decoration}</div>
<GroupMaskLayer style={{ paddingBlock: token.marginFarSM }}>
<div className={styles.typographyWrapper}>
<Typography.Title
id={id}
level={1}
<div
style={{
fontWeight: 900,
color: titleColor,
// Special for the title
fontSize: isMobile ? token.fontSizeHeading2 : token.fontSizeHeading1,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: token.paddingXS,
}}
>
{title}
</Typography.Title>
<Typography.Title
id={id}
level={1}
style={{
fontWeight: 900,
color: titleColor,
margin: 0,
// Special for the title
fontSize: isMobile ? token.fontSizeHeading2 : token.fontSizeHeading1,
}}
>
{title}
</Typography.Title>
{extra}
</div>
<Typography.Paragraph
style={{
color: titleColor,

View File

@@ -4,15 +4,16 @@ import {
Alert,
App,
Button,
Card,
Checkbox,
ColorPicker,
ConfigProvider,
DatePicker,
Dropdown,
Flex,
Modal,
Progress,
Radio,
Segmented,
Select,
Slider,
Space,
@@ -26,6 +27,7 @@ import clsx from 'clsx';
import useLocale from '../../../../hooks/useLocale';
const { _InternalPanelDoNotUseOrYouWillBeFired: ModalPanel } = Modal;
const { Group: RadioButtonGroup, Button: RadioButton } = Radio;
const locales = {
cn: {
@@ -48,6 +50,9 @@ const locales = {
icon: '图标按钮',
hello: '你好Ant Design!',
release: 'Ant Design 6.0 正式发布!',
segmentedDaily: '每日',
segmentedWeekly: '每周',
segmentedMonthly: '每月',
},
en: {
range: 'Set Range',
@@ -69,6 +74,9 @@ const locales = {
icon: 'Icon',
hello: 'Hello, Ant Design!',
release: 'Ant Design 6.0 is released!',
segmentedDaily: 'Daily',
segmentedWeekly: 'Weekly',
segmentedMonthly: 'Monthly',
},
};
@@ -95,7 +103,7 @@ const ComponentsBlock: React.FC<ComponentsBlockProps> = (props) => {
return (
<ConfigProvider {...config}>
<Card className={clsx(containerClassName, styles.container)}>
<div className={clsx(containerClassName, styles.container)}>
<App>
<Flex vertical gap="middle" style={style} className={className}>
<ModalPanel title="Ant Design" width="100%">
@@ -120,13 +128,32 @@ const ComponentsBlock: React.FC<ComponentsBlockProps> = (props) => {
</Space.Compact>
</div>
<ColorPicker style={{ flex: 'none' }} />
<ColorPicker showText defaultValue="#1677ff" style={{ flex: 'none' }} />
<Select
style={{ flex: 'auto' }}
mode="multiple"
maxTagCount="responsive"
defaultValue={[{ value: 'apple' }, { value: 'banana' }]}
defaultValue={['apple', 'banana']}
options={[
{ value: 'apple', label: locale.apple },
{ value: 'banana', label: locale.banana },
{ value: 'orange', label: locale.orange },
{ value: 'watermelon', label: locale.watermelon },
]}
/>
</Flex>
{/* Filled variants */}
<Flex gap="middle">
<DatePicker variant="filled" />
<Select
variant="filled"
style={{ flex: 'auto' }}
mode="multiple"
maxTagCount="responsive"
defaultValue={['apple', 'banana']}
options={[
{ value: 'apple', label: locale.apple },
{ value: 'banana', label: locale.banana },
@@ -147,20 +174,7 @@ const ComponentsBlock: React.FC<ComponentsBlockProps> = (props) => {
]}
/>
{/* Line */}
<Slider
style={{ marginInline: 20 }}
range
marks={{
0: '0°C',
26: '26°C',
37: '37°C',
100: {
style: { color: '#f50' },
label: <strong>100°C</strong>,
},
}}
defaultValue={[26, 37]}
/>
<Slider defaultValue={50} />
{/* Line */}
<Flex gap="middle">
<Button type="primary" className={styles.flexAuto}>
@@ -188,9 +202,20 @@ const ComponentsBlock: React.FC<ComponentsBlockProps> = (props) => {
/>
<Radio.Group defaultValue={locale.apple} options={[locale.apple, locale.banana]} />
</Flex>
<Flex gap="middle" align="center">
<RadioButtonGroup defaultValue="a">
<RadioButton value="a">A</RadioButton>
<RadioButton value="b">B</RadioButton>
<RadioButton value="c">C</RadioButton>
</RadioButtonGroup>
<Segmented
defaultValue={locale.segmentedDaily}
options={[locale.segmentedDaily, locale.segmentedWeekly, locale.segmentedMonthly]}
/>
</Flex>
</Flex>
</App>
</Card>
</div>
</ConfigProvider>
);
};

View File

@@ -13,11 +13,15 @@ const locales = {
cn: {
themeTitle: '定制主题,随心所欲',
themeDesc: '开放样式算法与语义化结构,让你与 AI 一起轻松定制主题',
aiGenerate: 'AI 生成主题',
aiGenerateDesc: '用一句话描述你想要的风格',
},
en: {
themeTitle: 'Flexible theme customization',
themeDesc:
'Open style algorithms and semantic structures make it easy for you and AI to customize themes',
aiGenerate: 'AI Generate Theme',
aiGenerateDesc: 'Describe your desired style',
},
};
@@ -40,7 +44,7 @@ const useStyles = createStyles(({ css, cssVar }) => ({
listStyleType: 'none',
display: 'flex',
flexDirection: 'column',
gap: cssVar.paddingMD,
gap: cssVar.paddingSM,
}),
listItem: css({
margin: 0,
@@ -53,7 +57,7 @@ const useStyles = createStyles(({ css, cssVar }) => ({
borderColor: 'transparent',
transition: `all ${cssVar.motionDurationMid} ${cssVar.motionEaseInOut}`,
'&:hover:not(.active)': {
'&:hover:not(.active):not(.ai-generate-item)': {
borderColor: cssVar.colorPrimaryBorder,
backgroundColor: cssVar.colorPrimaryBg,
cursor: 'pointer',
@@ -82,6 +86,39 @@ const useStyles = createStyles(({ css, cssVar }) => ({
},
}),
// AI Generate Item
aiGenerateItem: css({
borderStyle: 'dashed',
opacity: 0.7,
cursor: 'pointer',
color: cssVar.colorTextSecondary,
paddingInline: cssVar.padding,
'&:hover': {
borderColor: cssVar.colorPrimary,
color: cssVar.colorPrimary,
opacity: 1,
},
}),
aiGenerateContent: css({
position: 'relative',
zIndex: 1,
}),
aiGenerateIcon: css({
fontSize: 14,
marginRight: 6,
opacity: 0.6,
}),
aiGenerateDesc: css({
fontSize: cssVar.fontSizeSM,
opacity: 0.5,
marginTop: 2,
fontWeight: 400,
}),
// Components
componentsBlockContainer: css({
flex: 'auto',
@@ -98,7 +135,12 @@ const useStyles = createStyles(({ css, cssVar }) => ({
}),
}));
export default function ThemePreview() {
export interface ThemePreviewProps {
onOpenPromptDrawer?: () => void;
}
export default function ThemePreview(props: ThemePreviewProps = {}) {
const { onOpenPromptDrawer } = props;
const [locale] = useLocale(locales);
const { styles } = useStyles();
const isDark = React.use(DarkContext);
@@ -111,10 +153,7 @@ export default function ThemePreview() {
const defaultThemeName = isDark ? 'dark' : 'light';
const targetTheme =
process.env.NODE_ENV !== 'production'
? previewThemes[previewThemes.length - 1].name
: previewThemes.find((theme) => theme.key === defaultThemeName)?.name ||
previewThemes[0].name;
previewThemes.find((theme) => theme.key === defaultThemeName)?.name || previewThemes[0].name;
setActiveName(targetTheme);
}, [isDark]);
@@ -148,24 +187,50 @@ export default function ThemePreview() {
backgroundPrefetchList={backgroundPrefetchList}
>
<Flex className={styles.container} gap="large">
<div className={styles.list} role="tablist" aria-label="Theme selection">
{previewThemes.map((theme) => (
<div
style={{
display: 'flex',
}}
>
<div className={styles.list} role="tablist" aria-label="Theme selection">
{previewThemes.map((theme) => (
<div
className={clsx(
styles.listItem,
activeName === theme.name && 'active',
activeTheme?.bgImgDark && 'dark',
)}
key={theme.name}
role="tab"
tabIndex={activeName === theme.name ? 0 : -1}
aria-selected={activeName === theme.name}
onClick={() => handleThemeClick(theme.name)}
onKeyDown={(event) => handleKeyDown(event, theme.name)}
style={{ marginBottom: 8 }}
>
{theme.name}
</div>
))}
{/* AI 生成主题 - 最后一个选项 */}
<div
className={clsx(
styles.listItem,
activeName === theme.name && 'active',
activeTheme?.bgImgDark && 'dark',
)}
key={theme.name}
className={clsx(styles.listItem, styles.aiGenerateItem, 'ai-generate-item')}
role="tab"
tabIndex={activeName === theme.name ? 0 : -1}
aria-selected={activeName === theme.name}
onClick={() => handleThemeClick(theme.name)}
onKeyDown={(event) => handleKeyDown(event, theme.name)}
tabIndex={0}
onClick={onOpenPromptDrawer}
onKeyDown={(event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
onOpenPromptDrawer?.();
}
}}
>
{theme.name}
<div className={styles.aiGenerateContent}>
<span className={styles.aiGenerateIcon}>🎨</span>
<span>{locale.aiGenerate}</span>
</div>
<div className={styles.aiGenerateDesc}>{locale.aiGenerateDesc}</div>
</div>
))}
</div>
</div>
<ComponentsBlock
key={activeName}

View File

@@ -8,7 +8,6 @@ import type { UseTheme } from '.';
const useStyles = createStyles(({ css, cssVar }) => {
const glassBorder = {
border: `${cssVar.lineWidth} solid rgba(255,255,255,0.3)`,
boxShadow: [
`${cssVar.boxShadowSecondary}`,
`inset 0 0 5px 2px rgba(255, 255, 255, 0.3)`,

View File

@@ -1,4 +1,4 @@
import React, { Suspense } from 'react';
import React, { Suspense, useState } from 'react';
import { theme } from 'antd';
import { createStaticStyles } from 'antd-style';
@@ -8,6 +8,9 @@ import BannerRecommends from './components/BannerRecommends';
import Group from './components/Group';
import PreviewBanner from './components/PreviewBanner';
import ThemePreview from './components/ThemePreview';
import PromptDrawer from '../../theme/common/ThemeSwitch/PromptDrawer';
import SiteContext from '../../theme/slots/SiteContext';
import type { SiteContextProps } from '../../theme/slots/SiteContext';
const ComponentsList = React.lazy(() => import('./components/ComponentsList'));
const DesignFramework = React.lazy(() => import('./components/DesignFramework'));
@@ -42,6 +45,16 @@ const Homepage: React.FC = () => {
const { token } = theme.useToken();
const isDark = React.use(DarkContext);
const [promptDrawerOpen, setPromptDrawerOpen] = useState(false);
const siteContext = React.use(SiteContext);
const handlePromptDrawerOpen = () => setPromptDrawerOpen(true);
const handlePromptDrawerClose = () => setPromptDrawerOpen(false);
const handleThemeChange = (themeConfig: SiteContextProps['dynamicTheme']) => {
if (siteContext?.updateSiteConfig) {
siteContext.updateSiteConfig({ dynamicTheme: themeConfig });
}
};
return (
<section>
@@ -49,13 +62,14 @@ const Homepage: React.FC = () => {
<BannerRecommends />
</PreviewBanner>
{/* 定制主题 */}
{/* <ConfigProvider theme={{ algorithm: theme.defaultAlgorithm }}>
<Suspense fallback={null}>
<Theme />
</Suspense>
</ConfigProvider> */}
<ThemePreview />
<ThemePreview onOpenPromptDrawer={handlePromptDrawerOpen} />
{/* AI 生成主题抽屉 */}
<PromptDrawer
open={promptDrawerOpen}
onClose={handlePromptDrawerClose}
onThemeChange={handleThemeChange}
/>
{/* 组件列表 */}
<Group
@@ -90,55 +104,6 @@ const Homepage: React.FC = () => {
</Group>
</section>
);
// return (
// <section>
// <PreviewBanner>
// <BannerRecommends />
// </PreviewBanner>
// <div>
// {/* 定制主题 */}
// <ConfigProvider theme={{ algorithm: theme.defaultAlgorithm }}>
// <Suspense fallback={null}>
// <Theme />
// </Suspense>
// </ConfigProvider>
// {/* 组件列表 */}
// <Group
// background={token.colorBgElevated}
// collapse
// title={locale.assetsTitle}
// description={locale.assetsDesc}
// id="design"
// >
// <Suspense fallback={null}>
// <ComponentsList />
// </Suspense>
// </Group>
// {/* 设计语言 */}
// <Group
// title={locale.designTitle}
// description={locale.designDesc}
// background={isDark ? '#393F4A' : '#F5F8FF'}
// decoration={
// <img
// draggable={false}
// className={classNames.image}
// src="https://gw.alipayobjects.com/zos/bmw-prod/ba37a413-28e6-4be4-b1c5-01be1a0ebb1c.svg"
// alt="bg"
// />
// }
// >
// <Suspense fallback={null}>
// <DesignFramework />
// </Suspense>
// </Group>
// </div>
// </section>
// );
};
export default Homepage;

View File

@@ -1,22 +1,46 @@
import React, { useRef, useState } from 'react';
import { AntDesignOutlined, UserOutlined } from '@ant-design/icons';
import { Bubble, Sender } from '@ant-design/x';
import { UserOutlined } from '@ant-design/icons';
import { Bubble, Prompts, Sender, Welcome } from '@ant-design/x';
import type { BubbleItemType } from '@ant-design/x/es/bubble/interface';
import type { PromptsItemType } from '@ant-design/x';
import type { SenderRef } from '@ant-design/x/es/sender';
import { Drawer, Flex, Typography } from 'antd';
import type { GetProp } from 'antd';
import { Button, Divider, Drawer, Flex, Skeleton, Splitter, Typography } from 'antd';
import useLocale from '../../../hooks/useLocale';
import type { SiteContextProps } from '../../../theme/slots/SiteContext';
import SiteContext from '../../../theme/slots/SiteContext';
import useLocale from '../../../hooks/useLocale';
import ComponentsBlock from '../../../pages/index/components/ThemePreview/ComponentsBlock';
import usePromptTheme from './usePromptTheme';
import usePromptRecommend from './usePromptRecommend';
const antdLogoSrc = 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg';
const THEME_EMOJIS = ['🌅', '🌊', '🌿', '🍂', '🌸', '🌌', '🎨', '⚡', '🔮', '🪐'];
const getEmojiForTheme = (index: number) => THEME_EMOJIS[index % THEME_EMOJIS.length];
const locales = {
cn: {
title: 'AI 生成主题',
finishTips: '生成完成,对话以重新生成。',
title: '🎨 AI 生成主题',
finishTips: '生成主题完成,已应用',
placeholder: '描述你想要的主题风格,如:温暖阳光、清新自然、科技感...',
welcomeTitle: 'AI 主题生成器',
welcomeDescription: '描述你想要的风格,我会为你生成专属主题',
recommendTitle: '推荐主题',
loading: '加载中...',
refresh: '换一换',
resetToDefault: '恢复默认主题',
},
en: {
title: 'AI Theme Generator',
finishTips: 'Completed. Regenerate by start a new conversation.',
title: '🎨 AI Theme Generator',
finishTips: 'Theme generated and applied',
placeholder: 'Describe your desired theme style, e.g., warm sunny, fresh natural, tech feel...',
welcomeTitle: 'AI Theme Generator',
welcomeDescription: 'Describe your desired style and I will generate a custom theme for you',
recommendTitle: 'Recommended Themes',
loading: 'Loading...',
refresh: 'Refresh',
resetToDefault: 'Reset to default theme',
},
};
@@ -26,17 +50,40 @@ export interface PromptDrawerProps {
onThemeChange?: (themeConfig: SiteContextProps['dynamicTheme']) => void;
}
// Extended type for Prompts items with additional properties
interface ExtendedPromptsItemType extends PromptsItemType {
originalDescription?: string;
isRefresh?: boolean;
}
const PromptDrawer: React.FC<PromptDrawerProps> = ({ open, onClose, onThemeChange }) => {
const [locale] = useLocale(locales);
const { updateSiteConfig, isDark } = React.use(SiteContext) as SiteContextProps;
const [locale, localeKey] = useLocale(locales);
const [inputValue, setInputValue] = useState('');
const senderRef = useRef<SenderRef>(null);
const [submitPrompt, loading, prompt, resText, cancelRequest] = usePromptTheme(onThemeChange);
const {
recommendations,
loading: recommendLoading,
fetch: fetchRecommendations,
} = usePromptRecommend(localeKey);
const handleSubmit = (value: string) => {
submitPrompt(value);
setInputValue('');
const handleSubmit = React.useCallback(
(value: string) => {
submitPrompt(value);
setInputValue('');
},
[submitPrompt],
);
const handleRefreshRecommendations = React.useCallback(() => {
fetchRecommendations(`prompt-drawer-refresh-${Date.now()}`);
}, [fetchRecommendations]);
const handleResetToDefaultTheme = () => {
updateSiteConfig({ dynamicTheme: undefined });
};
const handleAfterOpenChange = (isOpen: boolean) => {
@@ -44,14 +91,18 @@ const PromptDrawer: React.FC<PromptDrawerProps> = ({ open, onClose, onThemeChang
// Focus the Sender component when drawer opens
senderRef.current.focus?.();
}
// Fetch AI recommendations when drawer opens
if (isOpen) {
fetchRecommendations('prompt-drawer-init');
}
};
const items = React.useMemo<GetProp<typeof Bubble.List, 'items'>>(() => {
const items = React.useMemo<BubbleItemType[]>(() => {
if (!prompt) {
return [];
}
const nextItems: GetProp<typeof Bubble.List, 'items'> = [
const nextItems: BubbleItemType[] = [
{
key: 1,
role: 'user',
@@ -62,54 +113,274 @@ const PromptDrawer: React.FC<PromptDrawerProps> = ({ open, onClose, onThemeChang
},
{
key: 2,
role: 'system',
role: 'ai',
placement: 'start',
content: resText,
avatar: <AntDesignOutlined />,
avatar: <img src={antdLogoSrc} alt="Ant Design" style={{ width: 28, height: 28 }} />,
loading: !resText,
contentRender: (content: string) => (
<Typography>
<pre style={{ margin: 0 }}>{content}</pre>
<pre
style={{
margin: 0,
padding: '16px',
borderRadius: 8,
background: isDark
? 'linear-gradient(135deg, rgba(90,196,255,0.08) 0%, rgba(174,136,255,0.08) 100%)'
: 'linear-gradient(135deg, #f2f9fe 0%, #f7f3ff 100%)',
fontSize: 13,
lineHeight: 1.6,
border: 'none',
color: isDark ? 'rgba(255,255,255,0.85)' : 'rgba(0,0,0,0.85)',
}}
>
{content}
</pre>
</Typography>
),
styles: {
content: {
background: 'transparent',
padding: 0,
border: 'none',
},
},
},
];
if (!loading) {
nextItems.push({
key: 3,
role: 'divider',
role: 'system',
placement: 'start',
shape: 'round',
content: locale.finishTips,
avatar: <AntDesignOutlined />,
shape: 'corner',
});
// Add recommended themes prompts
const recommendedPrompts: ExtendedPromptsItemType[] = recommendations
.slice(0, 4)
.map((text, index) => ({
key: `rec-${text}`,
description: `${getEmojiForTheme(index)} ${text}`,
originalDescription: text,
}));
// Add refresh button
recommendedPrompts.push({
key: 'refresh',
description: `🔄 ${locale.refresh}`,
isRefresh: true,
});
nextItems.push({
key: 4,
role: 'ai',
placement: 'start',
content: '',
avatar: <img src={antdLogoSrc} alt="Ant Design" style={{ width: 28, height: 28 }} />,
contentRender: () =>
recommendLoading ? (
<Flex gap={8} wrap style={{ justifyContent: 'center' }}>
{Array.from({ length: 4 }).map((_, index) => (
<Skeleton.Input
key={index}
active
size="small"
style={{ width: 140, borderRadius: 8 }}
/>
))}
</Flex>
) : (
<Prompts
wrap
items={recommendedPrompts}
onItemClick={({ data }) => {
if ('isRefresh' in data && data.isRefresh) {
handleRefreshRecommendations();
} else {
handleSubmit(
String(
(data as ExtendedPromptsItemType).originalDescription ?? data.description,
),
);
}
}}
styles={{ root: { marginTop: 8 } }}
/>
),
styles: { content: { padding: 0, background: 'transparent' } },
});
}
return nextItems;
}, [prompt, resText, loading, locale.finishTips]);
}, [
prompt,
resText,
loading,
recommendLoading,
locale.finishTips,
isDark,
recommendations,
handleSubmit,
handleRefreshRecommendations,
locale.refresh,
]);
// Limit to 3 recommendations for Prompts component + refresh button
const prompts: ExtendedPromptsItemType[] = React.useMemo(() => {
const themePrompts: ExtendedPromptsItemType[] = recommendations
.slice(0, 3)
.map((text, index) => ({
key: text,
description: `${getEmojiForTheme(index)} ${text}`,
originalDescription: text,
}));
// Add refresh button as last item only when we have recommendations
if (themePrompts.length > 0) {
themePrompts.push({
key: 'refresh',
description: `🔄 ${locale.refresh}`,
isRefresh: true,
});
}
return themePrompts;
}, [recommendations, locale.refresh]);
const renderedWelcome = React.useMemo(
() => (
<div style={{ padding: '0 0 16px' }}>
<Welcome
icon={<img src={antdLogoSrc} alt="Ant Design" style={{ width: 48, height: 48 }} />}
title={locale.welcomeTitle}
description={locale.welcomeDescription}
styles={{
root: {
background: isDark
? 'linear-gradient(97deg, rgba(90,196,255,0.12) 0%, rgba(174,136,255,0.12) 100%)'
: 'linear-gradient(97deg, #f2f9fe 0%, #f7f3ff 100%)',
},
}}
/>
</div>
),
[locale.welcomeTitle, locale.welcomeDescription, isDark],
);
const renderedPrompts = React.useMemo(
() => (
<div style={{ padding: '0 0 32px' }}>
<Flex vertical gap={12} align="center">
<Divider titlePlacement="center" style={{ margin: 0, fontSize: 12 }}>
{locale.recommendTitle}
</Divider>
{recommendLoading ? (
<Flex gap={8} wrap style={{ justifyContent: 'center' }}>
{Array.from({ length: 3 }).map((_, index) => (
<Skeleton.Input
key={index}
active
size="small"
style={{ width: 140, borderRadius: 8 }}
/>
))}
</Flex>
) : (
prompts.length > 0 && (
<Prompts
wrap
items={prompts}
onItemClick={({ data }) => {
if ('isRefresh' in data && data.isRefresh) {
handleRefreshRecommendations();
} else {
handleSubmit(
String(
(data as ExtendedPromptsItemType).originalDescription ?? data.description,
),
);
}
}}
styles={{
root: {
marginTop: 4,
},
item: {
borderRadius: 8,
},
}}
/>
)
)}
</Flex>
</div>
),
[locale.recommendTitle, recommendLoading, prompts, handleSubmit, handleRefreshRecommendations],
);
return (
<Drawer
title={locale.title}
open={open}
onClose={onClose}
size={480}
width="80vw"
placement="right"
afterOpenChange={handleAfterOpenChange}
extra={
<Button type="text" size="small" onClick={handleResetToDefaultTheme}>
{locale.resetToDefault}
</Button>
}
>
<Flex vertical style={{ height: '100%' }}>
<Bubble.List style={{ flex: 1, overflow: 'auto' }} items={items} />
<Sender
ref={senderRef}
style={{ flex: 0 }}
value={inputValue}
onChange={setInputValue}
onSubmit={handleSubmit}
loading={loading}
onCancel={cancelRequest}
/>
</Flex>
<Splitter style={{ height: '100%' }}>
{/* 左侧预览区域 */}
<Splitter.Panel defaultSize="50%" min="30%" max="70%">
<Flex vertical style={{ height: '100%', padding: '0 8px' }}>
<div
style={{
flex: 1,
padding: '16px 8px',
overflow: 'auto',
}}
>
<ComponentsBlock className="prompt-drawer-preview" />
</div>
</Flex>
</Splitter.Panel>
{/* 右侧对话区域 */}
<Splitter.Panel defaultSize="50%" min="30%" max="70%">
<Flex vertical gap={0} style={{ height: '100%', padding: '0 8px' }}>
<div
style={{
flex: 1,
padding: 0,
overflow: 'auto',
}}
>
{!prompt ? (
<>
{renderedWelcome}
{renderedPrompts}
</>
) : (
<Bubble.List items={items} />
)}
</div>
<Sender
ref={senderRef}
value={inputValue}
onChange={setInputValue}
onSubmit={handleSubmit}
loading={loading}
onCancel={cancelRequest}
placeholder={locale.placeholder}
/>
</Flex>
</Splitter.Panel>
</Splitter>
</Drawer>
);
};

View File

@@ -0,0 +1,158 @@
import { XStream } from '@ant-design/x-sdk';
import { useRef, useState } from 'react';
const locales = {
cn: {
recommendPrompt:
'请生成 4 个 Tailwindcss 主题描述词,需要多样化的 UI 设计风格,用于 Ant Design 主题生成器推荐。参考官方内建风格:暗黑风格、类 MUI 风格、类 shadcn 风格、卡通风格、插画风格、类 Bootstrap 拟物化风格、玻璃风格、极客风格。回复格式:用逗号分隔。',
},
en: {
recommendPrompt:
'Generate 4 Tailwind CSS theme names featuring diverse UI design styles for an Ant Design theme generator recommendation. Reference the official built-in styles: Dark, MUI-like, shadcn-like, Cartoon, Illustration, Bootstrap-like Skeuomorphic, Glassmorphism, and Geek. Output format: comma-separated.',
},
};
const FALLBACK_THEMES = {
cn: [
'温暖阳光的橙色调,营造活力积极的氛围',
'专业稳重的深海蓝商务风格',
'清新自然的森林绿环保主题',
'极客紫霓虹感的科技前沿风格',
'柔和粉紫的樱花春日浪漫主题',
'高对比度的赛博朋克深色科技风',
'莫兰迪灰色调,简约优雅的现代感',
'青花瓷蓝白配色,东方雅韵',
'马卡龙多彩配色,活泼童趣',
'水墨黑白灰,传统韵味',
],
en: [
'Warm sunny orange tones for energetic positive vibes',
'Professional deep ocean blue business style',
'Fresh natural forest green eco-friendly theme',
'Geek purple neon tech cutting-edge style',
'Soft pink-purple cherry blossom spring romantic theme',
'High contrast cyberpunk dark tech style',
'Morandi gray tones, minimalist elegant modern feel',
'Blue and white porcelain colors, Eastern elegance',
'Colorful macaron, lively and playful',
'Ink black white gray, traditional charm',
],
};
const fetchRecommendations = async (
localeKey: keyof typeof locales,
abortSignal?: AbortSignal,
): Promise<string[]> => {
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: locales[localeKey].recommendPrompt,
userId: 'AntDesignSite',
}),
signal: abortSignal,
};
try {
const response = await fetch('https://api.x.ant.design/api/agent_tbox_antd', options);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
if (!response.body) {
throw new Error('Response body is null');
}
let fullContent = '';
for await (const chunk of XStream({
readableStream: response.body,
})) {
if (chunk.event === 'message') {
try {
const data = JSON.parse(chunk.data) as {
lane: string;
payload: string;
};
const payload = JSON.parse(data.payload) as {
text: string;
};
fullContent += payload.text || '';
} catch {
// Skip malformed chunks and continue
}
}
}
// Parse theme names from response - separated by commas
const text = fullContent.trim();
const items = text
.split(/[,\n]/)
.map((item) => item.trim())
.filter(Boolean);
const result = items.slice(0, 4);
// If parsing failed or got no results, use fallback
if (result.length === 0) {
return FALLBACK_THEMES[localeKey];
}
return result;
} catch (error) {
// Don't log AbortError - it's expected when cancelling requests
if (error instanceof Error && error.name === 'AbortError') {
throw error; // Re-throw AbortError to be handled by caller
}
console.error('Error in fetchRecommendations:', error);
// Return fallback themes
return FALLBACK_THEMES[localeKey];
}
};
export default function usePromptRecommend(localeKey: keyof typeof locales = 'cn') {
const [recommendations, setRecommendations] = useState<string[]>([]);
const [loading, setLoading] = useState(false);
const abortControllerRef = useRef<AbortController | null>(null);
const fetchedKeyRef = useRef<string>('');
const fetch = async (key: string) => {
if (fetchedKeyRef.current === key) {
return;
}
// Cancel previous request if it exists
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
const abortController = new AbortController();
abortControllerRef.current = abortController;
setLoading(true);
try {
const items = await fetchRecommendations(localeKey, abortController.signal);
setRecommendations(items);
fetchedKeyRef.current = key;
} catch (error) {
if (!(error instanceof Error && error.name === 'AbortError')) {
console.error('Failed to fetch recommendations:', error);
// Use fallback on error
setRecommendations(FALLBACK_THEMES[localeKey]);
fetchedKeyRef.current = key;
}
} finally {
// Only clear loading and controller if this is still the active request
if (abortControllerRef.current === abortController) {
setLoading(false);
abortControllerRef.current = null;
}
}
};
return { recommendations, loading, fetch };
}

669
AGENTS.md
View File

@@ -11,14 +11,17 @@
- [命名规范](#命名规范)
- [TypeScript 规范](#typescript-规范)
- [样式规范](#样式规范)
- [代码格式化](#代码格式化)
- [开发指南](#开发指南)
- [测试指南](#测试指南)
- [演示代码规范](#演示代码规范)
- [国际化规范](#国际化规范)
- [组件开发模板](#组件开发模板)
- [文档和 Changelog](#文档和-changelog-规范)
- [Git 和 Pull Request](#git-和-pull-request-规范)
- [质量保证](#质量保证)
- [工具链和环境](#工具链和环境)
- [常见问题和故障排查](#常见问题和故障排查)
---
@@ -29,12 +32,15 @@
### 核心特性
- 使用 TypeScript 和 React 开发
- 兼容 React 18 ~ 19 版本
- 组件库设计精美,功能完善,广泛应用于企业级中后台产品
- 遵循 Ant Design 设计规范
- 支持国际化i18n
- 支持主题定制和暗色模式
- 兼容 React 18+ 版本peerDependencies: `>=18.0.0`)
- 包含 **84+ 个组件**,涵盖通用、输入、数据展示、反馈、导航、布局等类型
- 采用完整的 CSS-in-JS 架构(基于 `@ant-design/cssinjs`
- 支持 Design Token 主题系统和动态主题切换
- 支持国际化i18n包含 150+ 语言 locales
- 支持暗色模式和自定义主题
- 支持 RTL从右到左布局
- 支持服务端渲染SSR
- 提供完整的 TypeScript 类型定义
---
@@ -42,9 +48,9 @@
### 开发环境要求
- **Node.js**: >= 16
- **包管理器**: npm 或 utoo
- **浏览器兼容性**: Chrome 80+
- **Node.js**: >= 18.12.0(推荐使用 LTS 版本)
- **包管理器**: npm 或 ut(内部包管理器)
- **浏览器兼容性**: 现代浏览器(Chrome 80+、Edge、Firefox、Safari
- **编辑器**: VS Code推荐或其他支持 TypeScript 的编辑器
### 安装依赖
@@ -58,52 +64,53 @@ utoo install
### 常用开发命令
```bash
# 启动开发服务器(访问 http://127.0.0.1:8001
npm start
# 编译 TypeScript 代码到 lib 和 es 目录
npm run compile
# 构建 UMD 格式的构建产物
npm run build
# 运行所有测试
npm test
# 监听模式运行测试
npm test -- --watch
# 生成测试覆盖率报告
npm run test:coverage
# 代码检查(包括 TypeScript、ESLint、Biome、Markdown、Changelog
npm run lint
# 格式化代码
npm run format
# 生成 Changelog交互式
npm run changelog
# 清理构建产物
npm run clean
npm start # 启动开发服务器http://127.0.0.1:8001
npm run build # 完整构建
npm test # 运行测试
npm run lint # 代码检查
npm run format # 代码格式化
npm run version # 生成版本信息
npm run clean # 清理构建产物
```
### 项目结构
```
ant-design/
├── components/ # 组件源代码
── [component]/ # 单个组件目录
├── demo/ # 演示代码
├── style/ # 样式文件
├── index.tsx # 组件入口
── index.zh-CN.md # 组件文档
├── scripts/ # 构建和工具脚本
├── tests/ # 测试文件
├── CHANGELOG.zh-CN.md # 中文更新日志
├── CHANGELOG.en-US.md # 英文更新日志
└── package.json # 项目配置
├── components/ # 组件源代码84+ 组件)
── component-name/ # 单个组件目录
├── ComponentName.tsx # 主组件实现
├── SubComponent.tsx # 子组件(如有)
├── helpers.ts # 辅助函数
── hooks/ # 组件专属 hooks
│ │ ├── demo/ # 演示代码(*.tsx 和 *.md
│ │ ├── style/ # 样式系统
│ │ │ ├── index.ts # 样式钩子生成器
│ │ │ ├── token.ts # 主题 token 定义
│ │ │ └── variant.ts # 变体样式
│ │ ├── __tests__/ # 单元测试
│ │ ├── index.en-US.md # 英文文档
│ │ ├── index.zh-CN.md # 中文文档
│ │ └── index.tsx # 导出入口
│ ├── _util/ # 社会工具函数库
│ ├── theme/ # 主题系统
│ ├── locale/ # 国际化文本150+ 文件)
│ └── index.ts # 组件总入口
├── scripts/ # 构建和工具脚本26+ 脚本)
├── tests/ # 测试文件和工具
│ ├── __mocks__/ # Jest mocks
│ ├── shared/ # 共享测试工具
│ └── setup.ts # 测试环境设置
├── docs/ # 站点文档
├── CHANGELOG.zh-CN.md # 中文更新日志
├── CHANGELOG.en-US.md # 英文更新日志
├── package.json # 项目配置
├── tsconfig.json # TypeScript 配置
├── eslint.config.mjs # ESLint 配置
├── biome.json # Biome 配置
├── .jest.js # Jest 配置
├── .dumirc.ts # Dumi 文档配置
└── webpack.config.js # Webpack 构建配置
```
---
@@ -114,14 +121,20 @@ ant-design/
- ✅ 使用 TypeScript 和 React 书写
- ✅ 使用函数式组件和 Hooks**避免类组件**
- ✅ 使用 `forwardRef` 实现组件 ref 传递
- ✅ 使用提前返回early returns提高代码可读性
- ✅ 避免引入新依赖,严控打包体积
- ✅ 兼容 Chrome 80+ 浏览器
- ✅ 兼容现代浏览器
- ✅ 支持服务端渲染SSR
- ✅ 保持向下兼容,避免 breaking change
- ✅ 组件名使用大驼峰PascalCase`Button``DatePicker`
- ✅ 属性名使用小驼峰camelCase`onClick``defaultValue`
- ✅ 合理使用 `useLayoutEffect` 处理性能敏感操作(如 loading 延迟)
- ✅ 合理使用 `React.memo``useMemo``useCallback` 优化性能
- ✅ 使用 `clsx` 处理类名拼接
- ✅ 使用 `devUseWarning` 提供开发时 API 过期警告
- ✅ 使用 `displayName` 设置组件调试名称
- ✅ 支持 Semantic 样式系统(`classNames``styles` 属性)
#### Props 命名
@@ -140,6 +153,37 @@ ant-design/
| 图标 | `icon` | `icon``prefixIcon` |
| 触发器 | `trigger` | `trigger` |
| 类名 | `className` | `className` |
| 样式对象 | `style` | `style` |
#### 组件引用 (Ref)
组件应支持 `classNames``styles` 属性,用于精细化样式定制:
```tsx
// classNames 属性类型定义
export type ComponentClassNamesType = {
root?: string;
icon?: string;
content?: string;
// ... 其他元素
};
// styles 属性类型定义
export type ComponentStylesType = {
root?: React.CSSProperties;
icon?: React.CSSProperties;
content?: React.CSSProperties;
// ... 其他元素
};
// 使用示例
<Button
classNames={{ root: 'custom-btn', icon: 'custom-icon' }}
styles={{ root: { width: 200 }, icon: { color: 'red' } }}
>
Button
</Button>;
```
#### 事件命名
@@ -164,27 +208,19 @@ interface ComponentRef {
}
```
#### 组件 Token 命名
格式:`variant (optional)` + `semantic part` + `semantic part variant (optional)` + `css property` + `size/disabled (optional)`
示例:
- `buttonPrimaryColor` - Button 主色
- `inputPaddingBlock` - Input 垂直内边距
- `menuItemActiveBg` - Menu 激活项背景色
### API 文档规范
#### API 表格格式
| Property | Description | Type | Default |
| --------- | --------------- | ------------------------------------------------------ | --------- |
| htmlType | Button 原生类型 | string | `button` |
| type | 按钮类型 | `primary` \| `default` \| `dashed` \| `link` \| `text` | `default` |
| disabled | 是否禁用 | boolean | false |
| minLength | 最小长度 | number | 0 |
| style | 自定义样式 | CSSProperties | - |
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| htmlType | Button 原生类型 | string | `button` | - |
| type | 按钮类型 | `primary` \| `default` \| `dashed` \| `link` \| `text` | `default` | - |
| disabled | 是否禁用 | boolean | false | - |
| minLength | 最小长度 | number | 0 | - |
| style | 自定义样式 | CSSProperties | - | - |
| classNames | 自定义类名 | ComponentClassNamesType | - | 5.0.0 |
| styles | 自定义内联样式 | ComponentStylesType | - | 5.0.0 |
#### API 文档要求
@@ -195,6 +231,7 @@ interface ComponentRef {
- ✅ 无默认值使用 `-`
- ✅ 描述首字母大写,结尾无句号
- ✅ API 按字母顺序排列
- ✅ 新增属性需要声明可用版本号,如 `5.0.0`
---
@@ -264,40 +301,117 @@ enum ButtonType {
## 样式规范
### 样式方案
### 样式架构
Ant Design 6.x 采用完整的 **CSS-in-JS** 架构,基于 `@ant-design/cssinjs` 实现:
- 使用 `@ant-design/cssinjs` 作为样式解决方案
- 每个组件的样式应该放在 `style/` 目录下
- 样式文件应该与组件结构保持一致
- 使用 CSS-in-JS 时应当注意性能影响,避免不必要的样式重计算
- 样式生成函数应遵循 `gen[ComponentName]Style` 的命名规范
- 样式覆盖应使用类选择器而非标签选择器,提高样式特异性
- 使用 `@ant-design/cssinjs-utils` 提供额外样式工具
- 支持动态样式和主题切换
- 样式独立注入,避免 CSS 污染
- 支持 Server-Side Rendering (SSR)
### 组件样式结构
每个组件的样式应该放在 `style/` 目录下,建议结构:
```
style/
├── index.ts # 样式钩生成器(导出点)
├── token.ts # 组件 token 定义
├── variant.ts # 变体样式solid/outlined/text 等)
├── compact.ts # 紧凑布局样式(如需要)
└── group.ts | 组合样式(如需要)
```
### 样式生成函数规范
```typescript
// 1. Token 准备函数
const prepareToken = (token: GlobalToken): ComponentToken => {
return mergeToken(token, {
// 组件特定 token
controlHeightLG: 40,
});
};
// 2. Component Token 准备函数
export const prepareComponentToken: GetDefaultToken<'ComponentName'> = (token) => ({
componentBg: token.colorBgContainer,
componentBorder: token.colorBorder,
// ...
});
// 3. 样式生成函数
const genComponentStyle: GenerateStyle<ComponentToken> = (token) => {
const { componentCls } = token;
return {
[componentCls]: {
// 基础样式
},
};
};
// 4. 导出样式钩子(使用 genStyleHooks
export default genStyleHooks(
'ComponentName', // 组件名称
(token) => [genComponentStyle(token)],
prepareComponentToken, // Component Token 准备函数
{
unitless: {
// 无单位 token
fontWeight: true,
},
},
);
```
### Token 系统
- 使用 Ant Design 的设计 Token 系统
使用 Ant Design 的 Design Token 系统
- 避免硬编码颜色、尺寸、间距等值
- 组件样式应基于全局 Token 和组件级 Token
- 自定义样式应尽可能使用现有的 Token,保持一致性
- 组件级 Token 命名规范:`Component` + 属性名,如 `buttonPrimaryColor`
- 对 Token 的修改应当向下传递,确保设计系统的一致性
- 自定义样式应尽可能使用现有的 Token
- 组件级 Token 命名规范:`Component` + `semantic part` + `css property`
- 使用 `mergeToken` 合并 token
- 使用 `calc` 处理 CSS 计算值
### Token 命名规范
格式:`variant (optional)` + `semantic part` + `semantic part variant (optional)` + `css property` + `size/disabled (optional)`
示例:
- `buttonPrimaryColor` - Button 主色
- `inputPaddingBlock` - Input 垂直内边距
- `menuItemActiveBg` - Menu 激活项背景色
### 响应式和主题支持
- ✅ 组件应支持不同屏幕尺寸下良好展示
- ✅ 组件应支持不同屏幕尺寸(使用 CSS 媒体查询)
- ✅ 所有组件必须支持暗色模式
- ✅ 组件应支持从右到左RTL的阅读方向
- ✅ 使用 CSS 逻辑属性(如 `margin-inline-start`)替代方向性属性(如 `margin-left`
- ✅ 组件应支持 RTL从右到左布局
- ✅ 使用 CSS 逻辑属性(如 `margin-inline-start`)替代方向性属性
- ✅ 支持通过 `ConfigProvider` 进行主题定制
- ✅ 使用 CSS 变量 (`cssVarCls`) 支持动态主题切换
### 动画效果
- 使用 CSS 过渡实现简单动画
- 复杂动画使用 `@rc-component/motion` 实现
- 尊重用户的减少动画设置(`prefers-reduced-motion`
- 动画时长和缓动函数应保持一致性
- 动画时长和缓动函数应使用 Token`motionDurationMid``motionEaseInOut`
- 动画不应干扰用户的操作和阅读体验
### CSS-in-JS 注意事项
- 样式生成函数应遵循 `gen[ComponentName]Style` 的命名规范
- 样式覆盖应使用类选择器而非标签选择器
- 避免在 render 过程中重复创建样式对象
- 使用 `hashId` 确保样式唯一性
- 使用 `cssVarCls` 支持 CSS 变量
### 可访问性样式
- 遵循 WCAG 2.1 AA 级别标准
@@ -309,32 +423,132 @@ enum ButtonType {
---
## 代码格式化
### 工具配置
项目使用多种代码格式化工具组合使用:
| 工具 | 用途 | 配置文件 |
| -------- | ------------------------ | ------------------- |
| Biome | 代码检查和格式化(主要) | `biome.json` |
| ESLint | 代码质量检查 | `eslint.config.mjs` |
| Prettier | 补充格式化 | `.prettierrc` |
### 格式化规范
配置文件:`biome.json``.prettierrc`
- **缩进**: 2 空格
- **行宽**: 100 字符
- **引号**: JavaScript 使用单引号JSX 属性使用双引号
- **尾随逗号**: 强制添加(`all`
- **分号**: 不强制使用
### 格式化命令
```bash
# 使用 Biome 格式化
npm run format
# 使用 Biome 检查
npm run lint:biome
# 使用 Prettier 格式化(补充)
npm run prettier
```
### 导入顺序
使用 `@ianvs/prettier-plugin-sort-imports` 插件自动排序导入:
```typescript
// 1. React 导入
import React, { forwardRef, useState } from 'react';
import RcComponent from '@rc-component/component';
// 2. 第三方库导入
import clsx from 'clsx';
// 3. Ant Design 内部导入
import { useToken } from '../theme/internal';
// 4. 相对路径导入
import { helperFunction } from './helpers';
// 5. 类型导入
import type { RefType } from './types';
// 6. 样式导入(如果有)
import './custom.css';
```
---
## 开发指南
### 测试指南
#### 测试框架和工具
- 使用 Jest React Testing Library 编写单元测试
- 对 UI 组件使用快照测试Snapshot Testing
- 使用 **Jest 30+****React Testing Library** 编写单元测试
- 使用 **jest-axe** 进行可访问性测试
- 使用 **jest-image-snapshot** 进行视觉回归测试
- 测试覆盖率要求 **100%**
- 测试文件放在 `tests/` 目录,命名格式为:`index.test.tsx``xxx.test.tsx`
- 测试文件放在组件目录下的 `__tests__/` 目录
#### 测试文件类型
| 测试类型 | 文件名 | 用途 |
| ------------- | ------------------------ | ------------------------- |
| 主测试 | `index.test.tsx` | 组件功能测试 |
| 无障碍测试 | `a11y.test.ts` | WCAG 可访问性标准测试 |
| 类型测试 | `type.test.tsx` | TypeScript 类型完整性测试 |
| Semantic 测试 | `demo-semantic.test.tsx` | Demo 语义化测试 |
| Demo 测试 | `demo.test.ts` | Demo 代码测试 |
#### 测试辅助函数
项目提供了多个测试辅助函数:
```typescript
// mountTest - 测试组件挂载/卸载
import mountTest from 'tests/shared/mountTest';
// rtlTest - 测试 RTL 布局渲染
import rtlTest from 'tests/shared/rtlTest';
mountTest(Button);
rtlTest(Button);
```
#### 运行测试
```bash
npm test # 运行所有测试
npm test -- --watch # 监听模式
npm run test:coverage # 生成覆盖率报告
npm run test:image # UI 快照测试(需要 Docker
# 运行单元测试
npm test
# 更新测试快照
npm run test:update
# 运行视觉回归测试(需要 Puppeteer/Docker
npm run test:image
# 运行所有测试套件
npm run test:all
# 运行 Node.js 环境测试
npm run test:node
# 运行站点文档测试
npm run test:site
```
#### 测试最佳实践
- ✅ 测试用户行为而非实现细节
- ✅ 使用有意义的测试描述
- ✅ 使用有意义的测试描述`describe``it`
- ✅ 每个测试用例应该独立,不依赖其他测试
- ✅ 测试边界情况和错误处理
- ✅ 组件应同时包含 `mountTest``rtlTest`
- ✅ 新增功能必须有对应的测试用例
- ✅ 使用 `toHaveBeenCalledTimes` 而非 `toHaveBeenCalledExactTimes`
### 演示代码规范
@@ -424,6 +638,147 @@ export function TestComp(props) {
}
```
### 组件开发模板
#### 标准组件目录结构
```
[component-name]/
├── ComponentName.tsx # 主组件实现
├── index.tsx # 导出入口
├── demo/ # 演示代码
│ ├── basic.tsx
│ └── basic.md
├── style/ # 样式系统
│ ├── index.ts
│ └── token.ts
├── __tests__/ # 测试文件
│ ├── index.test.tsx
│ └── a11y.test.ts
├── index.en-US.md # 英文文档
└── index.zh-CN.md # 中文文档
```
#### 主组件模板
```tsx
import React, { forwardRef, useContext, useRef } from 'react';
import { clsx } from 'clsx';
import { useComposeRef } from '../_util/hooks';
import { useComponentConfig } from '../_util/hooks/useComponentConfig';
import { devUseWarning } from '../_util/warning';
import { ConfigProviderContext } from '../../config-provider';
import useStyle from './style';
export interface ComponentNameProps {
// ... 其他 props
className?: string;
style?: React.CSSProperties;
classNames?: ComponentClassNames;
styles?: ComponentStyles;
}
export interface ComponentRef {
nativeElement: HTMLElement;
focus: VoidFunction;
blur: VoidFunction;
}
export type ComponentClassNames = {
root?: string;
// ...
};
export type ComponentStyles = {
root?: React.CSSProperties;
// ...
};
const InternalComponent = React.forwardRef<ComponentRef, ComponentNameProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
className,
style,
classNames,
styles,
...restProps
} = props;
const { getPrefixCls, direction } = useContext(ConfigProviderContext);
const componentConfig = useComponentConfig('ComponentName');
const prefixCls = getPrefixCls('component-name', customizePrefixCls);
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls);
const domRef = useRef<HTMLElement>(null);
const mergedRef = useComposeRef(ref, domRef);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning('ComponentName');
warning.deprecated(!deprecatedProp, 'deprecatedProp', 'newProp');
}
return wrapCSSVar(
<div
ref={mergedRef}
className={clsx(
prefixCls,
hashId,
cssVarCls,
className,
classNames?.root,
componentConfig.className,
)}
style={{ ...style, ...styles?.root, ...componentConfig.style }}
dir={direction}
{...restProps}
>
{/* 子内容 */}
</div>,
);
});
const Component = InternalComponent as typeof InternalComponent & {
displayName?: string;
};
if (process.env.NODE_ENV !== 'production') {
Component.displayName = 'ComponentName';
}
export default Component;
```
#### 样式模板
```typescript
// style/token.ts
import type { TokenType } from '../../theme/internal';
// style/index.ts
import { genStyleHooks } from '../../theme/internal';
import { prepareComponentToken } from './token';
export interface ComponentToken {
componentFontSize?: number;
componentPadding?: number;
}
export const prepareComponentToken: GetDefaultToken<'ComponentName'> = (token) => ({
componentFontSize: token.fontSize,
componentPadding: token.paddingXS,
});
const genComponentStyle: GenerateStyle<ComponentToken> = (token) => {
const { componentCls, fontSize, padding } = token;
return { [componentCls]: { fontSize, padding } };
};
export default genStyleHooks(
'ComponentName',
(token) => [genComponentStyle(token)],
prepareComponentToken,
);
```
---
## 文档和 Changelog 规范
@@ -514,6 +869,8 @@ export function TestComp(props) {
#### 💡 输出示例参考
需要同时提供中英文两个版本,格式如下:
**中文版**Emoji 在前、无冒号、每条含组件名、属性用反引号):
```markdown
@@ -668,17 +1025,27 @@ export function TestComp(props) {
### 开发工具
- 推荐使用 VS Code 或其他支持 TypeScript 的编辑器
- 配置 ESLint 和 Prettier
- 使用 TypeScript 严格模式
- 配置 Git hooks 进行代码检查
- **编辑器**: 推荐使用 VS Code 或其他支持 TypeScript 的编辑器
- **代码检查**: ESLint (@antfu/eslint-config) + Biome
- **格式化**: Biome + Prettier
- **类型检查**: TypeScript 5.9+ 严格模式
- **Git hooks**: Husky + lint-staged
### 构建工具
- 使用 webpack 进行构建
- 支持 ES modules 和 CommonJS
- 提供 UMD 格式的构建产物
- 支持按需加载
| 工具 | 用途 |
| ------- | ---------------------- |
| Father | 组件编译lib/es |
| Webpack | dist 构建和产物分析 |
| Dumi | 文档站点构建 |
| Mako | SSR 构建器(生产环境) |
### 构建产物
- **lib/**: CommonJS 格式
- **es/**: ES Modules 格式
- **dist/**: UMD 格式(包含 dist/antd.min.js
- **locale/**: 国际化配置
### CI/CD
@@ -686,6 +1053,113 @@ export function TestComp(props) {
- 包括单元测试、集成测试、类型检查、代码风格检查
- 自动化发布流程
- 支持多环境部署
- 支持视觉回归测试
### 相关配置文件
| 配置文件 | 说明 |
| ------------------- | ---------------- |
| `package.json` | 项目配置和脚本 |
| `tsconfig.json` | TypeScript 配置 |
| `eslint.config.mjs` | ESLint 配置 |
| `biome.json` | Biome 配置 |
| `.prettierrc` | Prettier 配置 |
| `.jest.js` | Jest 测试配置 |
| `.dumirc.ts` | Dumi 文档配置 |
| `webpack.config.js` | Webpack 构建配置 |
---
## 常见问题和故障排查
### 开发相关问题
#### 启动开发服务器失败
```bash
# 确认 Node.js 版本
node -v # 应该 >= 18
# 尝试清理 node_modules 和重新安装
rm -rf node_modules package-lock.json
npm install
# 重新生成版本信息
npm run version
```
#### 样式不生效
- 确保已运行 `npm run style` 生成样式文件
- 检查 `useStyle` hook 是否正确调用
- 确认 `hashId` 和 `cssVarCls` 是否正确应用到类名
#### TypeScript 类型错误
```bash
# 运行 TypeScript 类型检查
npm run tsc
# 清理构建产物后重新编译
npm run clean && npm run compile
```
### 测试相关问题
#### 快照测试失败
```bash
# 更新快照
npm run test:update
# 按组件更新快照
npm test -- --updateSnapshot components/button/__tests__
```
#### 视觉回归测试问题
```bash
# 本地运行视觉回归测试
npm run test:visual-regression:local
# 需要确保 Puppeteer 和相关依赖已正确安装
```
### 构建相关问题
#### 构建产物体积过大
```bash
# 运行包体积分析
npm run size-limit
# 检查是否有重复依赖包production 构建)
npm run dist
# 分析 bundle
ANALYZER=true npm run dist
```
#### Token 相关问题
```bash
# 重新生成 Token 元数据
npm run token:meta
# 收集 Token 统计
npm run token:statistic
# 重新构建样式
npm run style
```
### 国际化问题
#### 新增多语言配置
1. 在 `components/locale/` 下添加对应的语言文件
2. 更新 `components/locale/index.tsx` 的类型定义
3. 确保所有语言配置保持同步
---
@@ -695,3 +1169,6 @@ export function TestComp(props) {
- [#16048](https://github.com/ant-design/ant-design/issues/16048) - Current listing api & Chinese version
- [#25066](https://github.com/ant-design/ant-design/issues/25066) - API standard in the document
- [Development Guide](https://github.com/ant-design/ant-design/wiki/Development)
- [@ant-design/cssinjs](https://github.com/ant-design/cssinjs) - CSS-in-JS 解决方案
- [React 文档](https://react.dev)
- [TypeScript 文档](https://www.typescriptlang.org/docs/)

View File

@@ -187,7 +187,7 @@ $ npm start
</table>
<a href="https://openomy.app/github/ant-design/ant-design" target="_blank" style="display: block; width: 100%;" align="center">
<img src="https://openomy.app/svg?repo=ant-design/ant-design&chart=bubble&latestMonth=3" target="_blank" alt="Contribution Leaderboard" style="display: block; width: 100%;" />
<img src="https://openomy.app/svg?repo=ant-design/ant-design&chart=bubble&latestMonth=3" alt="Contribution Leaderboard" style="display: block; width: 100%;" />
</a>
请参考[贡献指南](https://ant.design/docs/react/contributing-cn).

View File

@@ -2,7 +2,7 @@
## Supported Versions
Use this section to tell people about which versions of your project are currently being supported with security updates.
Versions of ant-design that are currently supported with security updates:
| Version | Supported |
| ------- | ------------------ |
@@ -12,6 +12,14 @@ Use this section to tell people about which versions of your project are current
## Reporting a Vulnerability
Use this section to tell people how to report a vulnerability.
Security vulnerabilities in ant-design are handled by the ant-design team.
Tell them where to go, how often they can expect to get an update on a reported vulnerability, what to expect if the vulnerability is accepted or declined, etc.
### How to Report
If you find a security vulnerability, please **do not** open a public issue. Instead, please send an email to **security@ant.design**. Include details about:
- The affected version(s)
- Steps to reproduce the vulnerability
- Potential impact of the vulnerability
Our team will review your report and respond as soon as possible. We appreciate your help in reporting security issues responsibly.

View File

@@ -19,7 +19,7 @@ Please note that Affix should not cover other content on the page, especially wh
> Notes for developers
>
> After version `5.10.0`, we rewrite Affix use FC, Some methods of obtaining `ref` and calling internal instance methods will invalid.
> After version `5.10.0`, we rewrite Affix use FC, Some methods of obtaining `ref` and calling internal instance methods will be invalid.
## Examples

View File

@@ -4,35 +4,7 @@ exports[`renders components/back-top/demo/basic.tsx correctly 1`] = `
Array [
<div
class="css-var-test-id ant-back-top"
>
<div
class="ant-back-top-content"
>
<div
class="ant-back-top-icon"
>
<span
aria-label="vertical-align-top"
class="anticon anticon-vertical-align-top"
role="img"
>
<svg
aria-hidden="true"
data-icon="vertical-align-top"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M859.9 168H164.1c-4.5 0-8.1 3.6-8.1 8v60c0 4.4 3.6 8 8.1 8h695.8c4.5 0 8.1-3.6 8.1-8v-60c0-4.4-3.6-8-8.1-8zM518.3 355a8 8 0 00-12.6 0l-112 141.7a7.98 7.98 0 006.3 12.9h73.9V848c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V509.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 355z"
/>
</svg>
</span>
</div>
</div>
</div>,
/>,
Scroll down to see the bottom-right.,
]
`;

View File

@@ -3777,7 +3777,6 @@ exports[`renders components/color-picker/demo/line-gradient.tsx extend context c
class="ant-segmented-item"
>
<input
checked=""
class="ant-segmented-item-input"
name="test-id"
type="radio"
@@ -3793,6 +3792,7 @@ exports[`renders components/color-picker/demo/line-gradient.tsx extend context c
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
name="test-id"
type="radio"

View File

@@ -110,12 +110,10 @@ const ColorTrigger = forwardRef<HTMLDivElement, ColorTriggerProps>((props, ref)
prefixCls={prefixCls}
color={color.toCssString()}
className={classNames.body}
innerClassName={classNames.content}
style={styles.body}
innerStyle={styles.content}
/>
),
[color, prefixCls, classNames.body, classNames.content, styles.body, styles.content],
[color, prefixCls, classNames.body, styles.body],
);
return (

View File

@@ -59,7 +59,7 @@ Some components use dynamic style to support wave effect. You can config `csp` p
| getPopupContainer | To set the container of the popup element. The default is to create a `div` element in `body` | `(trigger?: HTMLElement) => HTMLElement \| ShadowRoot` | () => document.body | |
| getTargetContainer | Config Affix, Anchor scroll target container | `() => HTMLElement \| Window \| ShadowRoot` | () => window | 4.2.0 |
| iconPrefixCls | Set icon prefix className | string | `anticon` | 4.11.0 |
| locale | Language package setting, you can find the packages in [antd/locale](http://unpkg.com/antd/locale/) | object | - | |
| locale | Language package setting, you can find the packages in [antd/locale](https://unpkg.com/antd/locale/) | object | - | |
| popupMatchSelectWidth | Determine whether the dropdown menu and the select input are the same width. Default set `min-width` same as input. Will ignore when value less than select width. `false` will disable virtual scroll | boolean \| number | - | 5.5.0 |
| popupOverflow | Select like component popup logic. Can set to show in viewport or follow window scroll | 'viewport' \| 'scroll' <InlinePopover previewURL="https://user-images.githubusercontent.com/5378891/230344474-5b9f7e09-0a5d-49e8-bae8-7d2abed6c837.png"></InlinePopover> | 'viewport' | 5.5.0 |
| prefixCls | Set prefix className | string | `ant` | |

View File

@@ -60,7 +60,7 @@ export default Demo;
| getPopupContainer | 弹出框Select, Tooltip, Menu 等等)渲染父节点,默认渲染到 body 上。 | `(trigger?: HTMLElement) => HTMLElement \| ShadowRoot` | () => document.body | |
| getTargetContainer | 配置 Affix、Anchor 滚动监听容器。 | `() => HTMLElement \| Window \| ShadowRoot` | () => window | 4.2.0 |
| iconPrefixCls | 设置图标统一样式前缀 | string | `anticon` | 4.11.0 |
| locale | 语言包配置,语言包可到 [antd/locale](http://unpkg.com/antd/locale/) 目录下寻找 | object | - | |
| locale | 语言包配置,语言包可到 [antd/locale](https://unpkg.com/antd/locale/) 目录下寻找 | object | - | |
| popupMatchSelectWidth | 下拉菜单和选择器同宽。默认将设置 `min-width`,当值小于选择框宽度时会被忽略。`false` 时会关闭虚拟滚动 | boolean \| number | - | 5.5.0 |
| popupOverflow | Select 类组件弹层展示逻辑,默认为可视区域滚动,可配置成滚动区域滚动 | 'viewport' \| 'scroll' <InlinePopover previewURL="https://user-images.githubusercontent.com/5378891/230344474-5b9f7e09-0a5d-49e8-bae8-7d2abed6c837.png"></InlinePopover> | 'viewport' | 5.5.0 |
| prefixCls | 设置统一样式前缀 | string | `ant` | |

View File

@@ -317,34 +317,6 @@ Array [
</sup>
</span>
</button>
<button
class="ant-btn css-var-test-id ant-btn-circle ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg css-var-test-id ant-float-btn-css-var ant-float-btn ant-float-btn-default ant-float-btn-circle ant-float-btn-individual ant-float-btn-icon-only"
type="button"
>
<span
class="ant-btn-icon ant-float-btn-icon"
>
<span
aria-label="vertical-align-top"
class="anticon anticon-vertical-align-top"
role="img"
>
<svg
aria-hidden="true"
data-icon="vertical-align-top"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M859.9 168H164.1c-4.5 0-8.1 3.6-8.1 8v60c0 4.4 3.6 8 8.1 8h695.8c4.5 0 8.1-3.6 8.1-8v-60c0-4.4-3.6-8-8.1-8zM518.3 355a8 8 0 00-12.6 0l-112 141.7a7.98 7.98 0 006.3 12.9h73.9V848c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V509.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 355z"
/>
</svg>
</span>
</span>
</button>
</div>
</div>,
]
@@ -559,103 +531,6 @@ Array [
class="ant-float-btn-group css-var-test-id ant-float-btn-css-var ant-float-btn-group-individual ant-float-btn-group-top ant-float-btn-group-menu-mode"
style="inset-inline-end:24px"
>
<div
class="ant-float-btn-group-list ant-flex css-var-test-id ant-flex-align-stretch ant-flex-vertical"
>
<button
class="ant-btn css-var-test-id ant-btn-circle ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg css-var-test-id ant-float-btn-css-var ant-float-btn ant-float-btn-default ant-float-btn-circle ant-float-btn-individual ant-float-btn-icon-only"
type="button"
>
<span
class="ant-btn-icon ant-float-btn-icon"
>
<span
aria-label="file-text"
class="anticon anticon-file-text"
role="img"
>
<svg
aria-hidden="true"
data-icon="file-text"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494zM504 618H320c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h184c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM312 490v48c0 4.4 3.6 8 8 8h384c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H320c-4.4 0-8 3.6-8 8z"
/>
</svg>
</span>
</span>
</button>
<button
class="ant-btn css-var-test-id ant-btn-circle ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg css-var-test-id ant-float-btn-css-var ant-float-btn ant-float-btn-default ant-float-btn-circle ant-float-btn-individual ant-float-btn-icon-only"
type="button"
>
<span
class="ant-btn-icon ant-float-btn-icon"
>
<span
aria-label="file-text"
class="anticon anticon-file-text"
role="img"
>
<svg
aria-hidden="true"
data-icon="file-text"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494zM504 618H320c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h184c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM312 490v48c0 4.4 3.6 8 8 8h384c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H320c-4.4 0-8 3.6-8 8z"
/>
</svg>
</span>
</span>
</button>
<button
class="ant-btn css-var-test-id ant-btn-circle ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg css-var-test-id ant-float-btn-css-var ant-float-btn ant-float-btn-default ant-float-btn-circle ant-float-btn-individual ant-float-btn-icon-only"
type="button"
>
<span
class="ant-btn-icon ant-float-btn-icon"
>
<span
aria-label="comment"
class="anticon anticon-comment"
role="img"
>
<svg
aria-hidden="true"
data-icon="comment"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<defs>
<style />
</defs>
<path
d="M573 421c-23.1 0-41 17.9-41 40s17.9 40 41 40c21.1 0 39-17.9 39-40s-17.9-40-39-40zm-280 0c-23.1 0-41 17.9-41 40s17.9 40 41 40c21.1 0 39-17.9 39-40s-17.9-40-39-40z"
/>
<path
d="M894 345a343.92 343.92 0 00-189-130v.1c-17.1-19-36.4-36.5-58-52.1-163.7-119-393.5-82.7-513 81-96.3 133-92.2 311.9 6 439l.8 132.6c0 3.2.5 6.4 1.5 9.4a31.95 31.95 0 0040.1 20.9L309 806c33.5 11.9 68.1 18.7 102.5 20.6l-.5.4c89.1 64.9 205.9 84.4 313 49l127.1 41.4c3.2 1 6.5 1.6 9.9 1.6 17.7 0 32-14.3 32-32V753c88.1-119.6 90.4-284.9 1-408zM323 735l-12-5-99 31-1-104-8-9c-84.6-103.2-90.2-251.9-11-361 96.4-132.2 281.2-161.4 413-66 132.2 96.1 161.5 280.6 66 412-80.1 109.9-223.5 150.5-348 102zm505-17l-8 10 1 104-98-33-12 5c-56 20.8-115.7 22.5-171 7l-.2-.1A367.31 367.31 0 00729 676c76.4-105.3 88.8-237.6 44.4-350.4l.6.4c23 16.5 44.1 37.1 62 62 72.6 99.6 68.5 235.2-8 330z"
/>
<path
d="M433 421c-23.1 0-41 17.9-41 40s17.9 40 41 40c21.1 0 39-17.9 39-40s-17.9-40-39-40z"
/>
</svg>
</span>
</span>
</button>
</div>
<button
class="ant-btn css-var-test-id ant-btn-circle ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg css-var-test-id ant-float-btn-css-var ant-float-btn ant-float-btn-group-trigger ant-float-btn-default ant-float-btn-circle ant-float-btn-individual ant-float-btn-icon-only"
type="button"
@@ -690,103 +565,6 @@ Array [
class="ant-float-btn-group css-var-test-id ant-float-btn-css-var ant-float-btn-group-top ant-float-btn-group-menu-mode"
style="inset-inline-end:88px"
>
<div
class="ant-space-compact ant-space-compact-vertical ant-float-btn-group-list"
>
<button
class="ant-btn css-var-test-id ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg ant-btn-compact-vertical-item ant-btn-compact-vertical-first-item css-var-test-id ant-float-btn-css-var ant-float-btn ant-float-btn-default ant-float-btn-square ant-float-btn-icon-only"
type="button"
>
<span
class="ant-btn-icon ant-float-btn-icon"
>
<span
aria-label="file-text"
class="anticon anticon-file-text"
role="img"
>
<svg
aria-hidden="true"
data-icon="file-text"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494zM504 618H320c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h184c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM312 490v48c0 4.4 3.6 8 8 8h384c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H320c-4.4 0-8 3.6-8 8z"
/>
</svg>
</span>
</span>
</button>
<button
class="ant-btn css-var-test-id ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg ant-btn-compact-vertical-item css-var-test-id ant-float-btn-css-var ant-float-btn ant-float-btn-default ant-float-btn-square ant-float-btn-icon-only"
type="button"
>
<span
class="ant-btn-icon ant-float-btn-icon"
>
<span
aria-label="file-text"
class="anticon anticon-file-text"
role="img"
>
<svg
aria-hidden="true"
data-icon="file-text"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494zM504 618H320c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h184c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM312 490v48c0 4.4 3.6 8 8 8h384c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H320c-4.4 0-8 3.6-8 8z"
/>
</svg>
</span>
</span>
</button>
<button
class="ant-btn css-var-test-id ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg ant-btn-compact-vertical-item ant-btn-compact-vertical-last-item css-var-test-id ant-float-btn-css-var ant-float-btn ant-float-btn-default ant-float-btn-square ant-float-btn-icon-only"
type="button"
>
<span
class="ant-btn-icon ant-float-btn-icon"
>
<span
aria-label="comment"
class="anticon anticon-comment"
role="img"
>
<svg
aria-hidden="true"
data-icon="comment"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<defs>
<style />
</defs>
<path
d="M573 421c-23.1 0-41 17.9-41 40s17.9 40 41 40c21.1 0 39-17.9 39-40s-17.9-40-39-40zm-280 0c-23.1 0-41 17.9-41 40s17.9 40 41 40c21.1 0 39-17.9 39-40s-17.9-40-39-40z"
/>
<path
d="M894 345a343.92 343.92 0 00-189-130v.1c-17.1-19-36.4-36.5-58-52.1-163.7-119-393.5-82.7-513 81-96.3 133-92.2 311.9 6 439l.8 132.6c0 3.2.5 6.4 1.5 9.4a31.95 31.95 0 0040.1 20.9L309 806c33.5 11.9 68.1 18.7 102.5 20.6l-.5.4c89.1 64.9 205.9 84.4 313 49l127.1 41.4c3.2 1 6.5 1.6 9.9 1.6 17.7 0 32-14.3 32-32V753c88.1-119.6 90.4-284.9 1-408zM323 735l-12-5-99 31-1-104-8-9c-84.6-103.2-90.2-251.9-11-361 96.4-132.2 281.2-161.4 413-66 132.2 96.1 161.5 280.6 66 412-80.1 109.9-223.5 150.5-348 102zm505-17l-8 10 1 104-98-33-12 5c-56 20.8-115.7 22.5-171 7l-.2-.1A367.31 367.31 0 00729 676c76.4-105.3 88.8-237.6 44.4-350.4l.6.4c23 16.5 44.1 37.1 62 62 72.6 99.6 68.5 235.2-8 330z"
/>
<path
d="M433 421c-23.1 0-41 17.9-41 40s17.9 40 41 40c21.1 0 39-17.9 39-40s-17.9-40-39-40z"
/>
</svg>
</span>
</span>
</button>
</div>
<button
class="ant-btn css-var-test-id ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg css-var-test-id ant-float-btn-css-var ant-float-btn ant-float-btn-group-trigger ant-float-btn-default ant-float-btn-square ant-float-btn-individual ant-float-btn-icon-only"
type="button"
@@ -888,34 +666,6 @@ Array [
</span>
</span>
</button>
<button
class="ant-btn css-var-test-id ant-btn-circle ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg css-var-test-id ant-float-btn-css-var ant-float-btn ant-float-btn-default ant-float-btn-circle ant-float-btn-individual ant-float-btn-icon-only"
type="button"
>
<span
class="ant-btn-icon ant-float-btn-icon"
>
<span
aria-label="vertical-align-top"
class="anticon anticon-vertical-align-top"
role="img"
>
<svg
aria-hidden="true"
data-icon="vertical-align-top"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M859.9 168H164.1c-4.5 0-8.1 3.6-8.1 8v60c0 4.4 3.6 8 8.1 8h695.8c4.5 0 8.1-3.6 8.1-8v-60c0-4.4-3.6-8-8.1-8zM518.3 355a8 8 0 00-12.6 0l-112 141.7a7.98 7.98 0 006.3 12.9h73.9V848c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V509.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 355z"
/>
</svg>
</span>
</span>
</button>
</div>
</div>,
<div
@@ -1012,34 +762,6 @@ Array [
</span>
</span>
</button>
<button
class="ant-btn css-var-test-id ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg ant-btn-compact-vertical-item ant-btn-compact-vertical-last-item css-var-test-id ant-float-btn-css-var ant-float-btn ant-float-btn-default ant-float-btn-square ant-float-btn-icon-only"
type="button"
>
<span
class="ant-btn-icon ant-float-btn-icon"
>
<span
aria-label="vertical-align-top"
class="anticon anticon-vertical-align-top"
role="img"
>
<svg
aria-hidden="true"
data-icon="vertical-align-top"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M859.9 168H164.1c-4.5 0-8.1 3.6-8.1 8v60c0 4.4 3.6 8 8.1 8h695.8c4.5 0 8.1-3.6 8.1-8v-60c0-4.4-3.6-8-8.1-8zM518.3 355a8 8 0 00-12.6 0l-112 141.7a7.98 7.98 0 006.3 12.9h73.9V848c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V509.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 355z"
/>
</svg>
</span>
</span>
</button>
</div>
</div>,
]
@@ -1264,34 +986,6 @@ exports[`renders components/float-button/demo/render-panel.tsx correctly 1`] = `
<div
style="display:flex;column-gap:16px;align-items:center"
>
<button
class="ant-btn css-var-test-id ant-btn-circle ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg css-var-test-id ant-float-btn-css-var ant-float-btn ant-float-btn-pure ant-float-btn-default ant-float-btn-circle ant-float-btn-individual ant-float-btn-icon-only"
type="button"
>
<span
class="ant-btn-icon ant-float-btn-icon"
>
<span
aria-label="vertical-align-top"
class="anticon anticon-vertical-align-top"
role="img"
>
<svg
aria-hidden="true"
data-icon="vertical-align-top"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M859.9 168H164.1c-4.5 0-8.1 3.6-8.1 8v60c0 4.4 3.6 8 8.1 8h695.8c4.5 0 8.1-3.6 8.1-8v-60c0-4.4-3.6-8-8.1-8zM518.3 355a8 8 0 00-12.6 0l-112 141.7a7.98 7.98 0 006.3 12.9h73.9V848c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V509.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 355z"
/>
</svg>
</span>
</span>
</button>
<button
class="ant-btn css-var-test-id ant-btn-circle ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg css-var-test-id ant-float-btn-css-var ant-float-btn ant-float-btn-pure ant-float-btn-default ant-float-btn-circle ant-float-btn-individual ant-float-btn-icon-only"
type="button"
@@ -1454,97 +1148,6 @@ exports[`renders components/float-button/demo/render-panel.tsx correctly 1`] = `
<div
class="ant-float-btn-group css-var-test-id ant-float-btn-css-var ant-float-btn-pure ant-float-btn-group-individual ant-float-btn-group-top ant-float-btn-group-menu-mode"
>
<div
class="ant-float-btn-group-list ant-flex css-var-test-id ant-flex-align-stretch ant-flex-vertical"
>
<button
class="ant-btn css-var-test-id ant-btn-circle ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg css-var-test-id ant-float-btn-css-var ant-float-btn ant-float-btn-default ant-float-btn-circle ant-float-btn-individual ant-float-btn-icon-only"
type="button"
>
<span
class="ant-btn-icon ant-float-btn-icon"
>
<span
aria-label="question-circle"
class="anticon anticon-question-circle"
role="img"
>
<svg
aria-hidden="true"
data-icon="question-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
/>
<path
d="M623.6 316.7C593.6 290.4 554 276 512 276s-81.6 14.5-111.6 40.7C369.2 344 352 380.7 352 420v7.6c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V420c0-44.1 43.1-80 96-80s96 35.9 96 80c0 31.1-22 59.6-56.1 72.7-21.2 8.1-39.2 22.3-52.1 40.9-13.1 19-19.9 41.8-19.9 64.9V620c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8v-22.7a48.3 48.3 0 0130.9-44.8c59-22.7 97.1-74.7 97.1-132.5.1-39.3-17.1-76-48.3-103.3zM472 732a40 40 0 1080 0 40 40 0 10-80 0z"
/>
</svg>
</span>
</span>
</button>
<button
class="ant-btn css-var-test-id ant-btn-circle ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg css-var-test-id ant-float-btn-css-var ant-float-btn ant-float-btn-default ant-float-btn-circle ant-float-btn-individual ant-float-btn-icon-only"
type="button"
>
<span
class="ant-btn-icon ant-float-btn-icon"
>
<span
aria-label="customer-service"
class="anticon anticon-customer-service"
role="img"
>
<svg
aria-hidden="true"
data-icon="customer-service"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 128c-212.1 0-384 171.9-384 384v360c0 13.3 10.7 24 24 24h184c35.3 0 64-28.7 64-64V624c0-35.3-28.7-64-64-64H200v-48c0-172.3 139.7-312 312-312s312 139.7 312 312v48H688c-35.3 0-64 28.7-64 64v208c0 35.3 28.7 64 64 64h184c13.3 0 24-10.7 24-24V512c0-212.1-171.9-384-384-384zM328 632v192H200V632h128zm496 192H696V632h128v192z"
/>
</svg>
</span>
</span>
</button>
<button
class="ant-btn css-var-test-id ant-btn-circle ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg css-var-test-id ant-float-btn-css-var ant-float-btn ant-float-btn-default ant-float-btn-circle ant-float-btn-individual ant-float-btn-icon-only"
type="button"
>
<span
class="ant-btn-icon ant-float-btn-icon"
>
<span
aria-label="sync"
class="anticon anticon-sync"
role="img"
>
<svg
aria-hidden="true"
data-icon="sync"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M168 504.2c1-43.7 10-86.1 26.9-126 17.3-41 42.1-77.7 73.7-109.4S337 212.3 378 195c42.4-17.9 87.4-27 133.9-27s91.5 9.1 133.8 27A341.5 341.5 0 01755 268.8c9.9 9.9 19.2 20.4 27.8 31.4l-60.2 47a8 8 0 003 14.1l175.7 43c5 1.2 9.9-2.6 9.9-7.7l.8-180.9c0-6.7-7.7-10.5-12.9-6.3l-56.4 44.1C765.8 155.1 646.2 92 511.8 92 282.7 92 96.3 275.6 92 503.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8zm756 7.8h-60c-4.4 0-7.9 3.5-8 7.8-1 43.7-10 86.1-26.9 126-17.3 41-42.1 77.8-73.7 109.4A342.45 342.45 0 01512.1 856a342.24 342.24 0 01-243.2-100.8c-9.9-9.9-19.2-20.4-27.8-31.4l60.2-47a8 8 0 00-3-14.1l-175.7-43c-5-1.2-9.9 2.6-9.9 7.7l-.7 181c0 6.7 7.7 10.5 12.9 6.3l56.4-44.1C258.2 868.9 377.8 932 512.2 932c229.2 0 415.5-183.7 419.8-411.8a8 8 0 00-8-8.2z"
/>
</svg>
</span>
</span>
</button>
</div>
<button
class="ant-btn css-var-test-id ant-btn-circle ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg css-var-test-id ant-float-btn-css-var ant-float-btn ant-float-btn-group-trigger ant-float-btn-default ant-float-btn-circle ant-float-btn-individual ant-float-btn-icon-only"
type="button"

View File

@@ -55,7 +55,7 @@ Common props ref[Common props](/docs/react/common-props)
If the Ant Design grid layout component does not meet your needs, you can use the excellent layout components of the community:
- [react-flexbox-grid](http://roylee0704.github.io/react-flexbox-grid/)
- [react-flexbox-grid](https://roylee0704.github.io/react-flexbox-grid/)
- [react-blocks](https://github.com/whoisandy/react-blocks/)
### Row

View File

@@ -54,7 +54,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*DLUwQ4B2_zQAAA
Ant Design 的布局组件若不能满足你的需求,你也可以直接使用社区的优秀布局组件:
- [react-flexbox-grid](http://roylee0704.github.io/react-flexbox-grid/)
- [react-flexbox-grid](https://roylee0704.github.io/react-flexbox-grid/)
- [react-blocks](https://github.com/whoisandy/react-blocks/)
### Row

View File

@@ -1,7 +1,7 @@
## zh-CN
对于使用 [iconfont.cn](http://iconfont.cn/) 的用户,通过设置 `createFromIconfontCN` 方法参数对象中的 `scriptUrl` 字段, 即可轻松地使用已有项目中的图标。
对于使用 [iconfont.cn](https://iconfont.cn/) 的用户,通过设置 `createFromIconfontCN` 方法参数对象中的 `scriptUrl` 字段, 即可轻松地使用已有项目中的图标。
## en-US
If you are using [iconfont.cn](http://iconfont.cn/), you can use the icons in your project gracefully.
If you are using [iconfont.cn](https://iconfont.cn/), you can use the icons in your project gracefully.

View File

@@ -1,7 +1,7 @@
## zh-CN
`@ant-design/icons@4.1.0` 以后,`scriptUrl` 可引用多个资源,用户可灵活的管理 [iconfont.cn](http://iconfont.cn/) 图标。如果资源的图标出现重名,会按照数组顺序进行覆盖。
`@ant-design/icons@4.1.0` 以后,`scriptUrl` 可引用多个资源,用户可灵活的管理 [iconfont.cn](https://iconfont.cn/) 图标。如果资源的图标出现重名,会按照数组顺序进行覆盖。
## en-US
You can use `scriptUrl` as an array after `@ant-design/icons@4.1.0`, to manage icons in one `<Icon />` from multiple [iconfont.cn](http://iconfont.cn/) resources. If an icon with a duplicate name is in resources, it will be overridden in array order.
You can use `scriptUrl` as an array after `@ant-design/icons@4.1.0`, to manage icons in one `<Icon />` from multiple [iconfont.cn](https://iconfont.cn/) resources. If an icon with a duplicate name is in resources, it will be overridden in array order.

View File

@@ -104,9 +104,9 @@ getTwoToneColor(); // #eb2f96
### Custom Font Icon
We added a `createFromIconfontCN` function to help developer use their own icons deployed at [iconfont.cn](http://iconfont.cn/) in a convenient way.
We added a `createFromIconfontCN` function to help developer use their own icons deployed at [iconfont.cn](https://iconfont.cn/) in a convenient way.
> This method is specified for [iconfont.cn](http://iconfont.cn/).
> This method is specified for [iconfont.cn](https://iconfont.cn/).
```jsx
import React from 'react';
@@ -127,11 +127,11 @@ The following options are available:
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| extraCommonProps | Define extra properties to the component | { \[key: string]: any } | {} | |
| scriptUrl | The URL generated by [iconfont.cn](http://iconfont.cn/) project. Support `string[]` after `@ant-design/icons@4.1.0` | string \| string\[] | - | |
| scriptUrl | The URL generated by [iconfont.cn](https://iconfont.cn/) project. Support `string[]` after `@ant-design/icons@4.1.0` | string \| string\[] | - | |
The property `scriptUrl` should be set to import the SVG sprite symbols.
See [iconfont.cn documents](http://iconfont.cn/help/detail?spm=a313x.7781069.1998910419.15&helptype=code) to learn about how to generate `scriptUrl`.
See [iconfont.cn documents](https://iconfont.cn/help/detail?spm=a313x.7781069.1998910419.15&helptype=code) to learn about how to generate `scriptUrl`.
### Custom SVG Icon

View File

@@ -101,7 +101,7 @@ getTwoToneColor(); // #eb2f96
### 自定义 font 图标 {#custom-font-icon}
`3.9.0` 之后,我们提供了一个 `createFromIconfontCN` 方法,方便开发者调用在 [iconfont.cn](http://iconfont.cn/) 上自行管理的图标。
`3.9.0` 之后,我们提供了一个 `createFromIconfontCN` 方法,方便开发者调用在 [iconfont.cn](https://iconfont.cn/) 上自行管理的图标。
```jsx
import React from 'react';
@@ -122,11 +122,11 @@ options 的配置项如下:
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| extraCommonProps | 给所有的 `svg` 图标 `<Icon />` 组件设置额外的属性 | { \[key: string]: any } | {} | |
| scriptUrl | [iconfont.cn](http://iconfont.cn/) 项目在线生成的 js 地址,`@ant-design/icons@4.1.0` 之后支持 `string[]` 类型 | string \| string\[] | - | |
| scriptUrl | [iconfont.cn](https://iconfont.cn/) 项目在线生成的 js 地址,`@ant-design/icons@4.1.0` 之后支持 `string[]` 类型 | string \| string\[] | - | |
`scriptUrl` 都设置有效的情况下,组件在渲染前会自动引入 [iconfont.cn](http://iconfont.cn/) 项目中的图标符号集,无需手动引入。
`scriptUrl` 都设置有效的情况下,组件在渲染前会自动引入 [iconfont.cn](https://iconfont.cn/) 项目中的图标符号集,无需手动引入。
见 [iconfont.cn 使用帮助](http://iconfont.cn/help/detail?spm=a313x.7781069.1998910419.15&helptype=code) 查看如何生成 js 地址。
见 [iconfont.cn 使用帮助](https://iconfont.cn/help/detail?spm=a313x.7781069.1998910419.15&helptype=code) 查看如何生成 js 地址。
### 自定义 SVG 图标 {#custom-svg-icon}

View File

@@ -195,7 +195,7 @@ Added in `5.16.0`.
## FAQ
### Why Input lose focus when change `prefix/suffix/showCount` {#faq-lose-focus}
### Why Input loses focus when change `prefix/suffix/showCount` {#faq-lose-focus}
When Input dynamic add or remove `prefix/suffix/showCount` will make React recreate the dom structure and new input will be not focused. You can set an empty `<span />` element to keep the dom structure:

View File

@@ -58,7 +58,7 @@ Common props ref[Common props](/docs/react/common-props)
| showTitle | Show page item's title | boolean | true | |
| showTotal | To display the total number and range | function(total, range) | - | |
| simple | Whether to use simple mode | boolean \| { readOnly?: boolean } | - | |
| size | Component size | `large` \| `middle` \| `small` | `middle` | |
| size | Component size | `large` \| `medium` \| `small` | `medium` | |
| styles | Customize inline style for each semantic structure inside the component. Supports object or function | Record<[SemanticDOM](#semantic-dom), CSSProperties> \| (info: { props }) => Record<[SemanticDOM](#semantic-dom), CSSProperties> | - | |
| total | Total number of data items | number | 0 | |
| onChange | Called when the page number or `pageSize` is changed, and it takes the resulting page number and pageSize as its arguments | function(page, pageSize) | - | |

View File

@@ -59,7 +59,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*WM86SrBC8TsAAA
| showTitle | 是否显示原生 tooltip 页码提示 | boolean | true | |
| showTotal | 用于显示数据总量和当前数据顺序 | function(total, range) | - | |
| simple | 当添加该属性时,显示为简单分页 | boolean \| { readOnly?: boolean } | - | |
| size | 组件尺寸 | `large` \| `middle` \| `small` | `middle` | |
| size | 组件尺寸 | `large` \| `medium` \| `small` | `medium` | |
| styles | 自定义组件内部各语义化结构的内联样式。支持对象或函数 | Record<[SemanticDOM](#semantic-dom), CSSProperties> \| (info: { props }) => Record<[SemanticDOM](#semantic-dom), CSSProperties> | - | |
| total | 数据总数 | number | 0 | |
| onChange | 页码或 `pageSize` 改变的回调,参数是改变后的页码及每页条数 | function(page, pageSize) | - | |

View File

@@ -297,6 +297,10 @@ const genSelectInputStyle: GenerateStyle<SelectToken> = (token) => {
[`&${componentCls}-open ${componentCls}-content`]: {
color: token.colorTextPlaceholder,
'&-has-search-value': {
color: 'transparent',
},
},
},
},

View File

@@ -56,7 +56,7 @@ dayjs.extend(customParseFormat)
| changeOnScroll | Trigger selection when scroll the column | boolean | false | 5.14.0 |
| className | The className of picker | string | - | |
| classNames | Customize class for each semantic structure inside the component. Supports object or function. | Record<[SemanticDOM](#semantic-dom), string> \| (info: { props })=> Record<[SemanticDOM](#semantic-dom), string> | - | |
| defaultValue | To set default time | [dayjs](http://day.js.org/) | - | |
| defaultValue | To set default time | [dayjs](https://day.js.org/) | - | |
| disabled | Determine whether the TimePicker is disabled | boolean | false | |
| disabledTime | To specify the time that cannot be selected | [DisabledTime](#disabledtime) | - | 4.19.0 |
| format | To set the time format | string | `HH:mm:ss` | |
@@ -81,7 +81,7 @@ dayjs.extend(customParseFormat)
| styles | Customize inline style for each semantic structure inside the component. Supports object or function. | Record<[SemanticDOM](#semantic-dom), CSSProperties> \| (info: { props })=> Record<[SemanticDOM](#semantic-dom), CSSProperties> | - | |
| suffixIcon | The custom suffix icon | ReactNode | - | |
| use12Hours | Display as 12 hours format, with default format `h:mm:ss a` | boolean | false | |
| value | To set time | [dayjs](http://day.js.org/) | - | |
| value | To set time | [dayjs](https://day.js.org/) | - | |
| variant | Variants of picker | `outlined` \| `borderless` \| `filled` \| `underlined` | `outlined` | 5.13.0 \| `underlined`: 5.24.0 |
| onCalendarChange | Callback function, can be executed when the start time or the end time of the range is changing. `info` argument is added in 4.4.0 | function(dates: \[dayjs, dayjs], dateStrings: \[string, string], info: { range:`start`\|`end` }) | - | |
| onChange | A callback function, can be executed when the selected time is changing | function(time: dayjs, timeString: string): void | - | |

View File

@@ -11,7 +11,7 @@ This is due to the implementation of `@rc-component/trigger`. `@rc-component/tri
Similar issues: [#15909](https://github.com/ant-design/ant-design/issues/15909), [#12812](https://github.com/ant-design/ant-design/issues/12812).
Please ensure that the child node to accept `onMouseEnter`, `onMouseLeave`, `onPointerEnter`, `onPointerLeave`, `onFocus`, and `onClick` events, If you create your own component and do not explicitly add these mouse and pointer events as props, the tooltip will never appear. [See Example](http://ant.design/components/tooltip#tooltip-demo-wrap-custom-component).
Please ensure that the child node to accept `onMouseEnter`, `onMouseLeave`, `onPointerEnter`, `onPointerLeave`, `onFocus`, and `onClick` events, If you create your own component and do not explicitly add these mouse and pointer events as props, the tooltip will never appear. [See Example](/components/tooltip#tooltip-demo-wrap-custom-component).
### What's the placement logic?

View File

@@ -141,7 +141,7 @@ const genPictureCardStyle: GenerateStyle<UploadToken> = (token) => {
[`${listCls}${listCls}-picture-card, ${listCls}${listCls}-picture-circle`]: {
display: 'flex',
flexWrap: 'wrap',
height: uploadPictureCardSize,
minHeight: uploadPictureCardSize,
'@supports not (gap: 1px)': {
'& > *': {

View File

@@ -78,11 +78,11 @@ Runs Ant Design website locally.
<InstallDependencies npm='$ npm start' yarn='$ yarn start'></InstallDependencies>
### Checks the code style
### Check the code style
<InstallDependencies npm='$ npm run lint' yarn='$ yarn lint'></InstallDependencies>
### Run test
### Run tests
runs the complete test suite. (Make sure the `NODE_ENV` environment variable is unset, or it may causing some problems.)

View File

@@ -88,7 +88,7 @@ If you encounter build errors during the upgrade, please verify that your `@ant-
- `Breadcrumb`
- `routes` is deprecated and replaced by `items`.
- `Breadcrumb.Item` `Breadcrumb.Separator` is deprecated and replaced by `items`.
- `Breadcrumb.Item` and `Breadcrumb.Separator` are deprecated and replaced by `items`.
- `Button.Group`
- `Button.Group` is deprecated and replaced by `Space.Compact`.

View File

@@ -126,7 +126,7 @@
"@rc-component/input-number": "~1.6.2",
"@rc-component/mentions": "~1.6.0",
"@rc-component/menu": "~1.2.0",
"@rc-component/motion": "~1.1.6",
"@rc-component/motion": "^1.3.1",
"@rc-component/mutate-observer": "^2.0.1",
"@rc-component/notification": "~1.2.0",
"@rc-component/pagination": "~1.2.0",
@@ -173,7 +173,7 @@
"@emotion/css": "^11.13.5",
"@emotion/react": "^11.14.0",
"@emotion/server": "^11.11.0",
"@eslint-react/eslint-plugin": "2.12.2",
"@eslint-react/eslint-plugin": "2.12.4",
"@ianvs/prettier-plugin-sort-imports": "^4.7.0",
"@inquirer/prompts": "^8.0.2",
"@madccc/duplicate-package-checker-webpack-plugin": "^1.0.0",