chore: merge next

This commit is contained in:
二货机器人 2025-06-03 20:07:06 +08:00
commit 432570d252
232 changed files with 4427 additions and 1645 deletions

View File

@ -18,3 +18,119 @@ html {
scrollbar-width: thin;
scrollbar-color: #eaeaea transparent;
}
.rc-footer {
position: relative;
clear: both;
color: rgba(255, 255, 255, 0.4);
font-size: 14px;
line-height: 1.5;
background-color: #000;
}
.rc-footer a {
color: rgba(255, 255, 255, 0.9);
text-decoration: none;
transition: all 0.3s;
}
.rc-footer a:hover {
color: #40a9ff;
}
.rc-footer-container {
width: 100%;
max-width: 1200px;
margin: auto;
padding: 80px 0 20px;
}
.rc-footer-columns {
display: flex;
justify-content: space-around;
}
.rc-footer-column {
margin-bottom: 60px;
}
.rc-footer-column h2 {
position: relative;
margin: 0 auto;
color: #fff;
font-weight: 500;
font-size: 16px;
}
.rc-footer-column-icon {
position: relative;
top: -1px;
display: inline-block;
width: 22px;
text-align: center;
vertical-align: middle;
margin-inline-end: 0.5em;
}
.rc-footer-column-icon > span,
.rc-footer-column-icon > svg,
.rc-footer-column-icon img {
display: block;
width: 100%;
}
.rc-footer-item {
margin: 12px 0;
}
.rc-footer-item-icon {
position: relative;
top: -1px;
display: inline-block;
width: 16px;
text-align: center;
vertical-align: middle;
margin-inline-end: 0.4em;
}
.rc-footer-item-icon > span,
.rc-footer-item-icon > svg,
.rc-footer-item-icon img {
display: block;
width: 100%;
}
.rc-footer-item-separator {
margin: 0 0.3em;
}
.rc-footer-bottom-container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 16px 0;
font-size: 16px;
line-height: 32px;
text-align: center;
border-top: 1px solid rgba(255, 255, 255, 0.25);
}
.rc-footer-light {
color: rgba(0, 0, 0, 0.85);
background-color: transparent;
}
.rc-footer-light h2,
.rc-footer-light a {
color: rgba(0, 0, 0, 0.85);
}
.rc-footer-light .rc-footer-bottom-container {
border-top-color: #e8e8e8;
}
.rc-footer-light .rc-footer-item-separator,
.rc-footer-light .rc-footer-item-description {
color: rgba(0, 0, 0, 0.45);
}
@media only screen and (max-width: 767.99px) {
.rc-footer {
text-align: center;
}
.rc-footer-container {
padding: 40px 0;
}
.rc-footer-columns {
display: block;
}
.rc-footer-column {
display: block;
margin-bottom: 40px;
}
.rc-footer-column:last-child {
margin-bottom: 0;
}
}

89
.dumi/rehypeChangelog.ts Normal file
View File

@ -0,0 +1,89 @@
import type { UnifiedTransformer } from 'dumi';
import { unistUtilVisit } from 'dumi';
import set from 'lodash/set';
let hastToString: typeof import('hast-util-to-string').toString;
// workaround to import pure esm module
(async () => {
({ toString: hastToString } = await import('hast-util-to-string'));
})();
const COMPONENT_NAME = 'RefinedChangelog';
function rehypeChangelog(): UnifiedTransformer<any> {
return (tree, vFile) => {
const { filename } = vFile.data.frontmatter as any;
// 只处理 changelog 文件
if (!/^changelog\.\S+\.md$/i.test(filename)) return;
const nodesToWrap: { parent: any; startIdx: number }[] = [];
const WRAPPER_FLAG = 'data-changelog-wrapped'; // 包裹容器唯一标识
unistUtilVisit.visit(tree, 'element', (node, idx, parent) => {
if (node.properties?.[WRAPPER_FLAG]) return unistUtilVisit.SKIP;
if (
idx !== undefined &&
parent &&
idx! + 2 < parent.children.length &&
node.tagName === 'h2' &&
parent.children[idx! + 1].tagName === 'p' &&
parent.children[idx! + 2].tagName === 'ul'
) {
nodesToWrap.push({ parent, startIdx: idx! });
}
});
nodesToWrap.reverse().forEach(({ parent, startIdx }) => {
const [heading, date, list] = parent.children.splice(startIdx, 3);
const version = hastToString(heading);
const dateStr = hastToString(date);
const headingWrap = {
type: 'element',
tagName: `${COMPONENT_NAME}.Version`,
// 为标签添加语义化 className (下面同理)
children: [set(heading, 'properties.className', 'changelog-version')],
};
const dateWrap = {
type: 'element',
tagName: `${COMPONENT_NAME}.Date`,
children: [set(date, 'properties.className', 'changelog-date')],
};
const listWrap = {
type: 'element',
tagName: `${COMPONENT_NAME}.Details`,
children: [set(list, 'properties.className', 'changelog-details')],
};
const wrapper = {
type: 'element',
tagName: COMPONENT_NAME,
properties: {
[WRAPPER_FLAG]: true,
},
JSXAttributes: [
{
type: 'JSXAttribute',
name: 'version',
value: JSON.stringify(version),
},
{
type: 'JSXAttribute',
name: 'date',
value: JSON.stringify(dateStr),
},
],
children: [headingWrap, dateWrap, listWrap],
};
parent.children.splice(startIdx, 0, wrapper);
});
};
}
export default rehypeChangelog;

View File

@ -1,176 +0,0 @@
(function createMirrorModal() {
if (
(navigator.languages.includes('zh') || navigator.languages.includes('zh-CN')) &&
/-cn\/?$/.test(window.location.pathname) &&
!['ant-design.gitee.io', 'ant-design.antgroup.com'].includes(window.location.hostname) &&
!window.location.host.includes('surge') &&
window.location.hostname !== 'localhost'
) {
const ANTD_DOT_NOT_SHOW_MIRROR_MODAL = 'ANT_DESIGN_DO_NOT_OPEN_MIRROR_MODAL';
const lastShowTime = window.localStorage.getItem(ANTD_DOT_NOT_SHOW_MIRROR_MODAL);
if (
lastShowTime &&
lastShowTime !== 'true' &&
Date.now() - new Date(lastShowTime).getTime() < 7 * 24 * 60 * 60 * 1000
) {
return;
}
const style = document.createElement('style');
style.innerHTML = `
@keyframes mirror-fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes mirror-zoom-in {
from {
transform: scale(0.8);
}
to {
transform: scale(1);
}
}
.mirror-modal-mask {
position: fixed;
inset: 0;
height: 100vh;
width: 100vw;
background: rgba(0, 0, 0, 0.3);
z-index: 9999;
animation: mirror-fade-in 0.3s forwards;
}
.mirror-modal-dialog {
position: fixed;
top: 120px;
inset-inline-start: 0;
inset-inline-end: 0;
margin: 0 auto;
width: 420px;
display: flex;
align-items: center;
flex-direction: column;
border-radius: 8px;
border: 1px solid #eee;
background: #fff;
padding: 20px 24px;
box-shadow: 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
animation: mirror-zoom-in 0.3s forwards;
box-sizing: border-box;
max-width: 100vw;
z-index: 9999;
}
.mirror-modal-title {
font-size: 16px;
font-weight: 500;
align-self: flex-start;
margin-bottom: 8px;
}
.mirror-modal-content {
font-size: 14px;
align-self: flex-start;
margin-bottom: 24px;
}
.mirror-modal-btns {
align-self: flex-end;
margin-top: auto;
display: flex;
align-items: center;
}
.mirror-modal-btn {
border-radius: 6px;
cursor: pointer;
height: 32px;
box-sizing: border-box;
font-size: 14px;
padding: 4px 16px;
display: inline-flex;
align-items: center;
text-decoration: none;
transition: all 0.2s;
}
.mirror-modal-confirm-btn {
background: #1677ff;
color: #fff;
}
.mirror-modal-confirm-btn:hover {
background: #4096ff;
}
.mirror-modal-confirm-btn:active {
background: #0958d9;
}
.mirror-modal-cancel-btn {
border: 1px solid #eee;
color: #000;
margin-inline-end: 8px;
}
.mirror-modal-cancel-btn:hover {
border-color: #4096ff;
color: #4096ff
}
.mirror-modal-cancel-btn:active {
border-color: #0958d9;
color: #0958d9;
}
`;
document.head.append(style);
const modal = document.createElement('div');
modal.className = 'mirror-modal-mask';
const dialog = document.createElement('div');
dialog.className = 'mirror-modal-dialog';
modal.append(dialog);
const title = document.createElement('div');
title.className = 'mirror-modal-title';
title.textContent = '提示';
dialog.append(title);
const content = document.createElement('div');
content.className = 'mirror-modal-content';
content.textContent = '🚀 国内用户推荐访问国内镜像以获得极速体验~';
dialog.append(content);
const btnWrapper = document.createElement('div');
btnWrapper.className = 'mirror-modal-btns';
dialog.append(btnWrapper);
const cancelBtn = document.createElement('a');
cancelBtn.className = 'mirror-modal-cancel-btn mirror-modal-btn';
cancelBtn.textContent = '7 天内不再显示';
btnWrapper.append(cancelBtn);
cancelBtn.addEventListener('click', () => {
window.localStorage.setItem(ANTD_DOT_NOT_SHOW_MIRROR_MODAL, new Date().toISOString());
document.body.removeChild(modal);
document.head.removeChild(style);
document.body.style.overflow = '';
});
const confirmBtn = document.createElement('a');
confirmBtn.className = 'mirror-modal-confirm-btn mirror-modal-btn';
confirmBtn.href = window.location.href.replace(window.location.host, 'ant-design.antgroup.com');
confirmBtn.textContent = '🚀 立刻前往';
btnWrapper.append(confirmBtn);
document.body.append(modal);
document.body.style.overflow = 'hidden';
}
})();

View File

@ -0,0 +1,230 @@
(function createMirrorModal() {
const SIGN = Symbol.for('antd.mirror-notify');
const always = window.localStorage.getItem('DEBUG') === 'antd';
const officialChinaMirror = 'https://ant-design.antgroup.com';
const enabledCondition = [
// Check if the browser language is Chinese
navigator.languages.includes('zh') || navigator.languages.includes('zh-CN'),
// Check if the URL path ends with -cn
/-cn\/?$/.test(window.location.pathname),
// chinese mirror URL
!['ant-design.gitee.io', new URL(officialChinaMirror).hostname].includes(
window.location.hostname,
),
// PR review URL
!window.location.host.includes('surge'),
// development mode
!['127.0.0.1', 'localhost'].includes(window.location.hostname),
];
const isEnabled = always || enabledCondition.every(Boolean);
if (!isEnabled) return;
const prefixCls = 'antd-mirror-notify';
const primaryColor = '#1677ff';
function insertCss() {
const style = document.createElement('style');
style.innerHTML = `
@keyframes slideInRight {
from {
transform: translate3d(100%, 0, 0);
visibility: visible;
}
to {
transform: translate3d(0, 0, 0);
}
}
.${prefixCls} {
position: fixed;
inset-inline-end: 12px;
inset-block-start: 12px;
z-index: 9999;
width: 360px;
background-color: #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
border-radius: 4px;
overflow: hidden;
animation: slideInRight 0.3s ease-in-out;
}
.${prefixCls}-content {
padding: 16px;
}
.${prefixCls}-content a {
color: ${primaryColor};
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
.${prefixCls}-title {
font-size: 16px;
font-weight: bold;
margin-block-end: 8px;
}
.${prefixCls}-message {
font-size: 14px;
color: #555;
line-height: 1.57;
}
.${prefixCls}-footer {
display: none;
margin-block-start: 16px;
justify-content: flex-end;
}
.${prefixCls}-progress {
position: relative;
inset-inline-end: 0;
width: 100%;
height: 4px;
background-color: #f0f0f0;
border-radius: 2px;
overflow: hidden;
}
.${prefixCls}-progress::after {
content: '';
position: absolute;
top: 0;
left: 0;
height: 100%;
width: var(--progress, 0%);
background-color: ${primaryColor};
transition: width 0.05s linear; /* Adjusted for smoother animation matching refreshRate */
}
.${prefixCls}-close {
all: unset;
position: absolute;
inset-inline-end: 2px;
inset-block-start: 2px;
width: 32px;
height: 32px;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
color: #999;
cursor: pointer;
}
.${prefixCls}-close:hover {
color: #333;
}
.${prefixCls}-action {
all: unset;
display: inline-block;
padding: 4px 8px;
background-color: ${primaryColor};
color: #fff;
border-radius: 4px;
text-align: center;
cursor: pointer;
font-size: 14px;
}
`;
document.head.append(style);
}
function createNotification() {
insertCss();
const notify = document.createElement('div');
notify.className = `${prefixCls} slideInRight`;
notify.innerHTML = `
<div class="${prefixCls}-content">
<div class="${prefixCls}-title">🇨🇳 访问不畅试试国内镜像</div>
<div class="${prefixCls}-message">
国内镜像站点可以帮助您更快地访问文档和资源<br>
请尝试访问 <a href="${officialChinaMirror}">国内镜像站点</a>
</div>
<div class="${prefixCls}-footer">
<button class="${prefixCls}-action">🚀 立即前往</button>
</div>
</div>
<button class="${prefixCls}-close">X</button>
<div class="${prefixCls}-progress" style="--progress: 100%;"></div>
`;
document.body.appendChild(notify);
notify.querySelector(`.${prefixCls}-close`).addEventListener('click', () => {
removeNotify();
});
notify.querySelector(`.${prefixCls}-action`).addEventListener('click', () => {
window.location.href = officialChinaMirror;
removeNotify();
});
const refreshRate = 50; // ms
const duration = 10; // s
const step = 100 / ((duration * 1000) / refreshRate);
let progressInterval = -1;
function removeNotify() {
clearInterval(progressInterval);
notify.remove();
}
const progressEl = notify.querySelector(`.${prefixCls}-progress`);
let currentProgressValue = 100;
const progress = {
get value() {
return currentProgressValue;
},
set value(val) {
currentProgressValue = Math.max(0, Math.min(100, val));
progressEl.style.setProperty('--progress', `${currentProgressValue}%`);
},
};
function startProgressTimer() {
if (progressInterval !== -1) {
clearInterval(progressInterval);
}
progressInterval = setInterval(() => {
if (progress.value <= 0) {
removeNotify();
} else {
progress.value -= step;
}
}, refreshRate);
}
startProgressTimer();
notify.addEventListener('mouseenter', () => {
clearInterval(progressInterval);
});
notify.addEventListener('mouseleave', () => {
startProgressTimer();
});
}
// 断定网络不畅阈值(秒)
const delayDuration = 3;
const reactTimeoutId = setTimeout(() => {
if (typeof window[SIGN]?.YES === 'undefined') {
console.error(
`antd.mirror-notify: 页面加载超过 ${delayDuration} 秒,可能是网络不畅。\n请尝试访问国内镜像站点。%c${officialChinaMirror}`,
`color: ${primaryColor}; font-weight: bold;`,
);
createNotification();
}
}, delayDuration * 1000);
// 交给 React effect 清理
window[SIGN] = function stopMirrorNotify() {
window[SIGN].YES = Date.now();
clearTimeout(reactTimeoutId);
};
})();

View File

@ -19,25 +19,23 @@ const Container: React.FC<React.PropsWithChildren<ContainerProps>> = ({
const { styles, cx } = useStyles();
return (
<div data-type={type} className={styles.container}>
<Alert
showIcon
type={type}
title={title || type.toUpperCase()}
description={
<div
className={cx(
styles.desc,
// 为了让 markdown 的样式生效,需要在这里添加一个额外的 class
'markdown',
)}
>
{children}
</div>
}
className={styles.alert}
/>
</div>
<Alert
showIcon
type={type}
title={title || type.toUpperCase()}
description={
<div
className={cx(
styles.desc,
// 为了让 markdown 的样式生效,需要在这里添加一个额外的 class
'markdown',
)}
>
{children}
</div>
}
className={styles.alert}
/>
);
};

View File

@ -1,11 +1,8 @@
import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token, prefixCls, css }) => ({
container: css`
margin: ${token.marginXS}px 0;
`,
const useStyles = createStyles(({ prefixCls, css }) => ({
alert: css`
padding: 12px 16px;
.${prefixCls}-alert-message {
font-weight: bold;
}

View File

@ -1,41 +1,8 @@
import React, { Suspense, useEffect, useState } from 'react';
import { Tooltip } from 'antd';
import React, { useState } from 'react';
import { Tooltip, App } from 'antd';
import { LoadingOutlined } from '@ant-design/icons';
import { FormattedMessage } from 'dumi';
const codeBlockJs =
'https://renderoffice.a' +
'lipay' +
'objects.com/p' +
'/yuyan/180020010001206410/parseFileData-v1.0.1.js';
function useShowCodeBlockButton() {
const [showCodeBlockButton, setShowCodeBlockButton] = useState(false);
useEffect(() => {
const scriptId = 'hitu-code-block-js';
const existScript = document.getElementById(scriptId) as HTMLScriptElement | null;
if (existScript) {
// 如果已标记 loaded直接显示按钮
if (existScript.dataset.loaded) {
setShowCodeBlockButton(true);
}
return;
}
const script = document.createElement('script');
script.src = codeBlockJs;
script.async = true;
script.id = scriptId;
script.onload = () => {
script.dataset.loaded = 'true';
setShowCodeBlockButton(true);
};
document.body.appendChild(script);
}, []);
return showCodeBlockButton;
}
interface CodeBlockButtonProps {
title?: string;
dependencies: Record<PropertyKey, string>;
@ -43,7 +10,8 @@ interface CodeBlockButtonProps {
}
const CodeBlockButton: React.FC<CodeBlockButtonProps> = ({ title, dependencies = {}, jsx }) => {
const showCodeBlockButton = useShowCodeBlockButton();
const { message } = App.useApp();
const [loading, setLoading] = useState(false);
const codeBlockPrefillConfig = {
title: `${title} - antd@${dependencies.antd}`,
@ -57,24 +25,56 @@ const CodeBlockButton: React.FC<CodeBlockButtonProps> = ({ title, dependencies =
json: JSON.stringify({ name: 'antd-demo', dependencies }, null, 2),
};
return showCodeBlockButton ? (
const openHituCodeBlockFn = () => {
setLoading(false);
// @ts-ignore
if (window.openHituCodeBlock) {
// @ts-ignore
window.openHituCodeBlock(JSON.stringify(codeBlockPrefillConfig));
} else {
message.error('此功能仅在内网环境可用');
}
};
const handleClick = () => {
const scriptId = 'hitu-code-block-js';
const existScript = document.getElementById(scriptId) as HTMLScriptElement | null;
// @ts-ignore
if (existScript?.dataset.loaded) {
openHituCodeBlockFn();
return;
}
setLoading(true);
const script = document.createElement('script');
script.src = `https://renderoffice.alipayobjects.com/p/yuyan/180020010001206410/parseFileData-v1.0.1.js?t=${Date.now()}`;
script.async = true;
script.id = scriptId;
script.onload = () => {
script.dataset.loaded = 'true';
openHituCodeBlockFn();
};
script.onerror = () => {
openHituCodeBlockFn();
};
document.body.appendChild(script);
};
return (
<Tooltip title={<FormattedMessage id="app.demo.codeblock" />}>
<div className="code-box-code-action">
<img
alt="codeblock"
src="https://mdn.alipayobjects.com/huamei_wtld8u/afts/img/A*K8rjSJpTNQ8AAAAAAAAAAAAADhOIAQ/original"
className="code-box-codeblock"
onClick={() => {
openHituCodeBlock?.(JSON.stringify(codeBlockPrefillConfig));
}}
/>
{loading ? (
<LoadingOutlined className="code-box-codeblock" />
) : (
<img
alt="codeblock"
src="https://mdn.alipayobjects.com/huamei_wtld8u/afts/img/A*K8rjSJpTNQ8AAAAAAAAAAAAADhOIAQ/original"
className="code-box-codeblock"
onClick={handleClick}
/>
)}
</div>
</Tooltip>
) : null;
);
};
export default (props: CodeBlockButtonProps) => (
<Suspense>
<CodeBlockButton {...props} />
</Suspense>
);
export default CodeBlockButton;

View File

@ -0,0 +1,131 @@
import * as React from 'react';
import { BugOutlined } from '@ant-design/icons';
import { Button, Flex, Popover, theme } from 'antd';
import { createStyles } from 'antd-style';
import dayjs, { Dayjs } from 'dayjs';
import useLocale from '../../../hooks/useLocale';
import { matchDeprecated } from '../../utils';
interface RefinedChangelogProps {
version?: string;
date?: string;
}
interface ContextProps {
version: string;
date?: Dayjs;
isDeprecated?: boolean;
reason?: string[];
}
const ChangelogContext = React.createContext<ContextProps>({
version: '0.0.0',
});
const locales = {
cn: {
deprecatedTitle: '🚨 该版本存在缺陷, 请升级至下一个新版本',
},
en: {
deprecatedTitle: '🚨 This version has defects, please upgrade to the next version',
},
};
const useStyle = createStyles(({ token, css }) => ({
container: css`
margin-block: ${token.margin}px;
padding: ${token.padding}px;
.changelog-version {
line-height: ${token.lineHeight} !important;
margin: 0 !important;
}
`,
isDeprecated: css``,
}));
function RefinedChangelog(props: React.PropsWithChildren<RefinedChangelogProps>) {
const { version, date, children } = props;
const { styles, cx } = useStyle();
const memoizedValue = React.useMemo(() => {
const realVersion = version || '0.0.0';
const bugVersionInfo = matchDeprecated(realVersion);
return {
version: realVersion,
isDeprecated: !!bugVersionInfo?.match,
reason: bugVersionInfo?.reason,
date: date ? dayjs(date) : undefined,
};
}, [version, date]);
return (
<ChangelogContext.Provider value={memoizedValue}>
<div
className={cx('refined-changelog', styles.container, {
[styles.isDeprecated]: memoizedValue.isDeprecated,
})}
>
{children}
</div>
</ChangelogContext.Provider>
);
}
function Version({ children }: React.PropsWithChildren) {
const { isDeprecated, reason } = React.use(ChangelogContext);
const { token } = theme.useToken();
const [locale] = useLocale(locales);
if (!isDeprecated) {
return children;
}
const reasonContent = (
<Flex vertical align="start">
{reason?.map((item, index) => (
<Button
key={index}
type="link"
target="_blank"
rel="noreferrer"
href={item}
icon={<BugOutlined />}
>
{item}
</Button>
))}
</Flex>
);
return (
<Flex align="center" gap="small">
{children}
<Popover placement="right" title={locale.deprecatedTitle} content={reasonContent}>
<BugOutlined
style={{
lineHeight: token.lineHeight,
fontSize: token.fontSize,
color: token.colorErrorText,
}}
/>
</Popover>
</Flex>
);
}
function DateComp(props: React.PropsWithChildren) {
return props.children;
}
function Details(props: React.PropsWithChildren) {
return props.children;
}
export default Object.assign(RefinedChangelog, {
Version,
Date: DateComp,
Details,
});

View File

@ -79,7 +79,7 @@ const ArticleList: React.FC<ArticleListProps> = ({ name, data = [], authors = []
<a href={author?.href} target="_blank" rel="noreferrer">
<Avatar size="small" src={author?.avatar} />
</a>
<Divider type="vertical" />
<Divider vertical />
<a href={article.href} target="_blank" rel="noreferrer">
{article?.title}
</a>

View File

@ -3,19 +3,13 @@ import { BugOutlined } from '@ant-design/icons';
import { Button, Drawer, Flex, Grid, Popover, Tag, Timeline, Typography } from 'antd';
import type { TimelineItemProps } from 'antd';
import { createStyles } from 'antd-style';
import semver from 'semver';
import deprecatedVersions from '../../../../BUG_VERSIONS.json';
import useFetch from '../../../hooks/useFetch';
import useLocale from '../../../hooks/useLocale';
import useLocation from '../../../hooks/useLocation';
import { matchDeprecated } from '../../utils';
import Link from '../Link';
interface MatchDeprecatedResult {
match?: string;
reason: string[];
}
interface ChangelogInfo {
version: string;
changelog: string;
@ -24,17 +18,6 @@ interface ChangelogInfo {
releaseDate: string;
}
function matchDeprecated(v: string): MatchDeprecatedResult {
const match = Object.keys(deprecatedVersions).find((depreciated) =>
semver.satisfies(v, depreciated),
);
const reason = deprecatedVersions[match as keyof typeof deprecatedVersions] || [];
return {
match,
reason: Array.isArray(reason) ? reason : [reason],
};
}
const useStyle = createStyles(({ token, css }) => ({
listWrap: css`
> li {

View File

@ -1,6 +1,6 @@
import React from 'react';
import useLocale from '../hooks/useLocale';
import useLocale from '../../hooks/useLocale';
import SemanticPreview from './SemanticPreview';
export const locales = {

View File

@ -1,4 +1,4 @@
import React, { use } from 'react';
import React, { use, useRef } from 'react';
import { BgColorsOutlined, LinkOutlined, SmileOutlined, SunOutlined } from '@ant-design/icons';
import { Badge, Button, Dropdown } from 'antd';
import type { MenuProps } from 'antd';
@ -22,6 +22,7 @@ const ThemeSwitch: React.FC<ThemeSwitchProps> = () => {
const { pathname, search } = useLocation();
const { theme, updateSiteConfig } = use<SiteContextProps>(SiteContext);
const toggleAnimationTheme = useThemeAnimation();
const lastThemeKey = useRef<string>(theme.includes('dark') ? 'dark' : 'light');
const badge = <Badge color="blue" style={{ marginTop: -1 }} />;
@ -95,10 +96,12 @@ const ThemeSwitch: React.FC<ThemeSwitchProps> = () => {
// 处理主题切换
const handleThemeChange = (key: string, domEvent: React.MouseEvent<HTMLElement, MouseEvent>) => {
// 主题编辑器特殊处理
if (key === 'theme-editor') {
if (key === 'theme-editor' || key === lastThemeKey.current) {
return;
}
lastThemeKey.current = key;
// 亮色/暗色模式切换时应用动画效果
if (key === 'dark' || key === 'light') {
toggleAnimationTheme(domEvent, theme.includes('dark'));

View File

@ -14,8 +14,6 @@ import GlobalStyles from '../../common/GlobalStyles';
import Header from '../../slots/Header';
import SiteContext from '../../slots/SiteContext';
import '../../static/style';
import IndexLayout from '../IndexLayout';
import ResourceLayout from '../ResourceLayout';
import SidebarLayout from '../SidebarLayout';

View File

@ -137,6 +137,12 @@ const GlobalLayout: React.FC = () => {
// Handle isMobile
updateMobileMode();
// 配合 dumi 的 mirror-notify 脚本使用
const retrieveMirrorNotification = (window as any)[Symbol.for('antd.mirror-notify')];
if (typeof retrieveMirrorNotification === 'function') {
retrieveMirrorNotification();
}
window.addEventListener('resize', updateMobileMode);
return () => {
window.removeEventListener('resize', updateMobileMode);

View File

@ -1,24 +0,0 @@
import React, { Suspense } from 'react';
import { Skeleton } from 'antd';
import { InView } from 'react-intersection-observer';
import type { IntersectionObserverProps } from 'react-intersection-observer';
type InViewSuspenseProps = Pick<IntersectionObserverProps, 'delay'> & {
fallback?: React.ReactNode;
};
const InViewSuspense: React.FC<React.PropsWithChildren<InViewSuspenseProps>> = ({
children,
fallback = <Skeleton.Input active size="small" />,
delay = 200,
}) => (
<InView triggerOnce delay={delay}>
{({ inView, ref }) => (
<div ref={ref}>
<Suspense fallback={fallback}>{inView ? children : <span />}</Suspense>
</div>
)}
</InView>
);
export default InViewSuspense;

View File

@ -1,5 +1,5 @@
import React, { useLayoutEffect, useMemo, useState } from 'react';
import { Col, Flex, Skeleton, Space, Typography } from 'antd';
import React, { Suspense, useLayoutEffect, useMemo, useState } from 'react';
import { Col, Flex, FloatButton, Skeleton, Space, Typography } from 'antd';
import classNames from 'classnames';
import { FormattedMessage, useRouteMeta } from 'dumi';
@ -9,16 +9,13 @@ import ComponentMeta from '../../builtins/ComponentMeta';
import type { DemoContextProps } from '../DemoContext';
import DemoContext from '../DemoContext';
import SiteContext from '../SiteContext';
import { useStyle } from './DocAnchor';
import InViewSuspense from './InViewSuspense';
const Contributors = React.lazy(() => import('./Contributors'));
const ColumnCard = React.lazy(() => import('./ColumnCard'));
const DocAnchor = React.lazy(() => import('./DocAnchor'));
const DocMeta = React.lazy(() => import('./DocMeta'));
const Footer = React.lazy(() => import('../Footer'));
const PrevAndNext = React.lazy(() => import('../../common/PrevAndNext'));
const EditButton = React.lazy(() => import('../../common/EditButton'));
import DocAnchor, { useStyle } from './DocAnchor';
import Contributors from './Contributors';
import ColumnCard from './ColumnCard';
import DocMeta from './DocMeta';
import Footer from '../Footer';
import PrevAndNext from '../../common/PrevAndNext';
import EditButton from '../../common/EditButton';
const AvatarPlaceholder: React.FC<{ num?: number }> = ({ num = 6 }) =>
Array.from({ length: num }).map<React.ReactNode>((_, i) => (
@ -54,9 +51,7 @@ const Content: React.FC<React.PropsWithChildren> = ({ children }) => {
return (
<DemoContext value={contextValue}>
<Col xxl={20} xl={19} lg={18} md={18} sm={24} xs={24}>
<InViewSuspense fallback={null}>
<DocAnchor showDebug={showDebug} debugDemos={debugDemos} />
</InViewSuspense>
<DocAnchor showDebug={showDebug} debugDemos={debugDemos} />
<article className={classNames(styles.articleWrapper, { rtl: isRTL })}>
{meta.frontmatter?.title ? (
<Flex justify="space-between">
@ -65,20 +60,16 @@ const Content: React.FC<React.PropsWithChildren> = ({ children }) => {
<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>
<EditButton
title={<FormattedMessage id="app.content.edit-page" />}
filename={meta.frontmatter.filename}
/>
)}
</Space>
</Typography.Title>
</Flex>
) : null}
<InViewSuspense fallback={null}>
<DocMeta />
</InViewSuspense>
<DocMeta />
{!meta.frontmatter.__autoDescription && meta.frontmatter.description}
{/* Import Info */}
@ -92,23 +83,22 @@ const Content: React.FC<React.PropsWithChildren> = ({ children }) => {
designUrl={meta.frontmatter.designUrl}
/>
)}
<div style={{ minHeight: 'calc(100vh - 64px)' }}>{children}</div>
<InViewSuspense fallback={null}>
<ColumnCard
zhihuLink={meta.frontmatter.zhihu_url}
yuqueLink={meta.frontmatter.yuque_url}
juejinLink={meta.frontmatter.juejin_url}
/>
</InViewSuspense>
<div style={{ minHeight: 'calc(100vh - 64px)' }}>
{children}
<FloatButton.BackTop />
</div>
<ColumnCard
zhihuLink={meta.frontmatter.zhihu_url}
yuqueLink={meta.frontmatter.yuque_url}
juejinLink={meta.frontmatter.juejin_url}
/>
<div style={{ marginTop: 120 }}>
<InViewSuspense fallback={<AvatarPlaceholder />}>
<Suspense fallback={<AvatarPlaceholder />}>
<Contributors filename={meta.frontmatter.filename} />
</InViewSuspense>
</Suspense>
</div>
</article>
<InViewSuspense fallback={null}>
<PrevAndNext rtl={isRTL} />
</InViewSuspense>
<PrevAndNext rtl={isRTL} />
<Footer />
</Col>
</DemoContext>

View File

@ -1 +0,0 @@
import 'rc-footer/assets/index.css';

View File

@ -1,107 +0,0 @@
import tsToJs from '../tsToJs';
// 简单测试用例:基本的 TypeScript 到 JavaScript 转换
console.log('测试 1: 基本 TypeScript 转换');
const tsInput = `
interface Person {
name: string;
age: number;
}
function greet(person: Person): string {
return \`Hello, \${person.name}!\`;
}
const john: Person = { name: 'John', age: 30 };
greet(john);
`;
const jsOutput = tsToJs(tsInput);
console.log('输入:', tsInput);
console.log('输出:', jsOutput);
console.log('检查点:');
console.log('- interface 被移除:', !jsOutput.includes('interface'));
console.log('- 类型注解被移除:', !jsOutput.includes(': string') && !jsOutput.includes(': Person'));
console.log('- 函数定义正确:', jsOutput.includes('function greet(person)'));
console.log('- 对象定义正确:', jsOutput.includes('const john = { name:'));
// 测试用例 2: JSX 转换
console.log('\n测试 2: JSX 转换');
const tsxInput = `
import React, { FC } from 'react';
interface ButtonProps {
text: string;
onClick: () => void;
}
const Button: FC<ButtonProps> = ({ text, onClick }) => {
return (
<button
className="primary-button"
onClick={onClick}
>
{text}
</button>
);
};
export default Button;
`;
const jsxOutput = tsToJs(tsxInput);
console.log('输入:', tsxInput);
console.log('输出:', jsxOutput);
console.log('检查点:');
console.log('- interface 被移除:', !jsxOutput.includes('interface ButtonProps'));
console.log('- 类型注解被移除:', !jsxOutput.includes(': FC<ButtonProps>'));
console.log('- JSX 被保留:', jsxOutput.includes('<button') && jsxOutput.includes('</button>'));
console.log(
'- 属性被保留:',
jsxOutput.includes('className="primary-button"') && jsxOutput.includes('onClick={onClick}'),
);
// 测试用例 3: 类型导入处理
console.log('\n测试 3: 类型导入处理');
const typeImportInput = `
import React from 'react';
import type { ReactNode } from 'react';
import { Button } from 'antd';
import type { ButtonProps } from 'antd/es/button';
const MyButton = (props: ButtonProps) => {
return <Button {...props} />;
};
`;
const typeImportOutput = tsToJs(typeImportInput);
console.log('输入:', typeImportInput);
console.log('输出:', typeImportOutput);
console.log('检查点:');
console.log('- 类型导入被移除:', !typeImportOutput.includes('import type'));
console.log('- ReactNode 被移除:', !typeImportOutput.includes('ReactNode'));
console.log('- ButtonProps 被移除:', !typeImportOutput.includes('ButtonProps'));
console.log(
'- 普通导入被保留:',
typeImportOutput.includes("import React from 'react'") &&
typeImportOutput.includes("import { Button } from 'antd'"),
);
// 总结测试结果
console.log('\n测试总结:');
const test1Pass =
!jsOutput.includes('interface') &&
!jsOutput.includes(': string') &&
jsOutput.includes('function greet(person)');
const test2Pass =
!jsxOutput.includes('interface ButtonProps') &&
jsxOutput.includes('<button') &&
jsxOutput.includes('</button>');
const test3Pass =
!typeImportOutput.includes('import type') &&
typeImportOutput.includes("import React from 'react'");
console.log('测试 1 (基本 TypeScript 转换):', test1Pass ? '通过' : '失败');
console.log('测试 2 (JSX 转换):', test2Pass ? '通过' : '失败');
console.log('测试 3 (类型导入处理):', test3Pass ? '通过' : '失败');
console.log('所有测试:', test1Pass && test2Pass && test3Pass ? '通过' : '失败');

View File

@ -1,93 +0,0 @@
import tsToJs from '../tsToJs';
describe('tsToJs', () => {
it('应该将基本的 TypeScript 转换为 JavaScript', () => {
const tsInput = `
interface Person {
name: string;
age: number;
}
function greet(person: Person): string {
return \`Hello, \${person.name}!\`;
}
const john: Person = { name: 'John', age: 30 };
greet(john);
`;
const jsOutput = tsToJs(tsInput);
// 检查结果中不应包含 TypeScript 特有的语法
expect(jsOutput).not.toContain('interface');
expect(jsOutput).not.toContain(': string');
expect(jsOutput).not.toContain(': Person');
expect(jsOutput).not.toContain(': number');
// 检查结果应包含 JavaScript 代码
expect(jsOutput).toContain('function greet(person)');
expect(jsOutput).toContain('return');
expect(jsOutput).toContain('Hello');
expect(jsOutput).toContain("const john = { name: 'John', age: 30 }");
});
it('应该保留 JSX 语法', () => {
const tsxInput = `
import React, { FC } from 'react';
interface ButtonProps {
text: string;
onClick: () => void;
}
const Button: FC<ButtonProps> = ({ text, onClick }) => {
return (
<button
className="primary-button"
onClick={onClick}
>
{text}
</button>
);
};
export default Button;
`;
const jsxOutput = tsToJs(tsxInput);
// 检查结果中不应包含 TypeScript 特有的语法
expect(jsxOutput).not.toContain('interface ButtonProps');
expect(jsxOutput).not.toContain(': FC<ButtonProps>');
// 检查结果应保留 JSX 语法
expect(jsxOutput).toContain('<button');
expect(jsxOutput).toContain('className="primary-button"');
expect(jsxOutput).toContain('</button>');
expect(jsxOutput).toContain('onClick={onClick}');
});
it('应该删除类型导入', () => {
const tsInput = `
import React from 'react';
import type { ReactNode } from 'react';
import { Button } from 'antd';
import type { ButtonProps } from 'antd/es/button';
const MyButton = (props: ButtonProps) => {
return <Button {...props} />;
};
`;
const jsOutput = tsToJs(tsInput);
// 检查结果中不应包含类型导入
expect(jsOutput).not.toContain('import type');
expect(jsOutput).not.toContain('ReactNode');
expect(jsOutput).not.toContain('ButtonProps');
// 保留普通导入
expect(jsOutput).toContain("import React from 'react'");
expect(jsOutput).toContain("import { Button } from 'antd'");
});
});

View File

@ -1,7 +1,8 @@
import semver from 'semver';
import flatten from 'lodash/flatten';
import flattenDeep from 'lodash/flattenDeep';
import themeConfig from './themeConfig';
import deprecatedVersions from '../../../BUG_VERSIONS.json';
import themeConfig from '../themeConfig';
interface Meta {
skip?: boolean;
@ -24,6 +25,11 @@ interface Orders {
[key: string]: number;
}
interface MatchDeprecatedResult {
match?: string;
reason: string[];
}
export function getMenuItems(
moduleData: ModuleDataItem[],
locale: string,
@ -201,4 +207,15 @@ export function getMetaDescription(jml?: any[] | null) {
return paragraph;
}
export function matchDeprecated(v: string): MatchDeprecatedResult {
const match = Object.keys(deprecatedVersions).find((depreciated) =>
semver.satisfies(v, depreciated),
);
const reason = deprecatedVersions[match as keyof typeof deprecatedVersions] || [];
return {
match,
reason: Array.isArray(reason) ? reason : [reason],
};
}
export const getThemeConfig = () => themeConfig;

View File

@ -1,273 +0,0 @@
import { parseText } from './tsToJs';
// 简单 TypeScript 代码示例
const tsCode = `
interface Person {
name: string;
age: number;
}
class Employee implements Person {
name: string;
age: number;
department: string;
constructor(name: string, age: number, department: string) {
this.name = name;
this.age = age;
this.department = department;
}
getInfo(): string {
return \`\${this.name}, \${this.age}, \${this.department}\`;
}
}
const employee: Employee = new Employee('张三', 30, '研发部');
console.log(employee.getInfo());
`;
// 包含 JSX 的 TypeScript 代码示例
const tsxCode = `
import React, { FC, useState } from 'react';
import { Button } from 'antd';
interface CounterProps {
initialCount?: number;
label: string;
}
const Counter: FC<CounterProps> = ({ initialCount = 0, label }) => {
const [count, setCount] = useState<number>(initialCount);
const increment = (): void => {
setCount(count + 1);
};
const decrement = (): void => {
setCount(count - 1);
};
return (
<div className="counter">
<h3>{label}: {count}</h3>
<Button type="primary" onClick={increment}>+</Button>
<Button onClick={decrement}>-</Button>
</div>
);
};
export default Counter;
`;
// 复杂 TypeScript 代码示例,包含泛型、类型导入、类型别名等
const complexTsCode = `
import React from 'react';
import type { ReactNode } from 'react';
import { Table } from 'antd';
import type { TableProps, TableColumnType } from 'antd/es/table';
// 类型别名
type Status = 'pending' | 'processing' | 'success' | 'failed';
// 泛型接口
interface DataItem<T = string> {
id: number;
name: string;
status: Status;
details: T;
createdAt: Date;
}
// 类型映射和条件类型
type ReadonlyDataItem<T> = {
readonly [K in keyof DataItem<T>]: DataItem<T>[K];
};
// 工具类型
type OptionalId<T> = Omit<T, 'id'> & { id?: number };
// 类型断言函数
function assertIsDataItem<T>(item: any): asserts item is DataItem<T> {
if (!item || typeof item.id !== 'number') {
throw new Error('Invalid DataItem: missing or invalid id');
}
}
// 使用泛型组件
const DataTable = <T extends string>(props: {
data: DataItem<T>[];
renderDetails?: (details: T) => ReactNode;
}) => {
const { data, renderDetails } = props;
// 定义表格列
const columns: TableColumnType<DataItem<T>>[] = [
{
title: 'ID',
dataIndex: 'id',
key: 'id',
},
{
title: '名称',
dataIndex: 'name',
key: 'name',
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
render: (status: Status) => {
const statusColors = {
pending: 'blue',
processing: 'orange',
success: 'green',
failed: 'red',
};
return <span style={{ color: statusColors[status] }}>{status}</span>;
},
},
{
title: '详情',
dataIndex: 'details',
key: 'details',
render: (details: T) => renderDetails ? renderDetails(details) : details,
},
{
title: '创建时间',
dataIndex: 'createdAt',
key: 'createdAt',
render: (date: Date) => date.toLocaleString(),
},
];
return <Table<DataItem<T>> columns={columns} dataSource={data} rowKey="id" />;
};
// 使用工具类型创建数据
const createDataItem = <T extends string>(item: OptionalId<DataItem<T>>): DataItem<T> => {
return {
id: item.id ?? Math.floor(Math.random() * 1000),
name: item.name,
status: item.status,
details: item.details,
createdAt: item.createdAt || new Date(),
};
};
// 示例数据
const exampleData: DataItem<string>[] = [
createDataItem({
name: '项目 A',
status: 'success',
details: '项目顺利完成',
createdAt: new Date(2023, 0, 15),
}),
createDataItem({
name: '项目 B',
status: 'processing',
details: '正在进行中...',
createdAt: new Date(2023, 2, 10),
}),
];
// 渲染组件
const App = () => {
return (
<div>
<h1></h1>
<DataTable
data={exampleData}
renderDetails={(details) => <em>{details}</em>}
/>
</div>
);
};
export default App;
`;
// 加入Jest测试用例
describe('tsToJs函数测试', () => {
// 转换普通 TypeScript 代码
it('应该能正确转换普通TypeScript代码', () => {
const jsCode = parseText(tsCode);
// 验证类型注解被移除
expect(jsCode).not.toContain('interface Person');
expect(jsCode).not.toContain(': string');
expect(jsCode).not.toContain(': number');
// 验证类实现被保留
expect(jsCode).toContain('class Employee');
expect(jsCode).toContain('constructor(name, age, department)');
expect(jsCode).toContain('getInfo()');
// 验证实例创建
expect(jsCode).toContain("new Employee('张三', 30, '研发部')");
console.log('转换前的 TypeScript 代码:');
console.log(tsCode);
console.log('\n转换后的 JavaScript 代码:');
console.log(jsCode);
});
// 转换包含 JSX 的 TypeScript 代码
it('应该能正确转换TSX代码', () => {
const jsxCode = parseText(tsxCode);
// 验证React导入被保留
expect(jsxCode).toContain('import React');
// 验证类型注解和类型导入被移除
expect(jsxCode).not.toContain('FC<');
expect(jsxCode).not.toContain('interface CounterProps');
expect(jsxCode).not.toContain('<number>');
expect(jsxCode).not.toContain(': void');
// 验证JSX结构被保留
expect(jsxCode).toContain('<div className="counter">');
expect(jsxCode).toContain('<Button type="primary"');
// 验证默认参数被保留
expect(jsxCode).toContain('initialCount = 0');
console.log('\n\n转换前的 TSX 代码:');
console.log(tsxCode);
console.log('\n转换后的 JSX 代码:');
console.log(jsxCode);
});
// 转换复杂 TypeScript 代码
it('应该能正确转换复杂TypeScript代码', () => {
const complexJsCode = parseText(complexTsCode);
// 验证类型导入被移除
expect(complexJsCode).not.toContain('import type');
// 验证泛型被移除
expect(complexJsCode).not.toContain('<T>');
expect(complexJsCode).not.toContain('<T extends string>');
// 验证类型别名和接口被移除
expect(complexJsCode).not.toContain('type Status');
expect(complexJsCode).not.toContain('interface DataItem');
// 验证函数和组件结构被保留
expect(complexJsCode).toContain('function assertIsDataItem');
expect(complexJsCode).toContain('const DataTable = ');
expect(complexJsCode).toContain('const createDataItem = ');
// 验证JSX结构被保留
expect(complexJsCode).toContain('<Table');
expect(complexJsCode).toContain('<span style=');
// 验证空值合并运算符的处理
expect(complexJsCode).toContain('_a = item.id'); // TypeScript会将 ?? 转换为更兼容的语法
console.log('\n\n转换前的复杂 TypeScript 代码:');
console.log(complexTsCode);
console.log('\n转换后的 JavaScript 代码:');
console.log(complexJsCode);
});
});

View File

@ -2,27 +2,16 @@ import * as ts from 'typescript';
import { format } from '@prettier/sync';
/**
* TypeScript JavaScript
* TypeScript TSX JavaScript JSX
*
* TypeScript TSX JavaScript JSX
* sylvanas
* 使 TypeScript API JSX JavaScript Prettier
*
* 使 TypeScript API TS JS
* @param tsCode - TypeScript
* @returns JavaScript
*
*
* 1.
* 2. JSX
* 3.
* 4. ES6+
* 5.
* 6. 使 Prettier
* 7. React hooks
* 8. TypeScript
*
* @param tsCode TypeScript
* @returns JavaScript
* @remark Prettier
*/
export default function (tsCode: string): string {
export default function tsToJs(tsCode: string): string {
// 设置编译器选项,保留 JSX 语法
const compilerOptions: ts.CompilerOptions = {
target: ts.ScriptTarget.ES2016, // 目标 ECMAScript 版本
@ -61,15 +50,3 @@ export default function (tsCode: string): string {
return result.outputText;
}
}
/**
* TypeScript JavaScript
*
* API使
*
* @param tsCode TypeScript
* @returns JavaScript
*/
export function parseText(tsCode: string): string {
return exports.default(tsCode);
}

View File

@ -4,6 +4,7 @@ import * as fs from 'fs-extra';
import os from 'node:os';
import rehypeAntd from './.dumi/rehypeAntd';
import rehypeChangelog from './.dumi/rehypeChangelog';
import remarkAntd from './.dumi/remarkAntd';
import remarkAnchor from './.dumi/remarkAnchor';
import { version } from './package.json';
@ -52,7 +53,7 @@ export default defineConfig({
// https://github.com/ant-design/ant-design/issues/46628
'@ant-design/icons$': '@ant-design/icons/lib',
},
extraRehypePlugins: [rehypeAntd],
extraRehypePlugins: [rehypeAntd, rehypeChangelog],
extraRemarkPlugins: [remarkAntd, remarkAnchor],
metas: [
{ name: 'theme-color', content: '#1677ff' },
@ -189,7 +190,7 @@ export default defineConfig({
{
async: true,
content: fs
.readFileSync(path.join(__dirname, '.dumi', 'scripts', 'mirror-modal.js'))
.readFileSync(path.join(__dirname, '.dumi', 'scripts', 'mirror-notify.js'))
.toString(),
},
{

View File

@ -43,7 +43,7 @@ jobs:
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.GITHUB_TOKEN }} # Cannot be default!!!
assignees: 'afc163, zombieJ, xrkffgg, MadCcc'
assignees: 'afc163, yoyo837, Wxh16144'
title: "chore: upgrade deps"
commit-message: "chore: upgrade deps"
body: |

View File

@ -15,6 +15,27 @@ tag: vVERSION
---
## 5.25.4
`2025-05-31`
- 💄 Tweak Select selected item background in dark theme to make it easier to identify. [#53956](https://github.com/ant-design/ant-design/pull/53956) [@afc163](https://github.com/afc163)
- ⌨️ Timeline now uses `ol` instead of `ul` to meet HTML semantic requirements. [#53944](https://github.com/ant-design/ant-design/pull/53944) [@fnoopv](https://github.com/fnoopv)
- 🐞 Fix Drawer mask does not fade out when changing from true to false. [#53955](https://github.com/ant-design/ant-design/pull/53955) [@afc163](https://github.com/afc163)
- 🐞 Fix Input.Search does not support `onPressEnter`. [#53898](https://github.com/ant-design/ant-design/pull/53898) [@codingories](https://github.com/codingories)
- 🛠 Remove `!important` style syntax from Checkbox. [#53953](https://github.com/ant-design/ant-design/pull/53953) [@doandevhere](https://github.com/doandevhere)
- TypeScript
- 🤖 Fix Descriptions.Item `span` property type. [#53913](https://github.com/ant-design/ant-design/pull/53913) [@DDDDD12138](https://github.com/DDDDD12138)
## 5.25.3
`2025-05-26`
- 🐞 Fix Typography.Text `delete` property not updating. [#53861](https://github.com/ant-design/ant-design/pull/53861) [@codingories](https://github.com/codingories)
- 🐞 Fix the Statistic.Timer as a subcomponent of Tooltip could not display text prompts properly. [#53888](https://github.com/ant-design/ant-design/pull/53888) [@jin19980928](https://github.com/jin19980928)
- 🐞 Fix the `style` setting of the Upload component did not take effect in more types. [#53877](https://github.com/ant-design/ant-design/pull/53877) [@QuentinHsu](https://github.com/QuentinHsu)
- 💄 Fix the residual focus style after clicking Tabs. [#53901](https://github.com/ant-design/ant-design/pull/53901)
## 5.25.2
`2025-05-19`
@ -24,7 +45,6 @@ tag: vVERSION
- 🐞 Fix Statistic.Timer ssr hydrate issue. [#53817](https://github.com/ant-design/ant-design/pull/53817) [@zombieJ](https://github.com/zombieJ)
- 🐞 Fix Table header blink issue when sticky is enable. [#53803](https://github.com/ant-design/ant-design/pull/53803) [@afc163](https://github.com/afc163)
- 💄 Fix Input.Search `variant="filled"` broken UI. [#53787](https://github.com/ant-design/ant-design/pull/53787) [@afc163](https://github.com/afc163)
- TypeScript
- 🤖 Fix Upload.Dragger does not accept generic parameter problem. [#53842](https://github.com/ant-design/ant-design/pull/53842) [@fnoopv](https://github.com/fnoopv)
- 🤖 Remove Modal invalid properties type definition. [#53808](https://github.com/ant-design/ant-design/pull/53808) [@wanpan11](https://github.com/wanpan11)
@ -67,7 +87,7 @@ tag: vVERSION
- 🛠 MISCRefactor compatible code, use standard web API first, and downgrade to deprecated API when not compatibale [#53107](https://github.com/ant-design/ant-design/pull/53107) [@li-jia-nan](https://github.com/li-jia-nan)
- ⌨️ Opt Tour's `aria-*` props. [#53345](https://github.com/ant-design/ant-design/pull/53345) [@kiner-tang](https://github.com/kiner-tang)
- ⌨️ MISC: Optimized closable component's aria props. [#53410](https://github.com/ant-design/ant-design/pull/53410) [@kiner-tang](https://github.com/kiner-tang)
- 🗑 MISC: Deprecate `destory*` of some components for `destroyOnHidden` prop [#53739](https://github.com/ant-design/ant-design/pull/53739) [@li-jia-nan](https://github.com/li-jia-nan)
- 🗑 MISC: Deprecate `destroy*` of some components for `destroyOnHidden` prop. [#53739](https://github.com/ant-design/ant-design/pull/53739) [@li-jia-nan](https://github.com/li-jia-nan)
- 🗑 Deprecate `dropdownRender` of Dropdown for `popupRender` [#53263](https://github.com/ant-design/ant-design/pull/53263) [@aojunhao123](https://github.com/aojunhao123)
- 🗑 Deprecated `dropdown*` props in Cascader [#53133](https://github.com/ant-design/ant-design/pull/53133) [@aojunhao123](https://github.com/aojunhao123)
- 🇨🇿 Add cs_CZ locale for QRCode and ColorPicker. [#53741](https://github.com/ant-design/ant-design/pull/53741) [@malda26](https://github.com/malda26)
@ -544,7 +564,7 @@ Last version of the Dragon Year, Happy Chinese New Year! 🐲
- 🐞 Fix Transfer width issue when customized as TableTransfer. [#50974](https://github.com/ant-design/ant-design/pull/50974) [@zombieJ](https://github.com/zombieJ)
- 🇹🇷 Add Turkish text for `filterCheckall` in Table component. [#51000](https://github.com/ant-design/ant-design/pull/51000) [@ytahirkose](https://github.com/ytahirkose)
## 5.21.0 🔥
## 5.21.0
`2024-09-22`
@ -884,9 +904,9 @@ Last version of the Dragon Year, Happy Chinese New Year! 🐲
- 🆕 Rate add `keyboard` property to ignore keyboard events. [#49132](https://github.com/ant-design/ant-design/pull/49132) [@Wxh16144](https://github.com/Wxh16144)
- 🆕 Spin support `percent` to render as progress. [#48657](https://github.com/ant-design/ant-design/pull/48657)
- 🐞 Watermark add `overflow: hidden` style to prevent set `height: 0` to hide the watermark case. [#49130](https://github.com/ant-design/ant-design/pull/49130)
- 🐞 Anchor fix scroll animation lost bug when set `replace` property. [#49136](https://github.com/ant-design/ant-design/pull/49136) [@mySkey](https://github.com/mySkey)
- 🐞 Tour fix `current` argument of `onClose` is wrong. [#49124](https://github.com/ant-design/ant-design/pull/49124)
- 💄 List.Item add `styles` and `className` properties. [#49154](https://github.com/ant-design/ant-design/pull/49154) [@wanpan11](https://github.com/wanpan11)
- 🐞 Fix Anchor scroll animation lost bug when set `replace` property. [#49136](https://github.com/ant-design/ant-design/pull/49136) [@mySkey](https://github.com/mySkey)
- 🐞 Fix Tour `current` argument of `onClose` is wrong. [#49124](https://github.com/ant-design/ant-design/pull/49124)
- 🆕 List.Item add `styles` and `className` properties. [#49154](https://github.com/ant-design/ant-design/pull/49154) [@wanpan11](https://github.com/wanpan11)
- 🇯🇵 DatePicker add missing ja_JP translations. [#49155](https://github.com/ant-design/ant-design/pull/49155) [@huyikai](https://github.com/huyikai)
- 🛠 Simplify several logics and type definitions. [#49146](https://github.com/ant-design/ant-design/pull/49146) [#49156](https://github.com/ant-design/ant-design/pull/49156) [#49169](https://github.com/ant-design/ant-design/pull/49169) [#49162](https://github.com/ant-design/ant-design/pull/49162) [@coding-ice](https://github.com/coding-ice)

View File

@ -15,6 +15,27 @@ tag: vVERSION
---
## 5.25.4
`2025-05-31`
- 💄 调整 Select 选中项在暗色模式下的背景色,使其更容易识别。[#53956](https://github.com/ant-design/ant-design/pull/53956) [@afc163](https://github.com/afc163)
- ⌨️ Timeline 使用 `ol` 代替 `ul` 以符合 html 语义要求。[#53944](https://github.com/ant-design/ant-design/pull/53944) [@fnoopv](https://github.com/fnoopv)
- 🐞 修复 Drawer `mask` 从 true 改为 false 时没有渐出的问题。[#53955](https://github.com/ant-design/ant-design/pull/53955) [@afc163](https://github.com/afc163)
- 🐞 修复 Input.Search 不支持 `onPressEnter` 的问题。[#53898](https://github.com/ant-design/ant-design/pull/53898) [@codingories](https://github.com/codingories)
- 🛠 移除 Checkbox 的 `!important` 样式语法。[#53953](https://github.com/ant-design/ant-design/pull/53953) [@doandevhere](https://github.com/doandevhere)
- TypeScript
- 🤖 修正 Descriptions.Item `span` 属性类型。[#53913](https://github.com/ant-design/ant-design/pull/53913) [@DDDDD12138](https://github.com/DDDDD12138)
## 5.25.3
`2025-05-26`
- 🐞 修复 Typography.Text `delete` 属性无法更新的问题。[#53861](https://github.com/ant-design/ant-design/pull/53861) [@codingories](https://github.com/codingories)
- 🐞 修复 Statistic.Timer 组件作为 Tooltip 的子组件无法正常展示文字提示的问题。[#53888](https://github.com/ant-design/ant-design/pull/53888) [@jin19980928](https://github.com/jin19980928)
- 🐞 修复 Upload 组件在更多类型时 `style` 设置未生效的问题。[#53877](https://github.com/ant-design/ant-design/pull/53877) [@QuentinHsu](https://github.com/QuentinHsu)
- 💄 修复 Tabs 点击后有残留 focus 样式的问题。[#53901](https://github.com/ant-design/ant-design/pull/53901)
## 5.25.2
`2025-05-19`
@ -24,7 +45,6 @@ tag: vVERSION
- 🐞 修复 Statistic.Timer ssr 注水渲染不一致的问题。[#53817](https://github.com/ant-design/ant-design/pull/53817) [@zombieJ](https://github.com/zombieJ)
- 🐞 修复 Table 开启 `sticky` 时的一个列头渲染闪烁问题。[#53803](https://github.com/ant-design/ant-design/pull/53803) [@afc163](https://github.com/afc163)
- 💄 修复 Input.Search `variant="filled"` 的样式。[#53787](https://github.com/ant-design/ant-design/pull/53787) [@afc163](https://github.com/afc163)
- TypeScript
- 🤖 修复 Upload.Dragger 不支持泛型参数的问题。[#53842](https://github.com/ant-design/ant-design/pull/53842) [@fnoopv](https://github.com/fnoopv)
- 🤖 移除 Modal 无效的属性类型定义。[#53808](https://github.com/ant-design/ant-design/pull/53808) [@wanpan11](https://github.com/wanpan11)
@ -67,12 +87,11 @@ tag: vVERSION
- 🛠 杂项:重构兼容逻辑,优先使用标准 Web API不兼容的时候降级为废弃 API。[#53107](https://github.com/ant-design/ant-design/pull/53107) [@li-jia-nan](https://github.com/li-jia-nan)
- ⌨️ 优化 Tour 的 `aria-*` 属性。[#53345](https://github.com/ant-design/ant-design/pull/53345) [@kiner-tang](https://github.com/kiner-tang)
- ⌨️ 杂项:优化可关闭组件的无障碍属性。[#53410](https://github.com/ant-design/ant-design/pull/53410) [@kiner-tang](https://github.com/kiner-tang)
- 🗑 杂项:废弃多个可开关组件的 `destory*` 属性,统一为 `destroyOnHidden`。 [#53739](https://github.com/ant-design/ant-design/pull/53739) [@li-jia-nan](https://github.com/li-jia-nan)
- 🗑 杂项:废弃多个可开关组件的 `destroy*` 属性,统一为 `destroyOnHidden`。 [#53739](https://github.com/ant-design/ant-design/pull/53739) [@li-jia-nan](https://github.com/li-jia-nan)
- 🗑 废弃 Dropdown 中 `dropdownRender` 属性,用 `popupRender` 替代。[#53263](https://github.com/ant-design/ant-design/pull/53263) [@aojunhao123](https://github.com/aojunhao123)
- 🗑 废弃 Cascader 组件的 `dropdown*` 等属性。[#53133](https://github.com/ant-design/ant-design/pull/53133) [@aojunhao123](https://github.com/aojunhao123)
- 🇨🇿 添加 QRCode 和 ColorPicker 的捷克语cs_CZ支持。[#53741](https://github.com/ant-design/ant-design/pull/53741) [@malda26](https://github.com/malda26)
## 5.24.9
`2025-04-29`
@ -547,7 +566,7 @@ tag: vVERSION
- 💄 修复 Transfer 在自定义为 TableTransfer 时,宽度不正确的问题。[#50974](https://github.com/ant-design/ant-design/pull/50974) [@zombieJ](https://github.com/zombieJ)
- 🇹🇷 补充 Table 组件 `filterCheckall` 的土耳其语文案。[#51000](https://github.com/ant-design/ant-design/pull/51000) [@ytahirkose](https://github.com/ytahirkose)
## 5.21.0 🔥
## 5.21.0
`2024-09-22`

View File

@ -1,7 +1,7 @@
import React from 'react';
import { Alert, Button, Space } from 'antd';
import SemanticPreview from '../../../.dumi/components/SemanticPreview';
import SemanticPreview from '../../../.dumi/theme/common/SemanticPreview';
import useLocale from '../../../.dumi/hooks/useLocale';
const locales = {

View File

@ -2,7 +2,7 @@ import React from 'react';
import { Anchor } from 'antd';
import type { AnchorProps } from 'antd';
import SemanticPreview from '../../../.dumi/components/SemanticPreview';
import SemanticPreview from '../../../.dumi/theme/common/SemanticPreview';
import useLocale from '../../../.dumi/hooks/useLocale';
const locales = {

View File

@ -1,7 +1,7 @@
import React from 'react';
import { AutoComplete } from 'antd';
import SelectSemanticTemplate from '../../../.dumi/components/SelectSemanticTemplate';
import SelectSemanticTemplate from '../../../.dumi/theme/common/SelectSemanticTemplate';
const mockVal = (str: string, repeat = 1) => ({
value: str.repeat(repeat),

View File

@ -805,7 +805,7 @@ Array [
</span>
</div>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>,
<div
@ -935,7 +935,7 @@ Array [
</div>
</div>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>,
<div
@ -1065,7 +1065,7 @@ Array [
</div>
</div>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>,
<div
@ -1195,7 +1195,7 @@ Array [
</div>
</div>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>,
<div

View File

@ -683,7 +683,7 @@ Array [
</span>
</div>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>,
<div
@ -721,7 +721,7 @@ Array [
</span>
</div>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>,
<div
@ -759,7 +759,7 @@ Array [
</span>
</div>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>,
<div
@ -797,7 +797,7 @@ Array [
</span>
</div>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>,
<div

View File

@ -309,11 +309,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Presets
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<div
class="ant-space ant-space-vertical ant-space-gap-row-small ant-space-gap-col-small css-var-test-id"
@ -531,11 +537,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Custom
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<div
class="ant-space ant-space-vertical ant-space-gap-row-small ant-space-gap-col-small css-var-test-id"

View File

@ -305,11 +305,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Presets
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<div
class="ant-space ant-space-vertical ant-space-gap-row-small ant-space-gap-col-small css-var-test-id"
@ -527,11 +533,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Custom
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<div
class="ant-space ant-space-vertical ant-space-gap-row-small ant-space-gap-col-small css-var-test-id"

View File

@ -1,7 +1,7 @@
import React from 'react';
import { Avatar, Badge } from 'antd';
import SemanticPreview from '../../../.dumi/components/SemanticPreview';
import SemanticPreview from '../../../.dumi/theme/common/SemanticPreview';
import useLocale from '../../../.dumi/hooks/useLocale';
const locales = {

View File

@ -2,7 +2,7 @@ import React from 'react';
import { Badge, Card } from 'antd';
import type { RibbonProps } from 'antd/es/badge/Ribbon';
import SemanticPreview from '../../../.dumi/components/SemanticPreview';
import SemanticPreview from '../../../.dumi/theme/common/SemanticPreview';
import useLocale from '../../../.dumi/hooks/useLocale';
const locales = {

View File

@ -19,13 +19,13 @@ const colors = [
const App: React.FC = () => (
<>
<Divider orientation="left">Presets</Divider>
<Divider titlePlacement="start">Presets</Divider>
<Space vertical>
{colors.map((color) => (
<Badge key={color} color={color} text={color} />
))}
</Space>
<Divider orientation="left">Custom</Divider>
<Divider titlePlacement="start">Custom</Divider>
<Space vertical>
<Badge color="#f50" text="#f50" />
<Badge color="rgb(45, 183, 245)" text="rgb(45, 183, 245)" />

View File

@ -3,7 +3,7 @@ import { HomeOutlined, UserOutlined } from '@ant-design/icons';
import { Breadcrumb } from 'antd';
import type { BreadcrumbProps } from 'antd';
import SemanticPreview from '../../../.dumi/components/SemanticPreview';
import SemanticPreview from '../../../.dumi/theme/common/SemanticPreview';
import useLocale from '../../../.dumi/hooks/useLocale';
const locales = {

View File

@ -998,11 +998,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start ant-divider-plain"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Preview
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<div
class="ant-flex css-var-test-id ant-flex-align-stretch ant-flex-gap-small ant-flex-vertical"
@ -1404,11 +1410,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center ant-divider-plain"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
https://github.com/ant-design/ant-design/issues/51811
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>
<div>
<button
@ -1455,11 +1467,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center ant-divider-plain"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
https://github.com/ant-design/ant-design/issues/52124
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>
<div>
<button
@ -1508,11 +1526,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center ant-divider-plain"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
https://github.com/ant-design/ant-design/issues/51380
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>
<div>
<button
@ -1685,11 +1709,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center ant-divider-plain"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
https://github.com/ant-design/ant-design/issues/51380
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>
<div
class="ant-flex css-var-test-id ant-flex-gap-small"
@ -2467,11 +2497,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start ant-divider-plain"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Preview
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<div
class="ant-flex css-var-test-id ant-flex-align-stretch ant-flex-gap-small ant-flex-vertical"
@ -3800,11 +3836,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start ant-divider-plain"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Preview
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<div
class="ant-flex css-var-test-id ant-flex-align-flex-start ant-flex-gap-small ant-flex-vertical"

View File

@ -980,11 +980,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start ant-divider-plain"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Preview
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<div
class="ant-flex css-var-test-id ant-flex-align-stretch ant-flex-gap-small ant-flex-vertical"
@ -1306,11 +1312,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center ant-divider-plain"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
https://github.com/ant-design/ant-design/issues/51811
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>
<div>
<button
@ -1357,11 +1369,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center ant-divider-plain"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
https://github.com/ant-design/ant-design/issues/52124
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>
<div>
<button
@ -1410,11 +1428,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center ant-divider-plain"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
https://github.com/ant-design/ant-design/issues/51380
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>
<div>
<button
@ -1587,11 +1611,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center ant-divider-plain"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
https://github.com/ant-design/ant-design/issues/51380
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>
<div
class="ant-flex css-var-test-id ant-flex-gap-small"
@ -2281,11 +2311,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start ant-divider-plain"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Preview
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<div
class="ant-flex css-var-test-id ant-flex-align-stretch ant-flex-gap-small ant-flex-vertical"
@ -3291,11 +3327,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start ant-divider-plain"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Preview
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<div
class="ant-flex css-var-test-id ant-flex-align-flex-start ant-flex-gap-small ant-flex-vertical"

View File

@ -513,6 +513,7 @@ describe('Button', () => {
fireEvent.click(getByRole('link'));
expect(handleClick).toHaveBeenCalled();
});
it('should support classnames and styles', () => {
const cusomStyles = {
root: { color: 'red' },
@ -524,20 +525,27 @@ describe('Button', () => {
icon: 'custom-icon',
content: 'custom-content',
};
const { container } = render(
const { container, rerender, getByText } = render(
<Button classNames={customClassNames} styles={cusomStyles} icon={<SearchOutlined />}>
antd
</Button>,
);
const root = container.querySelector('.ant-btn') as HTMLElement;
const icon = container.querySelector('.ant-btn-icon') as HTMLElement;
const root = container.querySelector('.ant-btn');
const icon = container.querySelector('.ant-btn-icon');
const content = getByText('antd');
expect(root).toHaveClass(customClassNames.root);
expect(icon).toHaveClass(customClassNames.icon);
expect(root).toHaveStyle(cusomStyles.root);
expect(icon).toHaveStyle(cusomStyles.icon);
expect(container.querySelector(`.${customClassNames.content}`)).toHaveStyle(
cusomStyles.content,
expect(content).toHaveStyle(cusomStyles.content);
rerender(
<Button classNames={customClassNames} styles={cusomStyles} loading>
antd
</Button>,
);
const loadingIcon = container.querySelector('.ant-btn-icon');
expect(loadingIcon).toHaveClass(customClassNames.icon);
expect(loadingIcon).toHaveStyle(cusomStyles.icon);
});
it('should support customizing the background color of default type button in disabled state', () => {

View File

@ -27,7 +27,7 @@ import Compact from './style/compact';
export type LegacyButtonType = ButtonType | 'danger';
type SemanticName = 'root' | 'icon' | 'content';
export type ButtonSemanticName = 'root' | 'icon' | 'content';
export interface BaseButtonProps {
type?: ButtonType;
color?: ButtonColorType;
@ -46,8 +46,8 @@ export interface BaseButtonProps {
block?: boolean;
children?: React.ReactNode;
[key: `data-${string}`]: string;
classNames?: Partial<Record<SemanticName, string>>;
styles?: Partial<Record<SemanticName, React.CSSProperties>>;
classNames?: Partial<Record<ButtonSemanticName, string>>;
styles?: Partial<Record<ButtonSemanticName, React.CSSProperties>>;
}
type MergedHTMLAttributes = Omit<
@ -351,6 +351,8 @@ const InternalCompoundedButton = React.forwardRef<
</IconWrapper>
) : (
<DefaultLoadingIcon
className={iconClasses}
style={iconStyle}
existIcon={!!icon}
prefixCls={prefixCls}
loading={innerLoading}

View File

@ -2,7 +2,7 @@ import React from 'react';
import { AntDesignOutlined } from '@ant-design/icons';
import { Button } from 'antd';
import SemanticPreview from '../../../.dumi/components/SemanticPreview';
import SemanticPreview from '../../../.dumi/theme/common/SemanticPreview';
import useLocale from '../../../.dumi/hooks/useLocale';
const locales = {

View File

@ -22,7 +22,7 @@ const App: React.FC = () => {
<Radio.Button value="default">Default</Radio.Button>
<Radio.Button value="small">Small</Radio.Button>
</Radio.Group>
<Divider orientation="left" plain>
<Divider titlePlacement="start" plain>
Preview
</Divider>
<ConfigProvider componentSize={size}>

View File

@ -13,7 +13,7 @@ const App: React.FC = () => {
<Radio.Button value="end">end</Radio.Button>
</Radio.Group>
</Space>
<Divider orientation="left" plain>
<Divider titlePlacement="start" plain>
Preview
</Divider>
<Flex gap="small" vertical>

View File

@ -14,7 +14,7 @@ const App: React.FC = () => {
<Radio.Button value="default">Default</Radio.Button>
<Radio.Button value="small">Small</Radio.Button>
</Radio.Group>
<Divider orientation="left" plain>
<Divider titlePlacement="start" plain>
Preview
</Divider>
<Flex gap="small" align="flex-start" vertical>

View File

@ -1,7 +1,7 @@
import React from 'react';
import { Calendar } from 'antd';
import SemanticPreview from '../../../.dumi/components/SemanticPreview';
import SemanticPreview from '../../../.dumi/theme/common/SemanticPreview';
import useLocale from '../../../.dumi/hooks/useLocale';
const locales = {

View File

@ -2,7 +2,7 @@ import React from 'react';
import { EditOutlined, EllipsisOutlined, SettingOutlined } from '@ant-design/icons';
import { Avatar, Card } from 'antd';
import SemanticPreview from '../../../.dumi/components/SemanticPreview';
import SemanticPreview from '../../../.dumi/theme/common/SemanticPreview';
import useLocale from '../../../.dumi/hooks/useLocale';
const { Meta } = Card;

View File

@ -1,7 +1,7 @@
import React from 'react';
import { Avatar, Card } from 'antd';
import SemanticPreview from '../../../.dumi/components/SemanticPreview';
import SemanticPreview from '../../../.dumi/theme/common/SemanticPreview';
import useLocale from '../../../.dumi/hooks/useLocale';
const { Meta } = Card;

View File

@ -595,7 +595,7 @@ exports[`renders components/cascader/demo/custom-dropdown.tsx extend context cor
</ul>
</div>
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
style="margin: 0px;"
/>

View File

@ -15,8 +15,10 @@ exports[`Cascader can be selected 1`] = `
>
<li
aria-checked="false"
aria-label="Zhejiang"
class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active"
data-path-key="zhejiang"
data-title="Zhejiang"
role="menuitemcheckbox"
title="Zhejiang"
>
@ -92,8 +94,10 @@ exports[`Cascader can be selected 1`] = `
>
<li
aria-checked="false"
aria-label="Hangzhou"
class="ant-cascader-menu-item ant-cascader-menu-item-expand"
data-path-key="zhejiang__RC_CASCADER_SPLIT__hangzhou"
data-title="Hangzhou"
role="menuitemcheckbox"
title="Hangzhou"
>
@ -147,8 +151,10 @@ exports[`Cascader can be selected 2`] = `
>
<li
aria-checked="false"
aria-label="Zhejiang"
class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active"
data-path-key="zhejiang"
data-title="Zhejiang"
role="menuitemcheckbox"
title="Zhejiang"
>
@ -224,8 +230,10 @@ exports[`Cascader can be selected 2`] = `
>
<li
aria-checked="false"
aria-label="Hangzhou"
class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active"
data-path-key="zhejiang__RC_CASCADER_SPLIT__hangzhou"
data-title="Hangzhou"
role="menuitemcheckbox"
title="Hangzhou"
>
@ -297,8 +305,10 @@ exports[`Cascader can be selected 3`] = `
>
<li
aria-checked="false"
aria-label="Zhejiang"
class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active"
data-path-key="zhejiang"
data-title="Zhejiang"
role="menuitemcheckbox"
title="Zhejiang"
>
@ -374,8 +384,10 @@ exports[`Cascader can be selected 3`] = `
>
<li
aria-checked="false"
aria-label="Hangzhou"
class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active"
data-path-key="zhejiang__RC_CASCADER_SPLIT__hangzhou"
data-title="Hangzhou"
role="menuitemcheckbox"
title="Hangzhou"
>
@ -1241,8 +1253,10 @@ exports[`Cascader popup correctly with defaultValue 1`] = `
>
<li
aria-checked="false"
aria-label="Zhejiang"
class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active"
data-path-key="zhejiang"
data-title="Zhejiang"
role="menuitemcheckbox"
title="Zhejiang"
>
@ -1318,8 +1332,10 @@ exports[`Cascader popup correctly with defaultValue 1`] = `
>
<li
aria-checked="true"
aria-label="Hangzhou"
class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active"
data-path-key="zhejiang__RC_CASCADER_SPLIT__hangzhou"
data-title="Hangzhou"
role="menuitemcheckbox"
title="Hangzhou"
>
@ -1428,8 +1444,10 @@ exports[`Cascader popup correctly with defaultValue RTL 1`] = `
>
<li
aria-checked="false"
aria-label="Zhejiang"
class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active"
data-path-key="zhejiang"
data-title="Zhejiang"
role="menuitemcheckbox"
title="Zhejiang"
>
@ -1505,8 +1523,10 @@ exports[`Cascader popup correctly with defaultValue RTL 1`] = `
>
<li
aria-checked="true"
aria-label="Hangzhou"
class="ant-cascader-menu-item ant-cascader-menu-item-expand ant-cascader-menu-item-active"
data-path-key="zhejiang__RC_CASCADER_SPLIT__hangzhou"
data-title="Hangzhou"
role="menuitemcheckbox"
title="Hangzhou"
>

View File

@ -40,10 +40,14 @@ const options = [
{
value: 'zhejiang',
label: 'Zhejiang',
'aria-label': 'Zhejiang',
'data-title': 'Zhejiang',
children: [
{
value: 'hangzhou',
label: 'Hangzhou',
'aria-label': 'Hangzhou',
'data-title': 'Hangzhou',
children: [
{
value: 'xihu',
@ -788,4 +792,14 @@ describe('Cascader', () => {
errSpy.mockRestore();
});
it('Support aria-* and data-* in options', () => {
const { container } = render(
<Cascader options={options} open defaultValue={['zhejiang', 'hangzhou']} />,
);
const menuItems = container.querySelectorAll('.ant-cascader-menu-item');
expect(menuItems[0].getAttribute('aria-label')).toBe('Zhejiang');
expect(menuItems[0].getAttribute('data-title')).toBe('Zhejiang');
expect(menuItems[2].getAttribute('aria-label')).toBe('Hangzhou');
expect(menuItems[2].getAttribute('data-title')).toBe('Hangzhou');
});
});

View File

@ -1,7 +1,7 @@
import React from 'react';
import { Cascader } from 'antd';
import TemplateSemanticPreview from '../../../.dumi/components/SelectSemanticTemplate';
import TemplateSemanticPreview from '../../../.dumi/theme/common/SelectSemanticTemplate';
interface Option {
value: string;

View File

@ -48,7 +48,7 @@ Array [
</span>
</label>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>,
<div

View File

@ -46,7 +46,7 @@ Array [
</span>
</label>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>,
<div

View File

@ -1,7 +1,7 @@
import React from 'react';
import { Checkbox } from 'antd';
import SemanticPreview from '../../../.dumi/components/SemanticPreview';
import SemanticPreview from '../../../.dumi/theme/common/SemanticPreview';
import useLocale from '../../../.dumi/hooks/useLocale';
const locales = {

View File

@ -199,28 +199,30 @@ export const genCheckboxStyle: GenerateStyle<CheckboxToken> = (token) => {
{
[checkboxCls]: {
'&-indeterminate': {
// Wrapper > Checkbox > inner
[`${checkboxCls}-inner`]: {
backgroundColor: `${token.colorBgContainer} !important`,
borderColor: `${token.colorBorder} !important`,
'&': {
// Wrapper > Checkbox > inner
[`${checkboxCls}-inner`]: {
backgroundColor: `${token.colorBgContainer}`,
borderColor: `${token.colorBorder}`,
'&:after': {
top: '50%',
insetInlineStart: '50%',
width: token.calc(token.fontSizeLG).div(2).equal(),
height: token.calc(token.fontSizeLG).div(2).equal(),
backgroundColor: token.colorPrimary,
border: 0,
transform: 'translate(-50%, -50%) scale(1)',
opacity: 1,
content: '""',
'&:after': {
top: '50%',
insetInlineStart: '50%',
width: token.calc(token.fontSizeLG).div(2).equal(),
height: token.calc(token.fontSizeLG).div(2).equal(),
backgroundColor: token.colorPrimary,
border: 0,
transform: 'translate(-50%, -50%) scale(1)',
opacity: 1,
content: '""',
},
},
},
// https://github.com/ant-design/ant-design/issues/50074
[`&:hover ${checkboxCls}-inner`]: {
backgroundColor: `${token.colorBgContainer} !important`,
borderColor: `${token.colorPrimary} !important`,
// https://github.com/ant-design/ant-design/issues/50074
[`&:hover ${checkboxCls}-inner`]: {
backgroundColor: `${token.colorBgContainer}`,
borderColor: `${token.colorPrimary}`,
},
},
},
},

View File

@ -1601,11 +1601,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Default Size
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<div
class="ant-collapse ant-collapse-icon-position-start css-var-test-id"
@ -1655,11 +1661,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Small Size
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<div
class="ant-collapse ant-collapse-icon-position-start ant-collapse-small css-var-test-id"
@ -1709,11 +1721,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Large Size
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<div
class="ant-collapse ant-collapse-icon-position-start ant-collapse-large css-var-test-id"

View File

@ -1500,11 +1500,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Default Size
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<div
class="ant-collapse ant-collapse-icon-position-start css-var-test-id"
@ -1554,11 +1560,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Small Size
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<div
class="ant-collapse ant-collapse-icon-position-start ant-collapse-small css-var-test-id"
@ -1608,11 +1620,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Large Size
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<div
class="ant-collapse ant-collapse-icon-position-start ant-collapse-large css-var-test-id"

View File

@ -3,7 +3,7 @@ import type { CollapseProps } from 'antd';
import { Collapse } from 'antd';
import { useToken } from '../../theme/internal';
import SemanticPreview from '../../../.dumi/components/SemanticPreview';
import SemanticPreview from '../../../.dumi/theme/common/SemanticPreview';
import useLocale from '../../../.dumi/hooks/useLocale';
const locales = {

View File

@ -9,16 +9,16 @@ const text = `
const App: React.FC = () => (
<>
<Divider orientation="left">Default Size</Divider>
<Divider titlePlacement="start">Default Size</Divider>
<Collapse
items={[{ key: '1', label: 'This is default size panel header', children: <p>{text}</p> }]}
/>
<Divider orientation="left">Small Size</Divider>
<Divider titlePlacement="start">Small Size</Divider>
<Collapse
size="small"
items={[{ key: '1', label: 'This is small size panel header', children: <p>{text}</p> }]}
/>
<Divider orientation="left">Large Size</Divider>
<Divider titlePlacement="start">Large Size</Divider>
<Collapse
size="large"
items={[{ key: '1', label: 'This is large size panel header', children: <p>{text}</p> }]}

View File

@ -5910,7 +5910,7 @@ exports[`renders components/color-picker/demo/panel-render.tsx extend context co
</div>
</div>
<div
class="ant-divider css-var-test-id ant-divider-vertical"
class="ant-divider css-var-test-id ant-divider-vertical ant-divider-rail"
role="separator"
style="height: auto;"
/>
@ -6658,7 +6658,7 @@ Array [
</div>
</div>
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>
<div

View File

@ -2,7 +2,7 @@ import React from 'react';
import type { ColorPickerProps } from 'antd';
import { ColorPicker } from 'antd';
import SemanticPreview from '../../../.dumi/components/SemanticPreview';
import SemanticPreview from '../../../.dumi/theme/common/SemanticPreview';
import useLocale from '../../../.dumi/hooks/useLocale';
const locales = {

View File

@ -27,7 +27,7 @@ const HorizontalLayoutDemo = () => {
<Col span={12}>
<Presets />
</Col>
<Divider type="vertical" style={{ height: 'auto' }} />
<Divider vertical style={{ height: 'auto' }} />
<Col flex="auto">
<Picker />
</Col>

View File

@ -14188,49 +14188,49 @@ exports[`ConfigProvider components DatePicker WeekPicker prefixCls 1`] = `
exports[`ConfigProvider components Divider configProvider 1`] = `
<div
class="config-divider css-var-root config-divider-horizontal"
class="config-divider css-var-root config-divider-horizontal config-divider-rail"
role="separator"
/>
`;
exports[`ConfigProvider components Divider configProvider componentDisabled 1`] = `
<div
class="config-divider css-var-root config-divider-horizontal"
class="config-divider css-var-root config-divider-horizontal config-divider-rail"
role="separator"
/>
`;
exports[`ConfigProvider components Divider configProvider componentSize large 1`] = `
<div
class="config-divider css-var-root config-divider-horizontal"
class="config-divider css-var-root config-divider-horizontal config-divider-rail"
role="separator"
/>
`;
exports[`ConfigProvider components Divider configProvider componentSize middle 1`] = `
<div
class="config-divider css-var-root config-divider-horizontal config-divider-md"
class="config-divider css-var-root config-divider-horizontal config-divider-md config-divider-rail"
role="separator"
/>
`;
exports[`ConfigProvider components Divider configProvider componentSize small 1`] = `
<div
class="config-divider css-var-root config-divider-horizontal config-divider-sm"
class="config-divider css-var-root config-divider-horizontal config-divider-sm config-divider-rail"
role="separator"
/>
`;
exports[`ConfigProvider components Divider normal 1`] = `
<div
class="ant-divider css-var-root ant-divider-horizontal"
class="ant-divider css-var-root ant-divider-horizontal ant-divider-rail"
role="separator"
/>
`;
exports[`ConfigProvider components Divider prefixCls 1`] = `
<div
class="prefix-Divider css-var-root prefix-Divider-horizontal"
class="prefix-Divider css-var-root prefix-Divider-horizontal prefix-Divider-rail"
role="separator"
/>
`;
@ -39072,7 +39072,7 @@ Array [
`;
exports[`ConfigProvider components Timeline configProvider 1`] = `
<ul
<ol
class="config-timeline css-var-root config-timeline-css-var"
>
<li
@ -39090,11 +39090,11 @@ exports[`ConfigProvider components Timeline configProvider 1`] = `
Bamboo
</div>
</li>
</ul>
</ol>
`;
exports[`ConfigProvider components Timeline configProvider componentDisabled 1`] = `
<ul
<ol
class="config-timeline css-var-root config-timeline-css-var"
>
<li
@ -39112,11 +39112,11 @@ exports[`ConfigProvider components Timeline configProvider componentDisabled 1`]
Bamboo
</div>
</li>
</ul>
</ol>
`;
exports[`ConfigProvider components Timeline configProvider componentSize large 1`] = `
<ul
<ol
class="config-timeline css-var-root config-timeline-css-var"
>
<li
@ -39134,11 +39134,11 @@ exports[`ConfigProvider components Timeline configProvider componentSize large 1
Bamboo
</div>
</li>
</ul>
</ol>
`;
exports[`ConfigProvider components Timeline configProvider componentSize middle 1`] = `
<ul
<ol
class="config-timeline css-var-root config-timeline-css-var"
>
<li
@ -39156,11 +39156,11 @@ exports[`ConfigProvider components Timeline configProvider componentSize middle
Bamboo
</div>
</li>
</ul>
</ol>
`;
exports[`ConfigProvider components Timeline configProvider componentSize small 1`] = `
<ul
<ol
class="config-timeline css-var-root config-timeline-css-var"
>
<li
@ -39178,11 +39178,11 @@ exports[`ConfigProvider components Timeline configProvider componentSize small 1
Bamboo
</div>
</li>
</ul>
</ol>
`;
exports[`ConfigProvider components Timeline normal 1`] = `
<ul
<ol
class="ant-timeline css-var-root ant-timeline-css-var"
>
<li
@ -39200,11 +39200,11 @@ exports[`ConfigProvider components Timeline normal 1`] = `
Bamboo
</div>
</li>
</ul>
</ol>
`;
exports[`ConfigProvider components Timeline prefixCls 1`] = `
<ul
<ol
class="prefix-Timeline css-var-root prefix-Timeline-css-var"
>
<li
@ -39222,7 +39222,7 @@ exports[`ConfigProvider components Timeline prefixCls 1`] = `
Bamboo
</div>
</li>
</ul>
</ol>
`;
exports[`ConfigProvider components Tooltip configProvider 1`] = `

View File

@ -18,6 +18,7 @@ import type { CollapseProps } from '../collapse';
import type { ColorPickerProps } from '../color-picker';
import type { DatePickerProps, RangePickerProps } from '../date-picker';
import type { DescriptionsProps } from '../descriptions';
import type { DividerProps } from '../divider';
import type { DrawerProps } from '../drawer';
import type { DropdownProps } from '../dropdown';
import type { EmptyProps } from '../empty';
@ -25,8 +26,9 @@ import type { FlexProps } from '../flex/interface';
import type { FloatButtonGroupProps } from '../float-button/interface';
import type { FormProps } from '../form/Form';
import type { ImageProps } from '../image';
import type { InputProps, TextAreaProps } from '../input';
import type { InputProps, SearchProps, TextAreaProps } from '../input';
import type { InputNumberProps } from '../input-number';
import type { OTPProps } from '../input/OTP';
import type { ListItemProps } from '../list';
import type { Locale } from '../locale';
import type { MasonryProps } from '../masonry';
@ -215,9 +217,13 @@ export type BreadcrumbConfig = ComponentStyleConfig &
export type InputConfig = ComponentStyleConfig &
Pick<InputProps, 'autoComplete' | 'classNames' | 'styles' | 'allowClear' | 'variant'>;
export type InputSearchConfig = ComponentStyleConfig & Pick<SearchProps, 'classNames' | 'styles'>;
export type TextAreaConfig = ComponentStyleConfig &
Pick<TextAreaProps, 'autoComplete' | 'classNames' | 'styles' | 'allowClear' | 'variant'>;
export type OTPConfig = ComponentStyleConfig & Pick<OTPProps, 'classNames' | 'styles'>;
export type ButtonConfig = ComponentStyleConfig &
Pick<ButtonProps, 'classNames' | 'styles' | 'autoInsertSpace' | 'variant' | 'color'>;
@ -243,6 +249,8 @@ export type CardMetaConfig = ComponentStyleConfig & Pick<CardMetaProps, 'classNa
export type DrawerConfig = ComponentStyleConfig &
Pick<DrawerProps, 'classNames' | 'styles' | 'closeIcon' | 'closable'>;
export type DividerConfig = ComponentStyleConfig & Pick<DividerProps, 'classNames' | 'styles'>;
export type DropdownConfig = ComponentStyleConfig & Pick<DropdownProps, 'classNames' | 'styles'>;
export type FlexConfig = ComponentStyleConfig & Pick<FlexProps, 'vertical'>;
@ -364,7 +372,9 @@ export interface WaveConfig {
export interface ConfigComponentProps {
input?: InputConfig;
inputSearch?: InputSearchConfig;
textArea?: TextAreaConfig;
otp?: OTPConfig;
inputNumber?: InputNumberConfig;
pagination?: PaginationConfig;
space?: SpaceConfig;
@ -375,7 +385,7 @@ export interface ConfigComponentProps {
affix?: ComponentStyleConfig;
anchor?: AnchorStyleConfig;
button?: ButtonConfig;
divider?: ComponentStyleConfig;
divider?: DividerConfig;
drawer?: DrawerConfig;
calendar?: CalendarConfig;
carousel?: ComponentStyleConfig;

View File

@ -163,7 +163,7 @@ const Page: React.FC<{ placement: Placement }> = ({ placement }) => {
<div className="direction-components">
<Row>
<Col span={24}>
<Divider orientation="left">Cascader example</Divider>
<Divider titlePlacement="start">Cascader example</Divider>
<Cascader
suffixIcon={<SearchIcon />}
options={cascaderOptions}
@ -185,7 +185,7 @@ const Page: React.FC<{ placement: Placement }> = ({ placement }) => {
<br />
<Row>
<Col span={12}>
<Divider orientation="left">Switch example</Divider>
<Divider titlePlacement="start">Switch example</Divider>
&nbsp;&nbsp;
<Switch defaultChecked />
&nbsp;&nbsp;
@ -194,7 +194,7 @@ const Page: React.FC<{ placement: Placement }> = ({ placement }) => {
<Switch size="small" loading />
</Col>
<Col span={12}>
<Divider orientation="left">Radio Group example</Divider>
<Divider titlePlacement="start">Radio Group example</Divider>
<Radio.Group defaultValue="c" buttonStyle="solid">
<Radio.Button value="a">تهران</Radio.Button>
<Radio.Button value="b" disabled>
@ -208,7 +208,7 @@ const Page: React.FC<{ placement: Placement }> = ({ placement }) => {
<br />
<Row>
<Col span={12}>
<Divider orientation="left">Button example</Divider>
<Divider titlePlacement="start">Button example</Divider>
<div className="button-demo">
<Button type="primary" icon={<DownloadOutlined />} />
<Button type="primary" shape="circle" icon={<DownloadOutlined />} />
@ -239,7 +239,7 @@ const Page: React.FC<{ placement: Placement }> = ({ placement }) => {
</div>
</Col>
<Col span={12}>
<Divider orientation="left">Tree example</Divider>
<Divider titlePlacement="start">Tree example</Divider>
<Tree
showLine
checkable
@ -262,7 +262,7 @@ const Page: React.FC<{ placement: Placement }> = ({ placement }) => {
<br />
<Row>
<Col span={24}>
<Divider orientation="left">Input (Input Group) example</Divider>
<Divider titlePlacement="start">Input (Input Group) example</Divider>
<InputGroup size="large">
<Row gutter={8}>
<Col span={5}>
@ -297,7 +297,7 @@ const Page: React.FC<{ placement: Placement }> = ({ placement }) => {
<br />
<Row>
<Col span={12}>
<Divider orientation="left">Select example</Divider>
<Divider titlePlacement="start">Select example</Divider>
<Space wrap>
<Select mode="multiple" defaultValue="مورچه" style={{ width: 120 }}>
<Option value="jack">Jack</Option>
@ -321,7 +321,7 @@ const Page: React.FC<{ placement: Placement }> = ({ placement }) => {
</Space>
</Col>
<Col span={12}>
<Divider orientation="left">TreeSelect example</Divider>
<Divider titlePlacement="start">TreeSelect example</Divider>
<TreeSelect
showSearch
style={{ width: '100%' }}
@ -349,7 +349,7 @@ const Page: React.FC<{ placement: Placement }> = ({ placement }) => {
<br />
<Row>
<Col span={24}>
<Divider orientation="left">Modal example</Divider>
<Divider titlePlacement="start">Modal example</Divider>
<Button type="primary" onClick={showModal}>
Open Modal
</Button>
@ -363,7 +363,7 @@ const Page: React.FC<{ placement: Placement }> = ({ placement }) => {
<br />
<Row>
<Col span={24}>
<Divider orientation="left">Steps example</Divider>
<Divider titlePlacement="start">Steps example</Divider>
<Steps
progressDot
current={currentStep}
@ -406,7 +406,7 @@ const Page: React.FC<{ placement: Placement }> = ({ placement }) => {
<br />
<Row>
<Col span={12}>
<Divider orientation="left">Rate example</Divider>
<Divider titlePlacement="start">Rate example</Divider>
<Rate defaultValue={2.5} />
<br />
<strong>* Note:</strong> Half star not implemented in RTL direction, it will be
@ -417,7 +417,7 @@ const Page: React.FC<{ placement: Placement }> = ({ placement }) => {
implement rtl support.
</Col>
<Col span={12}>
<Divider orientation="left">Badge example</Divider>
<Divider titlePlacement="start">Badge example</Divider>
<Badge count={badgeCount}>
<a href="#" className="head-example" />
</Badge>
@ -443,14 +443,14 @@ const Page: React.FC<{ placement: Placement }> = ({ placement }) => {
<br />
<Row>
<Col span={24}>
<Divider orientation="left">Pagination example</Divider>
<Divider titlePlacement="start">Pagination example</Divider>
<Pagination showSizeChanger defaultCurrent={3} total={500} />
</Col>
</Row>
<br />
<Row>
<Col span={24}>
<Divider orientation="left">Grid System example</Divider>
<Divider titlePlacement="start">Grid System example</Divider>
<div className="grid-demo">
<div className="code-box-demo">
<p>

View File

@ -188,7 +188,7 @@ const Page: React.FC = () => {
/>
</Space>
<Upload listType="picture-card" fileList={fileList} />
<Divider orientation="left">Tour</Divider>
<Divider titlePlacement="start">Tour</Divider>
<Button type="primary" onClick={() => setTourOpen(true)}>
Begin Tour
</Button>

View File

@ -126,7 +126,7 @@ const {
| datePicker | Set datePicker common props | { className?: string, style?: React.CSSProperties, classNames?: [DatePickerConfig\["classNames"\]](/components/date-picker#semantic-dom), styles?: [DatePickerConfig\["styles"\]](/components/date-picker#semantic-dom) } | - | 5.7.0 |
| rangePicker | Set rangePicker common props | { className?: string, style?: React.CSSProperties } | - | 5.11.0 |
| descriptions | Set Descriptions common props | { className?: string, style?: React.CSSProperties, classNames?: [DescriptionsProps\["classNames"\]](/components/descriptions#semantic-dom), styles?: [DescriptionsProps\["styles"\]](/components/descriptions#semantic-dom) } | - | 5.7.0, `classNames` and `styles`: 5.23.0 |
| divider | Set Divider common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| divider | Set Divider common props | { className?: string, style?: React.CSSProperties, classNames?: [DividerProps\["classNames"\]](/components/divider#semantic-dom), styles?: [DividerProps\["styles"\]](/components/divider#semantic-dom) } | - | |
| drawer | Set Drawer common props | { className?: string, style?: React.CSSProperties, classNames?: [DrawerProps\["classNames"\]](/components/drawer#semantic-dom), styles?: [DrawerProps\["styles"\]](/components/drawer#semantic-dom), closeIcon?: ReactNode } | - | 5.7.0, `classNames` and `styles`: 5.10.0, `closeIcon`: 5.14.0 |
| dropdown | Set Dropdown common props | { className?: string, style?: React.CSSProperties, classNames?: [DropdownConfig\["classNames"\]](/components/dropdown#semantic-dom), styles?: [DropdownConfig\["styles"\]](/components/dropdown#semantic-dom) } | - | |
| empty | Set Empty common props | { className?: string, style?: React.CSSProperties, classNames?: [EmptyProps\["classNames"\]](/components/empty#semantic-dom), styles?: [EmptyProps\["styles"\]](/components/empty#semantic-dom) } | - | 5.7.0, `classNames` and `styles`: 5.23.0 |
@ -136,7 +136,9 @@ const {
| image | Set Image common props | { className?: string, style?: React.CSSProperties, preview?: { closeIcon?: React.ReactNode, classNames?:[ImageConfig\["classNames"\]](/components/image#semantic-dom), styles?: [ImageConfig\["styles"\]](/components/image#semantic-dom) } } | - | 5.7.0, `closeIcon`: 5.14.0, `classNames` and `styles`: 6.0.0 |
| input | Set Input common props | { autoComplete?: string, className?: string, style?: React.CSSProperties, allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 4.2.0, `allowClear`: 5.15.0 |
| inputNumber | Set InputNumber common props | { className?: string, style?: React.CSSProperties, classNames?: [InputNumberConfig\["classNames"\]](/components/input-number#semantic-dom), styles?: [InputNumberConfig\["styles"\]](/components/input-number#semantic-dom) } | - | |
| textArea | Set TextArea common props | { autoComplete?: string, className?: string, style?: React.CSSProperties, allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 5.15.0 |
| otp | Set OTP common props | { className?: string, style?: React.CSSProperties, classNames?: [OTPConfig\["classNames"\]](/components/input#semantic-otp), styles?: [OTPConfig\["styles"\]](/components/input#semantic-otp) } | - | |
| inputSearch | Set Search common props | { className?: string, style?: React.CSSProperties, classNames?: [InputSearchConfig\["classNames"\]](/components/input#semantic-search), styles?: [InputSearchConfig\["styles"\]](/components/input#semantic-search) } | - | |
| textArea | Set TextArea common props | { autoComplete?: string, className?: string, style?: React.CSSProperties,classNames?:[TextAreaConfig\["classNames"\]](/components/input#semantic-textarea), styles?: [TextAreaConfig\["styles"\]](/components/input#semantic-textarea), allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 5.15.0 |
| layout | Set Layout common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| list | Set List common props | { className?: string, style?: React.CSSProperties, item?:{ classNames: [ListItemProps\["classNames"\]](/components/list#listitem), styles: [ListItemProps\["styles"\]](/components/list#listitem) } } | - | 5.7.0 |
| masonry | Set Masonry common props | { className?: string, style?: React.CSSProperties, classNames?: [MasonryProps\["classNames"\]](/components/masonry#semantic-dom), styles?: [MasonryProps\["styles"\]](/components/masonry#semantic-dom) } | - | |
@ -161,7 +163,7 @@ const {
| spin | Set Spin common props | { className?: string, style?: React.CSSProperties, indicator?: React.ReactElement, classNames?:[SpinConfig\["classNames"\]](/components/spin#semantic-dom), styles?: [SpinConfig\["styles"\]](/components/spin#semantic-dom) } | - | 5.7.0, `indicator`: 5.20.0, `classNames` and `styles`: 6.0.0 |
| statistic | Set Statistic common props | { className?: string, style?: React.CSSProperties, classNames?: [StatisticProps\["classNames"\]](/components/statistic#semantic-dom), styles?: [StatisticProps\["styles"\]](/components/statistic#semantic-dom)} | - | 5.7.0, `classNames` and `styles`: 6.0.0 |
| steps | Set Steps common props | { className?: string, style?: React.CSSProperties, classNames?:[StepsConfig\["classNames"\]](/components/steps#semantic-dom), styles?: [StepsConfig\["styles"\]](/components/steps#semantic-dom) } | - | |
| table | Set Table common props | { className?: string, style?: React.CSSProperties, expandable?: { expandIcon?: props => React.ReactNode }, classNames?: [TableProps\["classNames"\]](/components/table#semantic-dom), styles?: [TableProps\["styles"\]](/components/table#semantic-dom) } | - | |
| table | Set Table common props | { className?: string, style?: React.CSSProperties, expandable?: { expandIcon?: props => React.ReactNode }, classNames?: [TableProps\["classNames"\]](/components/table#semantic-dom), styles?: [TableProps\["styles"\]](/components/table#semantic-dom) } | - | |
| tabs | Set Tabs common props | { className?: string, style?: React.CSSProperties, indicator?: { size?: GetIndicatorSize, align?: `start` \| `center` \| `end` }, moreIcon?: ReactNode, addIcon?: ReactNode, removeIcon?: ReactNode, classNames?: [TabsConfig\["classNames"\]](/components/tabs#semantic-dom), styles?: [TabsConfig\["styles"\]](/components/tabs#semantic-dom) } | - | 5.7.0, `moreIcon` and `addIcon`: 5.14.0, `removeIcon`: 5.15.0, `classNames` and `styles`: 6.0.0 |
| tag | Set Tag common props | { className?: string, style?: React.CSSProperties, closeIcon?: React.ReactNode, classNames?: [TagProps\["classNames"\]](/components/tag#semantic-dom), styles?: [TagProps\["styles"\]](/components/tag#semantic-dom) } | - | 5.7.0, `closeIcon`: 5.14.0, `classNames` and `styles`: 6.0.0 |
| timeline | Set Timeline common props | { className?: string, style?: React.CSSProperties, classNames?: [TimelineConfig\["classNames"\]](/components/timeline#semantic-dom), styles?: [TimelineConfig\["styles"\]](/components/timeline#semantic-dom) } | - | 5.7.0, `classNames` and `styles`: 6.0.0 |

View File

@ -35,7 +35,9 @@ import type {
FormConfig,
ImageConfig,
InputConfig,
InputSearchConfig,
InputNumberConfig,
OTPConfig,
ListConfig,
MasonryConfig,
MentionsConfig,
@ -158,6 +160,8 @@ export interface ConfigProviderProps {
variant?: Variant;
form?: FormConfig;
input?: InputConfig;
inputSearch?: InputSearchConfig;
otp?: OTPConfig;
inputNumber?: InputNumberConfig;
textArea?: TextAreaConfig;
select?: SelectConfig;
@ -372,6 +376,7 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
pagination,
input,
textArea,
otp,
empty,
badge,
radio,
@ -470,6 +475,7 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
image,
input,
textArea,
otp,
layout,
list,
mentions,

View File

@ -128,7 +128,7 @@ const {
| datePicker | 设置 DatePicker 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [DatePickerConfig\["classNames"\]](/components/date-picker-cn#semantic-dom), styles?: [DatePickerConfig\["styles"\]](/components/date-picker-cn#semantic-dom) } | - | 5.7.0 |
| rangePicker | 设置 RangePicker 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.11.0 |
| descriptions | 设置 Descriptions 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [DescriptionsProps\["classNames"\]](/components/descriptions-cn#semantic-dom), styles?: [DescriptionsProps\["styles"\]](/components/descriptions-cn#semantic-dom) } | - | 5.7.0, `classNames``styles`: 5.23.0 |
| divider | 设置 Divider 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| divider | 设置 Divider 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [DividerProps\["classNames"\]](/components/divider-cn#semantic-dom), styles?: [DividerProps\["styles"\]](/components/divider-cn#semantic-dom) } | - | |
| drawer | 设置 Drawer 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [DrawerProps\["classNames"\]](/components/drawer-cn#semantic-dom), styles?: [DrawerProps\["styles"\]](/components/drawer-cn#semantic-dom), closeIcon?: ReactNode } | - | 5.7.0, `classNames``styles`: 5.10.0, `closeIcon`: 5.14.0 |
| dropdown | 设置 Dropdown 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [DropdownConfig\["classNames"\]](/components/dropdown-cn#semantic-dom), styles?: [DropdownConfig\["styles"\]](/components/dropdown-cn#semantic-dom) } | - | |
| empty | 设置 Empty 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?:[EmptyProps\["classNames"\]](/components/empty-cn#semantic-dom), styles?: [EmptyProps\["styles"\]](/components/empty-cn#semantic-dom) } | - | 5.7.0, `classNames``styles`: 5.23.0 |
@ -136,9 +136,11 @@ const {
| floatButtonGroup | 设置 FloatButton.Group 组件的通用属性 | { closeIcon?: React.ReactNode } | - | 5.16.0 |
| form | 设置 Form 组件的通用属性 | { className?: string, style?: React.CSSProperties, validateMessages?: [ValidateMessages](/components/form-cn#validatemessages), requiredMark?: boolean \| `optional`, colon?: boolean, scrollToFirstError?: boolean \| [Options](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options), classNames?:[FormConfig\["classNames"\]](/components/form-cn#semantic-dom), styles?: [FormConfig\["styles"\]](/components/form-cn#semantic-dom) } | - | `requiredMark`: 4.8.0; `colon`: 4.18.0; `scrollToFirstError`: 5.2.0; `className``style`: 5.7.0 |
| image | 设置 Image 组件的通用属性 | { className?: string, style?: React.CSSProperties, preview?: { closeIcon?: React.ReactNode, classNames?:[ImageConfig\["classNames"\]](/components/image-cn#semantic-dom), styles?: [ImageConfig\["styles"\]](/components/image-cn#semantic-dom) } } | - | 5.7.0, `closeIcon`: 5.14.0, `classNames``styles`: 6.0.0 |
| input | 设置 Input 组件的通用属性 | { autoComplete?: string, className?: string, style?: React.CSSProperties, allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 5.7.0, `allowClear`: 5.15.0 |
| input | 设置 Input 组件的通用属性 | { autoComplete?: string, className?: string, style?: React.CSSProperties,classNames?:[InputConfig\["classNames"\]](/components/input-cn#semantic-input), styles?: [InputConfig\["styles"\]](/components/input-cn#semantic-input), allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 5.7.0, `allowClear`: 5.15.0 |
| inputNumber | 设置 Input 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [InputNumberConfig\["classNames"\]](/components/input-number-cn#semantic-dom), styles?: [InputNumberConfig\["styles"\]](/components/input-number-cn#semantic-dom) } | - | |
| textArea | 设置 TextArea 组件的通用属性 | { autoComplete?: string, className?: string, style?: React.CSSProperties, allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 5.15.0 |
| otp | 设置 OTP 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [OTPConfig\["classNames"\]](/components/input-cn#semantic-otp), styles?: [OTPConfig\["styles"\]](/components/input-cn#semantic-otp) } | - | |
| inputSearch | 设置 Search 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [InputSearchConfig\["classNames"\]](/components/input-cn#semantic-search), styles?: [InputSearchConfig\["styles"\]](/components/input-cn#semantic-search) } | - | |
| textArea | 设置 TextArea 组件的通用属性 | { autoComplete?: string, className?: string, style?: React.CSSProperties,classNames?:[TextAreaConfig\["classNames"\]](/components/input-cn#semantic-textarea), styles?: [TextAreaConfig\["styles"\]](/components/input-cn#semantic-textarea), allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 5.15.0 |
| layout | 设置 Layout 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| list | 设置 List 组件的通用属性 | { className?: string, style?: React.CSSProperties, item?:{ classNames: [ListItemProps\["classNames"\]](/components/list-cn#listitem), styles: [ListItemProps\["styles"\]](/components/list-cn#listitem) } } | - | 5.7.0 |
| masonry | 设置 Masonry 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [MasonryProps\["classNames"\]](/components/masonry#semantic-dom), styles?: [MasonryProps\["styles"\]](/components/masonry#semantic-dom) } | - | |
@ -163,7 +165,7 @@ const {
| spin | 设置 Spin 组件的通用属性 | { className?: string, style?: React.CSSProperties, indicator?: React.ReactElement, classNames?:[SpinConfig\["classNames"\]](/components/spin-cn#semantic-dom), styles?: [SpinConfig\["styles"\]](/components/spin-cn#semantic-dom) } | - | 5.7.0, `indicator`: 5.20.0, `classNames``styles`: 6.0.0 |
| statistic | 设置 Statistic 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [StatisticProps\["classNames"\]](/components/statistic-cn#semantic-dom), styles?: [StatisticProps\["styles"\]](/components/statistic-cn#semantic-dom) } | - | 5.7.0, `classNames``styles`: 6.0.0 |
| steps | 设置 Steps 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?:[StepsConfig\["classNames"\]](/components/steps#semantic-dom), styles?: [StepsConfig\["styles"\]](/components/steps#semantic-dom) } | - | |
| table | 设置 Table 组件的通用属性 | { className?: string, style?: React.CSSProperties, expandable?: { expandIcon?: props => React.ReactNode }, classNames?: [TableProps\["classNames"\]](/components/table-cn#semantic-dom), styles?: [TableProps\["styles"\]](/components/table-cn#semantic-dom) } | - | |
| table | 设置 Table 组件的通用属性 | { className?: string, style?: React.CSSProperties, expandable?: { expandIcon?: props => React.ReactNode }, classNames?: [TableProps\["classNames"\]](/components/table-cn#semantic-dom), styles?: [TableProps\["styles"\]](/components/table-cn#semantic-dom) } | - | |
| tabs | 设置 Tabs 组件的通用属性 | { className?: string, style?: React.CSSProperties, indicator?: { size?: GetIndicatorSize, align?: `start` \| `center` \| `end` }, moreIcon?: ReactNode, addIcon?: ReactNode, removeIcon?: ReactNode, classNames?: [TabsConfig\["classNames"\]](/components/tabs-cn#semantic-dom), styles?: [TabsConfig\["styles"\]](/components/tabs-cn#semantic-dom) } | - | 5.7.0, `moreIcon` and `addIcon`: 5.14.0, `removeIcon`: 5.15.0, `classNames``styles`: 6.0.0 |
| tag | 设置 Tag 组件的通用属性 | { className?: string, style?: React.CSSProperties, closeIcon?: React.ReactNode, classNames?: [TagProps\["classNames"\]](/components/tag-cn#semantic-dom), styles?: [TagProps\["styles"\]](/components/tag-cn#semantic-dom) } | - | 5.7.0, closeIcon: 5.14.0, `classNames``styles`: 6.0.0 |
| timeline | 设置 Timeline 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [TimelineConfig\["classNames"\]](/components/timeline-cn#semantic-dom), styles?: [TimelineConfig\["styles"\]](/components/timeline-cn#semantic-dom) } | - | 5.7.0, `classNames``styles`: 6.0.0 |

View File

@ -2,7 +2,7 @@ import React from 'react';
import { SmileOutlined } from '@ant-design/icons';
import { DatePicker, Flex, Segmented } from 'antd';
import SemanticPreview from '../../../.dumi/components/SemanticPreview';
import SemanticPreview from '../../../.dumi/theme/common/SemanticPreview';
import useLocale from '../../../.dumi/hooks/useLocale';
const locales = {

View File

@ -1,5 +1,7 @@
import type * as React from 'react';
import type { Breakpoint } from '../_util/responsiveObserver';
type SemanticName = 'label' | 'content';
export interface DescriptionsItemProps {
prefixCls?: string;
@ -13,7 +15,7 @@ export interface DescriptionsItemProps {
styles?: Partial<Record<SemanticName, React.CSSProperties>>;
classNames?: Partial<Record<SemanticName, string>>;
children: React.ReactNode;
span?: number;
span?: number | 'filled' | { [key in Breakpoint]?: number };
}
// JSX Structure Syntactic Sugar. Never reach the render code.

View File

@ -2063,7 +2063,7 @@ Array [
</span>
</button>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>,
<div
@ -2116,7 +2116,7 @@ Array [
</label>
</div>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>,
<div
@ -2203,7 +2203,7 @@ Array [
</div>
</div>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>,
<div

View File

@ -1911,7 +1911,7 @@ Array [
</span>
</button>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>,
<div
@ -1964,7 +1964,7 @@ Array [
</label>
</div>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>,
<div
@ -2043,7 +2043,7 @@ Array [
</div>
</div>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>,
<div

View File

@ -0,0 +1,193 @@
import React from 'react';
import { render } from '../../../tests/utils';
import Descriptions from '..';
import { resetWarned } from '../../_util/warning';
describe('Descriptions.Item span property types', () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
afterEach(() => {
errorSpy.mockReset();
});
afterAll(() => {
errorSpy.mockRestore();
});
// 测试数字类型的 span
it('should support number type span in JSX mode', () => {
const { container } = render(
<Descriptions column={4}>
<Descriptions.Item label="Normal" span={1}>
Normal span
</Descriptions.Item>
<Descriptions.Item label="Double" span={2}>
Double span
</Descriptions.Item>
<Descriptions.Item label="Single">Default span (1)</Descriptions.Item>
</Descriptions>,
);
const items = container.querySelectorAll('.ant-descriptions-item');
expect(items[0]).toHaveAttribute('colSpan', '1');
expect(items[1]).toHaveAttribute('colSpan', '2');
expect(items[2]).toHaveAttribute('colSpan', '1');
});
// 测试 'filled' 类型的 span
it('should support "filled" type span in JSX mode', () => {
const { container } = render(
<Descriptions column={3}>
<Descriptions.Item label="Item 1">Content 1</Descriptions.Item>
<Descriptions.Item label="Item 2" span="filled">
This should fill the rest of the row
</Descriptions.Item>
<Descriptions.Item label="Item 3">Content 3</Descriptions.Item>
</Descriptions>,
);
const items = container.querySelectorAll('.ant-descriptions-item');
expect(items[0]).toHaveAttribute('colSpan', '1');
expect(items[1]).toHaveAttribute('colSpan', '2'); // 应该填充剩余的列
expect(items[2]).toHaveAttribute('colSpan', '3'); // 下一行应该占满整行
});
// 测试响应式对象类型的 span
it('should support responsive object type span in JSX mode', () => {
const { container } = render(
<Descriptions column={4}>
<Descriptions.Item label="Responsive" span={{ xs: 1, sm: 2, md: 3, lg: 4, xl: 2, xxl: 1 }}>
Responsive span
</Descriptions.Item>
<Descriptions.Item label="Normal">Normal content</Descriptions.Item>
</Descriptions>,
);
// 由于测试环境中无法真实模拟响应式断点,这里只能测试是否正确渲染
// 实际的响应式行为需要在浏览器环境中测试
expect(container.querySelectorAll('.ant-descriptions-item')).toHaveLength(2);
});
// 测试 items 模式下的所有 span 类型
it('should support all span types in items mode', () => {
const { container } = render(
<Descriptions
column={4}
items={[
{
key: '1',
label: 'Number Span',
children: 'Using number span',
span: 2,
},
{
key: '2',
label: 'Filled Span',
children: 'Using filled span',
span: 'filled',
},
{
key: '3',
label: 'Responsive Span',
children: 'Using responsive span',
span: { xs: 1, sm: 2, md: 3, lg: 4 },
},
{
key: '4',
label: 'Default Span',
children: 'Default span (1)',
},
]}
/>,
);
const items = container.querySelectorAll('.ant-descriptions-item');
expect(items).toHaveLength(4);
expect(items[0]).toHaveAttribute('colSpan', '2');
// 其他项的 colSpan 会根据实际渲染情况而定
});
// 测试混合使用不同类型的 span
it('should handle mixed span types in the same descriptions', () => {
const { container } = render(
<Descriptions
column={6}
items={[
// 第一行
{ key: '1', label: 'Number', children: 'Content', span: 2 },
{ key: '2', label: 'Default', children: 'Content' }, // 默认 span=1
{ key: '3', label: 'Responsive', children: 'Content', span: { xs: 1, sm: 2, md: 3 } },
// 第二行
{ key: '4', label: 'Filled', children: 'Content', span: 'filled' },
{ key: '5', label: 'Last', children: 'Content' },
]}
/>,
);
expect(container.querySelectorAll('.ant-descriptions-item')).toHaveLength(5);
});
// 测试 span 超出列数的警告
it('should warn when span exceeds column count', () => {
resetWarned();
render(
<Descriptions
column={3}
items={[{ key: '1', label: 'Excessive Span', children: 'Content', span: 4 }]}
/>,
);
expect(errorSpy).toHaveBeenCalledWith(
expect.stringContaining('Sum of column `span` in a line not match `column` of Descriptions.'),
);
});
// 测试多个 'filled' span 的行为
it('should handle multiple filled spans correctly', () => {
const { container } = render(
<Descriptions
column={4}
items={[
// 第一行
{ key: '1', label: 'Item 1', children: 'Content 1' },
{ key: '2', label: 'Filled 1', children: 'Content 2', span: 'filled' },
// 第二行
{ key: '3', label: 'Item 3', children: 'Content 3' },
{ key: '4', label: 'Filled 2', children: 'Content 4', span: 'filled' },
]}
/>,
);
const items = container.querySelectorAll('.ant-descriptions-item');
expect(items[0]).toHaveAttribute('colSpan', '1');
expect(items[1]).toHaveAttribute('colSpan', '3'); // 填充第一行剩余的列
expect(items[2]).toHaveAttribute('colSpan', '1');
expect(items[3]).toHaveAttribute('colSpan', '3'); // 填充第二行剩余的列
});
// 测试所有响应式断点
it('should support all responsive breakpoints', () => {
const { container } = render(
<Descriptions
column={6}
items={[
{
key: '1',
label: 'All Breakpoints',
children: 'Using all responsive breakpoints',
span: { xs: 1, sm: 2, md: 3, lg: 4, xl: 5, xxl: 6 },
},
{
key: '2',
label: 'Partial Breakpoints',
children: 'Using some responsive breakpoints',
span: { xs: 1, md: 3, xl: 5 },
},
]}
/>,
);
expect(container.querySelectorAll('.ant-descriptions-item')).toHaveLength(2);
});
});

View File

@ -2,7 +2,7 @@ import React from 'react';
import { Button, Descriptions, Divider, Switch } from 'antd';
import type { DescriptionsProps } from 'antd';
import SemanticPreview from '../../../.dumi/components/SemanticPreview';
import SemanticPreview from '../../../.dumi/theme/common/SemanticPreview';
import useLocale from '../../../.dumi/hooks/useLocale';
const locales = {

View File

@ -21,14 +21,14 @@ interface CompoundedComponent {
Item: typeof DescriptionsItem;
}
export interface InternalDescriptionsItemType extends DescriptionsItemProps {
export interface InternalDescriptionsItemType extends Omit<DescriptionsItemProps, 'span'> {
key?: React.Key;
filled?: boolean;
span?: number;
}
export interface DescriptionsItemType
extends Omit<InternalDescriptionsItemType, 'span' | 'filled'> {
span?: number | 'filled' | { [key in Breakpoint]?: number };
export interface DescriptionsItemType extends Omit<DescriptionsItemProps, 'prefixCls'> {
key?: React.Key;
}
type SemanticName = 'root' | 'header' | 'title' | 'extra' | 'label' | 'content';

View File

@ -9,11 +9,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Text
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
@ -22,11 +28,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Left Text
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
@ -35,39 +47,57 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-end"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Right Text
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start ant-divider-no-default-orientation-margin-start"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
style="margin-inline-start: 0;"
style="margin: 0px;"
>
Left Text with 0 orientationMargin
Left Text margin with 0
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-end ant-divider-no-default-orientation-margin-end"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-end"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
style="margin-inline-end: 50px;"
style="margin: 0px 50px;"
>
Right Text with 50px orientationMargin
Right Text margin with 50px
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
@ -80,12 +110,12 @@ exports[`renders components/divider/demo/component-token.tsx extend context corr
exports[`renders components/divider/demo/customize-style.tsx extend context correctly 1`] = `
Array [
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
style="border-width: 2px; border-color: #7cb305;"
/>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-dashed"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-dashed ant-divider-rail"
role="separator"
style="border-color: #7cb305;"
/>,
@ -94,19 +124,25 @@ Array [
role="separator"
style="border-color: #7cb305;"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Text
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<div
class="ant-divider css-var-test-id ant-divider-vertical"
class="ant-divider css-var-test-id ant-divider-vertical ant-divider-rail"
role="separator"
style="height: 60px; border-color: #7cb305;"
/>,
<div
class="ant-divider css-var-test-id ant-divider-vertical ant-divider-dashed"
class="ant-divider css-var-test-id ant-divider-vertical ant-divider-dashed ant-divider-rail"
role="separator"
style="height: 60px; border-color: #7cb305;"
/>,
@ -118,11 +154,17 @@ Array [
role="separator"
style="background: rgba(0, 255, 0, 0.05);"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Text
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>
</div>,
]
@ -136,14 +178,14 @@ Array [
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-dashed"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-dashed ant-divider-rail"
role="separator"
/>,
<p>
@ -163,11 +205,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center ant-divider-plain"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Text
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
@ -176,11 +224,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start ant-divider-plain"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Left Text
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
@ -189,11 +243,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-end ant-divider-plain"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Right Text
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
@ -209,21 +269,21 @@ Array [
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-sm"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-sm ant-divider-rail"
role="separator"
/>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-md"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-md ant-divider-rail"
role="separator"
/>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>,
<p>
@ -244,11 +304,17 @@ Array [
role="separator"
style="border-color: #7cb305;"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Solid
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
@ -258,11 +324,17 @@ Array [
role="separator"
style="border-color: #7cb305;"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Dotted
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
@ -272,11 +344,17 @@ Array [
role="separator"
style="border-color: #7cb305;"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Dashed
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
@ -290,7 +368,7 @@ exports[`renders components/divider/demo/vertical.tsx extend context correctly 1
Array [
Text,
<div
class="ant-divider css-var-test-id ant-divider-vertical"
class="ant-divider css-var-test-id ant-divider-vertical ant-divider-rail"
role="separator"
/>,
<a
@ -299,7 +377,7 @@ Array [
Link
</a>,
<div
class="ant-divider css-var-test-id ant-divider-vertical"
class="ant-divider css-var-test-id ant-divider-vertical ant-divider-rail"
role="separator"
/>,
<a
@ -321,11 +399,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Text
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
@ -334,11 +418,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Left Text
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
@ -347,39 +437,57 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-end"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Right Text
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start ant-divider-no-default-orientation-margin-start"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
style="margin-inline-start: 0;"
style="margin: 0px;"
>
Left Text with 0 orientationMargin
Left Text margin with 0
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-end ant-divider-no-default-orientation-margin-end"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-end"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
style="margin-inline-end: 50px;"
style="margin: 0px 50px;"
>
Right Text with 50px orientationMargin
Right Text margin with 50px
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.

View File

@ -9,11 +9,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Text
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
@ -22,11 +28,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Left Text
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
@ -35,39 +47,57 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-end"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Right Text
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start ant-divider-no-default-orientation-margin-start"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
style="margin-inline-start:0"
style="margin:0"
>
Left Text with 0 orientationMargin
Left Text margin with 0
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-end ant-divider-no-default-orientation-margin-end"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-end"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
style="margin-inline-end:50px"
style="margin:0 50px"
>
Right Text with 50px orientationMargin
Right Text margin with 50px
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
@ -78,12 +108,12 @@ Array [
exports[`renders components/divider/demo/customize-style.tsx correctly 1`] = `
Array [
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
style="border-width:2px;border-color:#7cb305"
/>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-dashed"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-dashed ant-divider-rail"
role="separator"
style="border-color:#7cb305"
/>,
@ -92,19 +122,25 @@ Array [
role="separator"
style="border-color:#7cb305"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Text
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<div
class="ant-divider css-var-test-id ant-divider-vertical"
class="ant-divider css-var-test-id ant-divider-vertical ant-divider-rail"
role="separator"
style="height:60px;border-color:#7cb305"
/>,
<div
class="ant-divider css-var-test-id ant-divider-vertical ant-divider-dashed"
class="ant-divider css-var-test-id ant-divider-vertical ant-divider-dashed ant-divider-rail"
role="separator"
style="height:60px;border-color:#7cb305"
/>,
@ -116,11 +152,17 @@ Array [
role="separator"
style="background:rgba(0,255,0,0.05)"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Text
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>
</div>,
]
@ -132,14 +174,14 @@ Array [
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-dashed"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-dashed ant-divider-rail"
role="separator"
/>,
<p>
@ -157,11 +199,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center ant-divider-plain"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Text
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
@ -170,11 +218,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start ant-divider-plain"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Left Text
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
@ -183,11 +237,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-end ant-divider-plain"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Right Text
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
@ -201,21 +261,21 @@ Array [
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-sm"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-sm ant-divider-rail"
role="separator"
/>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-md"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-md ant-divider-rail"
role="separator"
/>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>,
<p>
@ -234,11 +294,17 @@ Array [
role="separator"
style="border-color:#7cb305"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Solid
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
@ -248,11 +314,17 @@ Array [
role="separator"
style="border-color:#7cb305"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Dotted
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
@ -262,11 +334,17 @@ Array [
role="separator"
style="border-color:#7cb305"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Dashed
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
@ -278,7 +356,7 @@ exports[`renders components/divider/demo/vertical.tsx correctly 1`] = `
Array [
Text,
<div
class="ant-divider css-var-test-id ant-divider-vertical"
class="ant-divider css-var-test-id ant-divider-vertical ant-divider-rail"
role="separator"
/>,
<a
@ -287,7 +365,7 @@ Array [
Link
</a>,
<div
class="ant-divider css-var-test-id ant-divider-vertical"
class="ant-divider css-var-test-id ant-divider-vertical ant-divider-rail"
role="separator"
/>,
<a
@ -307,11 +385,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Text
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
@ -320,11 +404,17 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Left Text
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
@ -333,39 +423,57 @@ Array [
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-end"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
>
Right Text
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start ant-divider-no-default-orientation-margin-start"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-start"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
style="margin-inline-start:0"
style="margin:0"
>
Left Text with 0 orientationMargin
Left Text margin with 0
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-end ant-divider-no-default-orientation-margin-end"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-with-text ant-divider-with-text-end"
role="separator"
>
<div
class="ant-divider-rail ant-divider-rail-start"
/>
<span
class="ant-divider-inner-text"
style="margin-inline-end:50px"
style="margin:0 50px"
>
Right Text with 50px orientationMargin
Right Text margin with 50px
</span>
<div
class="ant-divider-rail ant-divider-rail-end"
/>
</div>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.

View File

@ -2,8 +2,10 @@ import * as React from 'react';
import { ConfigProvider } from 'antd';
import Divider from '..';
import type { Orientation } from '../../_util/hooks/useOrientation';
import mountTest from '../../../tests/shared/mountTest';
import { render } from '../../../tests/utils';
import type { TitlePlacement } from '../index';
describe('Divider', () => {
mountTest(Divider);
@ -19,7 +21,7 @@ describe('Divider', () => {
it('support string orientationMargin', () => {
const { container } = render(
<Divider orientation="right" orientationMargin="10">
<Divider titlePlacement="end" orientationMargin="10">
test test test
</Divider>,
);
@ -65,4 +67,49 @@ describe('Divider', () => {
rerender(<Divider type="vertical" size="small" />);
expect(container.querySelector<HTMLSpanElement>('.ant-divider-sm')).toBeTruthy();
});
describe('orientation and placement attribute', () => {
jest.spyOn(console, 'error').mockImplementation(() => {});
const testCases: Array<
[
params: [
orientation?: Orientation | TitlePlacement,
vertical?: boolean,
type?: Orientation,
titlePlacement?: TitlePlacement,
orientationMargin?: number,
],
expected: string,
]
> = [
[['right'], '.ant-divider-with-text-end'],
[['vertical', undefined, 'horizontal'], '.ant-divider-vertical'],
[[undefined, undefined, 'vertical'], '.ant-divider-vertical'],
[['center', undefined, undefined, 'left'], '.ant-divider-with-text-start'],
[['horizontal', true, undefined], '.ant-divider-horizontal'],
[[undefined, true, 'horizontal'], '.ant-divider-vertical'],
[['center', undefined, 'horizontal', 'left', 20], '.ant-divider-with-text-start'],
];
it.each(testCases)('with args %j should have %s node', (params, expected) => {
const { container } = render(
<Divider
orientation={params[0] as Orientation}
vertical={params[1]}
type={params[2]}
titlePlacement={params[3]}
{...(params[4] && { orientationMargin: params[4] })}
>
Bamboo
</Divider>,
);
expect(container.querySelector<HTMLSpanElement>(expected)).not.toBeNull();
if (params[4]) {
expect(
container
.querySelector<HTMLSpanElement>('.ant-divider-inner-text')
?.style.getPropertyValue('margin-inline-start'),
).toBe('20px');
}
});
});
});

View File

@ -0,0 +1,36 @@
import * as React from 'react';
import Divider from '..';
import mountTest from '../../../tests/shared/mountTest';
import { render } from '../../../tests/utils';
describe('Divider', () => {
mountTest(Divider);
it('not show children when vertical', () => {
const testClassNames = {
root: 'test-root',
rail: 'test-rail',
content: 'test-content',
};
const testStyles = {
root: { color: 'red' },
rail: { color: 'blue' },
content: { color: 'green' },
};
const { container } = render(
<Divider classNames={testClassNames} styles={testStyles}>
Text
</Divider>,
);
const root = container.querySelector('.ant-divider');
const rail = container.querySelector('.ant-divider-rail');
const content = container.querySelector('.ant-divider-inner-text');
expect(root).toHaveClass(testClassNames.root);
expect(root).toHaveStyle(testStyles.root);
expect(rail).toHaveClass(testClassNames.rail);
expect(rail).toHaveStyle(testStyles.rail);
expect(content).toHaveClass(testClassNames.content);
expect(content).toHaveStyle(testStyles.content);
});
});

View File

@ -0,0 +1,76 @@
import React from 'react';
import { Divider } from 'antd';
import type { DividerProps } from 'antd';
import useLocale from '../../../.dumi/hooks/useLocale';
import SemanticPreview from '../../../.dumi/theme/common/SemanticPreview';
const locales = {
cn: {
root: '根元素',
content: '内容元素',
rail: '背景条元素',
},
en: {
root: 'Root element',
content: 'Content element',
rail: 'Background rail element',
},
};
const Block: React.FC<DividerProps> = (props) => {
return (
<div>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi
ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>
<Divider {...props} />
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi
ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>
<Divider {...props}>Solid</Divider>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi
ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>
<Divider titlePlacement="left" variant="dotted" {...props}>
Dotted
</Divider>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi
ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>
<Divider titlePlacement="right" variant="dashed" {...props}>
Dashed
</Divider>
<>
These
<Divider orientation="vertical" {...props} />
are
<Divider orientation="vertical" {...props} />
vertical
<Divider orientation="vertical" {...props} />
Dividers
</>
</div>
);
};
const App: React.FC = () => {
const [locale] = useLocale(locales);
return (
<SemanticPreview
componentName="Divider"
semantics={[
{ name: 'root', desc: locale.root },
{ name: 'rail', desc: locale.rail },
{ name: 'content', desc: locale.content },
]}
>
<Block />
</SemanticPreview>
);
};
export default App;

View File

@ -28,25 +28,25 @@ const App: React.FC = () => (
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista
probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>
<Divider orientation="left">Left Text</Divider>
<Divider titlePlacement="start">Left Text</Divider>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista
probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>
<Divider orientation="right">Right Text</Divider>
<Divider titlePlacement="end">Right Text</Divider>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista
probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>
<Divider orientation="left" orientationMargin="0">
Left Text with 0 orientationMargin
<Divider titlePlacement="start" styles={{ content: { margin: 0 } }}>
Left Text margin with 0
</Divider>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista
probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>
<Divider orientation="right" orientationMargin={50}>
Right Text with 50px orientationMargin
<Divider titlePlacement="end" styles={{ content: { margin: '0 50px' } }}>
Right Text margin with 50px
</Divider>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista

View File

@ -8,11 +8,11 @@ const App: React.FC = () => (
<Divider style={{ borderColor: '#7cb305' }} dashed>
Text
</Divider>
<Divider type="vertical" style={{ height: 60, borderColor: '#7cb305' }} />
<Divider type="vertical" style={{ height: 60, borderColor: '#7cb305' }} dashed />
<Divider vertical style={{ height: 60, borderColor: '#7cb305' }} />
<Divider vertical style={{ height: 60, borderColor: '#7cb305' }} dashed />
<div style={{ display: 'flex', flexDirection: 'column', height: 50, boxShadow: '0 0 1px red' }}>
<Divider style={{ background: 'rgba(0,255,0,0.05)' }} orientation="left">
<Divider style={{ background: 'rgba(0,255,0,0.05)' }} titlePlacement="start">
Text
</Divider>
</div>

View File

@ -12,14 +12,14 @@ const App: React.FC = () => (
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista
probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>
<Divider orientation="left" plain>
<Divider titlePlacement="start" plain>
Left Text
</Divider>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista
probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>
<Divider orientation="right" plain>
<Divider titlePlacement="end" plain>
Right Text
</Divider>
<p>

View File

@ -1,7 +1,7 @@
## zh-CN
使用 `type="vertical"` 设置为行内的垂直分割线。
使用 `orientation="vertical"` 或者 `vertical` 设置为行内的垂直分割线。
## en-US
Use `type="vertical"` to make the divider vertical.
Use `orientation="vertical"` or `vertical` to make the divider vertical.

View File

@ -4,9 +4,9 @@ import { Divider } from 'antd';
const App: React.FC = () => (
<>
Text
<Divider type="vertical" />
<Divider orientation="vertical" />
<a href="#">Link</a>
<Divider type="vertical" />
<Divider vertical />
<a href="#">Link</a>
</>
);

View File

@ -1,7 +1,7 @@
## zh-CN
分割线中带有文字,可以用 `orientation` 指定文字位置。
分割线中带有文字,可以用 `titlePlacement` 指定文字位置。
## en-US
Divider with inner title, set `orientation="left/right"` to align it.
Divider with inner title, set `titlePlacement="start/end"` to align it.

View File

@ -12,25 +12,25 @@ const App: React.FC = () => (
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista
probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>
<Divider orientation="left">Left Text</Divider>
<Divider titlePlacement="start">Left Text</Divider>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista
probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>
<Divider orientation="right">Right Text</Divider>
<Divider titlePlacement="end">Right Text</Divider>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista
probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>
<Divider orientation="left" orientationMargin="0">
Left Text with 0 orientationMargin
<Divider titlePlacement="start" styles={{ content: { margin: 0 } }}>
Left Text margin with 0
</Divider>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista
probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>
<Divider orientation="right" orientationMargin={50}>
Right Text with 50px orientationMargin
<Divider titlePlacement="end" styles={{ content: { margin: '0 50px' } }}>
Right Text margin with 50px
</Divider>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista

View File

@ -37,13 +37,19 @@ Common props ref[Common props](/docs/react/common-props)
| children | The wrapped title | ReactNode | - | |
| className | The className of container | string | - | |
| dashed | Whether line is dashed | boolean | false | |
| variant | Whether line is dashed, dotted or solid | `dashed` \| `dotted` \| `solid` | solid | 5.20.0 |
| orientation | The position of title inside divider | `start` \| `end` \| `center` | `center` | `start` `end`: 5.24.0 |
| orientationMargin | The margin-left/right between the title and its closest border, while the `orientation` should not be `center`, If a numeric value of type `string` is provided without a unit, it is assumed to be in pixels (px) by default. | string \| number | - | |
| orientation | Whether line is horizontal or vertical | `horizontal` \| `vertical` | `horizontal` | - |
| ~~orientationMargin~~ | The margin-left/right between the title and its closest border, while the `titlePlacement` should not be `center`, If a numeric value of type `string` is provided without a unit, it is assumed to be in pixels (px) by default. | string \| number | - | |
| plain | Divider text show as plain style | boolean | true | 4.2.0 |
| style | The style object of container | CSSProperties | - | |
| size | The size of divider. Only valid for horizontal layout | `small` \| `middle` \| `large` | - | 5.25.0 |
| type | The direction type of divider | `horizontal` \| `vertical` | `horizontal` | |
| titlePlacement | The position of title inside divider | `start` \| `end` \| `center` | `center` | - |
| ~~type~~ | The direction type of divider | `horizontal` \| `vertical` | `horizontal` | - |
| variant | Whether line is dashed, dotted or solid | `dashed` \| `dotted` \| `solid` | solid | 5.20.0 |
| vertical | Orientation, Simultaneously configure with `orientation` and prioritize `orientation` | boolean | false | - |
## Semantic DOM
<code src="./demo/_semantic.tsx" simplify="true"></code>
## Design Token

View File

@ -1,24 +1,32 @@
import * as React from 'react';
import classNames from 'classnames';
import cls from 'classnames';
import useMergeSemantic from '../_util/hooks/useMergeSemantic';
import useOrientation from '../_util/hooks/useOrientation';
import type { Orientation } from '../_util/hooks/useOrientation';
import { devUseWarning } from '../_util/warning';
import { useComponentConfig } from '../config-provider/context';
import useSize from '../config-provider/hooks/useSize';
import { SizeType } from '../config-provider/SizeContext';
import useStyle from './style';
type SemanticName = 'root' | 'rail' | 'content';
export type TitlePlacement =
| 'left'
| 'right'
| 'center'
| 'start' // 👈 5.24.0+
| 'end'; // 👈 5.24.0+
const titlePlacementList = ['left', 'right', 'center', 'start', 'end'];
export interface DividerProps {
prefixCls?: string;
type?: 'horizontal' | 'vertical';
/**
* @default center
*/
orientation?:
| 'left'
| 'right'
| 'center'
| 'start' // 👈 5.24.0+
| 'end'; // 👈 5.24.0+
/** @deprecated please use `orientation`*/
type?: Orientation;
orientation?: Orientation;
vertical?: boolean;
titlePlacement?: TitlePlacement;
/** @deprecated please use `styles.content.margin` */
orientationMargin?: string | number;
className?: string;
rootClassName?: string;
@ -32,22 +40,27 @@ export interface DividerProps {
style?: React.CSSProperties;
size?: SizeType;
plain?: boolean;
classNames?: Partial<Record<SemanticName, string>>;
styles?: Partial<Record<SemanticName, React.CSSProperties>>;
}
const sizeClassNameMap: Record<string, string> = { small: 'sm', middle: 'md' };
const Divider: React.FC<DividerProps> = (props) => {
const {
getPrefixCls,
direction,
className: dividerClassName,
style: dividerStyle,
classNames: contextClassNames,
styles: contextStyles,
} = useComponentConfig('divider');
const {
prefixCls: customizePrefixCls,
type = 'horizontal',
orientation = 'center',
type,
orientation,
vertical,
titlePlacement,
orientationMargin,
className,
rootClassName,
@ -57,9 +70,17 @@ const Divider: React.FC<DividerProps> = (props) => {
plain,
style,
size: customSize,
classNames,
styles,
...restProps
} = props;
const prefixCls = getPrefixCls('divider', customizePrefixCls);
const railCls = `${prefixCls}-rail`;
const [mergedClassNames, mergedStyles] = useMergeSemantic(
[contextClassNames, classNames],
[contextStyles, styles],
);
const [hashId, cssVarCls] = useStyle(prefixCls);
@ -68,29 +89,34 @@ const Divider: React.FC<DividerProps> = (props) => {
const hasChildren = !!children;
const mergedOrientation = React.useMemo<'start' | 'end' | 'center'>(() => {
if (orientation === 'left') {
const validTitlePlacement = titlePlacementList.includes(orientation || '');
const mergedTitlePlacement = React.useMemo<'start' | 'end' | 'center'>(() => {
const placement =
titlePlacement ?? (validTitlePlacement ? (orientation as TitlePlacement) : 'center');
if (placement === 'left') {
return direction === 'rtl' ? 'end' : 'start';
}
if (orientation === 'right') {
if (placement === 'right') {
return direction === 'rtl' ? 'start' : 'end';
}
return orientation;
return placement;
}, [direction, orientation]);
const hasMarginStart = mergedOrientation === 'start' && orientationMargin != null;
const hasMarginStart = mergedTitlePlacement === 'start' && orientationMargin != null;
const hasMarginEnd = mergedOrientation === 'end' && orientationMargin != null;
const hasMarginEnd = mergedTitlePlacement === 'end' && orientationMargin != null;
const classString = classNames(
const [mergedOrientation, mergedVertical] = useOrientation(orientation, vertical, type);
const classString = cls(
prefixCls,
dividerClassName,
hashId,
cssVarCls,
`${prefixCls}-${type}`,
`${prefixCls}-${mergedOrientation}`,
{
[`${prefixCls}-with-text`]: hasChildren,
[`${prefixCls}-with-text-${mergedOrientation}`]: hasChildren,
[`${prefixCls}-with-text-${mergedTitlePlacement}`]: hasChildren,
[`${prefixCls}-dashed`]: !!dashed,
[`${prefixCls}-${variant}`]: variant !== 'solid',
[`${prefixCls}-plain`]: !!plain,
@ -98,12 +124,15 @@ const Divider: React.FC<DividerProps> = (props) => {
[`${prefixCls}-no-default-orientation-margin-start`]: hasMarginStart,
[`${prefixCls}-no-default-orientation-margin-end`]: hasMarginEnd,
[`${prefixCls}-${sizeCls}`]: !!sizeCls,
[railCls]: !children,
[mergedClassNames.rail as string]: mergedClassNames.rail && !children,
},
className,
rootClassName,
mergedClassNames.root,
);
const memoizedOrientationMargin = React.useMemo<string | number>(() => {
const memoizedPlacementMargin = React.useMemo<string | number>(() => {
if (typeof orientationMargin === 'number') {
return orientationMargin;
}
@ -114,32 +143,57 @@ const Divider: React.FC<DividerProps> = (props) => {
}, [orientationMargin]);
const innerStyle: React.CSSProperties = {
marginInlineStart: hasMarginStart ? memoizedOrientationMargin : undefined,
marginInlineEnd: hasMarginEnd ? memoizedOrientationMargin : undefined,
marginInlineStart: hasMarginStart ? memoizedPlacementMargin : undefined,
marginInlineEnd: hasMarginEnd ? memoizedPlacementMargin : undefined,
};
// Warning children not work in vertical mode
// =================== Warning =====================
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning('Divider');
warning(!children || !mergedVertical, 'usage', '`children` not working in `vertical` mode.');
warning(
!children || type !== 'vertical',
!validTitlePlacement,
'usage',
'`children` not working in `vertical` mode.',
'`orientation` is used for direction, please use `titlePlacement` replace this',
);
[
['type', 'orientation'],
['orientationMargin', 'styles.content.margin'],
].forEach(([deprecatedName, newName]) => {
warning.deprecated(!(deprecatedName in props), deprecatedName, newName);
});
}
return (
<div
className={classString}
style={{ ...dividerStyle, ...style }}
style={{
...dividerStyle,
...mergedStyles.root,
...(children ? {} : mergedStyles.rail),
...style,
}}
{...restProps}
role="separator"
>
{children && type !== 'vertical' && (
<span className={`${prefixCls}-inner-text`} style={innerStyle}>
{children}
</span>
{children && !mergedVertical && (
<>
<div
className={cls(railCls, `${railCls}-start`, mergedClassNames.rail)}
style={mergedStyles.rail}
/>
<span
className={cls(`${prefixCls}-inner-text`, mergedClassNames.content)}
style={{ ...innerStyle, ...mergedStyles.content }}
>
{children}
</span>
<div
className={cls(railCls, `${railCls}-end`, mergedClassNames.rail)}
style={mergedStyles.rail}
/>
</>
)}
</div>
);

View File

@ -38,13 +38,19 @@ group:
| children | 嵌套的标题 | ReactNode | - | |
| className | 分割线样式类 | string | - | |
| dashed | 是否虚线 | boolean | false | |
| variant | 分割线是虚线、点线还是实线 | `dashed` \| `dotted` \| `solid` | solid | 5.20.0 |
| orientation | 分割线标题的位置 | `start` \| `end` \| `center` | `center` | `start` `end`: 5.24.0 |
| orientationMargin | 标题和最近 left/right 边框之间的距离,去除了分割线,同时 `orientation` 不能为 `center`。如果传入 `string` 类型的数字且不带单位,默认单位是 px | string \| number | - | |
| orientation | 水平或垂直类型 | `horizontal` \| `vertical` | `horizontal` | - |
| ~~orientationMargin~~ | 标题和最近 left/right 边框之间的距离,去除了分割线,同时 `titlePlacement` 不能为 `center`。如果传入 `string` 类型的数字且不带单位,默认单位是 px | string \| number | - | |
| plain | 文字是否显示为普通正文样式 | boolean | false | 4.2.0 |
| style | 分割线样式对象 | CSSProperties | - | |
| size | 间距大小,仅对水平布局有效 | `small` \| `middle` \| `large` | - | 5.25.0 |
| type | 水平还是垂直类型 | `horizontal` \| `vertical` | `horizontal` | |
| titlePlacement | 分割线标题的位置 | `start` \| `end` \| `center` | `center` | - |
| ~~type~~ | 水平还是垂直类型 | `horizontal` \| `vertical` | `horizontal` | - |
| variant | 分割线是虚线、点线还是实线 | `dashed` \| `dotted` \| `solid` | solid | 5.20.0 |
| vertical | 是否垂直,和 orientation 同时配置以 orientation 优先 | boolean | false | - |
## Semantic DOM
<code src="./demo/_semantic.tsx" simplify="true"></code>
## 主题变量Design Token

View File

@ -17,7 +17,7 @@ export interface ComponentToken {
* @desc 0 1
* @descEN Distance between text and edge, which should be a number between 0 and 1.
*/
orientationMargin: number;
orientationMargin?: number;
/**
* @desc 线
* @descEN Horizontal margin of vertical Divider
@ -73,12 +73,17 @@ const genSharedDividerStyle: GenerateStyle<DividerToken> = (token): CSSObject =>
orientationMargin,
verticalMarginInline,
} = token;
const railCls = `${componentCls}-rail`;
return {
[componentCls]: {
...resetComponent(token),
borderBlockStart: `${unit(lineWidth)} solid ${colorSplit}`,
[railCls]: {
borderBlockStart: `${unit(lineWidth)} solid ${colorSplit}`,
},
// vertical
'&-vertical': {
position: 'relative',
@ -110,33 +115,29 @@ const genSharedDividerStyle: GenerateStyle<DividerToken> = (token): CSSObject =>
whiteSpace: 'nowrap',
textAlign: 'center',
borderBlockStart: `0 ${colorSplit}`,
'&::before, &::after': {
position: 'relative',
[`${railCls}-start, ${railCls}-end`]: {
width: '50%',
borderBlockStart: `${unit(lineWidth)} solid transparent`,
// Chrome not accept `inherit` in `border-top`
borderBlockStartColor: 'inherit',
borderBlockEnd: 0,
transform: 'translateY(50%)',
content: "''",
},
},
[`&-horizontal${componentCls}-with-text-start`]: {
'&::before': {
[`${railCls}-start`]: {
width: `calc(${orientationMargin} * 100%)`,
},
'&::after': {
[`${railCls}-end`]: {
width: `calc(100% - ${orientationMargin} * 100%)`,
},
},
[`&-horizontal${componentCls}-with-text-end`]: {
'&::before': {
[`${railCls}-start`]: {
width: `calc(100% - ${orientationMargin} * 100%)`,
},
'&::after': {
[`${railCls}-end`]: {
width: `calc(${orientationMargin} * 100%)`,
},
},
@ -152,10 +153,13 @@ const genSharedDividerStyle: GenerateStyle<DividerToken> = (token): CSSObject =>
borderColor: colorSplit,
borderStyle: 'dashed',
borderWidth: `${unit(lineWidth)} 0 0`,
[railCls]: {
borderBlockStart: `${unit(lineWidth)} dashed ${colorSplit}`,
},
},
[`&-horizontal${componentCls}-with-text${componentCls}-dashed`]: {
'&::before, &::after': {
[`${railCls}-start, ${railCls}-end`]: {
borderStyle: 'dashed none none',
},
},
@ -172,6 +176,9 @@ const genSharedDividerStyle: GenerateStyle<DividerToken> = (token): CSSObject =>
borderColor: colorSplit,
borderStyle: 'dotted',
borderWidth: `${unit(lineWidth)} 0 0`,
[railCls]: {
borderBlockStart: `${unit(lineWidth)} dotted ${colorSplit}`,
},
},
[`&-horizontal${componentCls}-with-text${componentCls}-dotted`]: {
@ -195,11 +202,11 @@ const genSharedDividerStyle: GenerateStyle<DividerToken> = (token): CSSObject =>
[`&-horizontal${componentCls}-with-text-start${componentCls}-no-default-orientation-margin-start`]:
{
'&::before': {
[`${railCls}-start`]: {
width: 0,
},
'&::after': {
[`${railCls}-end`]: {
width: '100%',
},
@ -210,11 +217,11 @@ const genSharedDividerStyle: GenerateStyle<DividerToken> = (token): CSSObject =>
[`&-horizontal${componentCls}-with-text-end${componentCls}-no-default-orientation-margin-end`]:
{
'&::before': {
[`${railCls}-start`]: {
width: '100%',
},
'&::after': {
[`${railCls}-end`]: {
width: 0,
},

View File

@ -4218,7 +4218,7 @@ Array [
</div>
</div>
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>
<p
@ -4311,7 +4311,7 @@ Array [
</div>
</div>
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
/>
<p

View File

@ -1,7 +1,7 @@
import React from 'react';
import { Button, Drawer, Typography } from 'antd';
import SemanticPreview from '../../../.dumi/components/SemanticPreview';
import SemanticPreview from '../../../.dumi/theme/common/SemanticPreview';
import useLocale from '../../../.dumi/hooks/useLocale';
const locales = {

View File

@ -2271,7 +2271,7 @@ Array [
style="display: none;"
/>
<div
class="ant-divider css-var-test-id ant-divider-horizontal"
class="ant-divider css-var-test-id ant-divider-horizontal ant-divider-rail"
role="separator"
style="margin: 0px;"
/>

View File

@ -2,7 +2,7 @@ import React from 'react';
import { DeleteOutlined, DownOutlined, EditOutlined, SaveOutlined } from '@ant-design/icons';
import { Dropdown, MenuProps, Space } from 'antd';
import SemanticPreview from '../../../.dumi/components/SemanticPreview';
import SemanticPreview from '../../../.dumi/theme/common/SemanticPreview';
import useLocale from '../../../.dumi/hooks/useLocale';
const locales = {

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