Merge remote-tracking branch 'origin/feature' into fix/datePicker

This commit is contained in:
jrr997 2023-06-04 14:35:43 +08:00
commit 852394c198
265 changed files with 29927 additions and 6137 deletions

View File

@ -1,10 +1,10 @@
import type { ReactNode } from 'react';
import React, { useMemo } from 'react';
import type { MenuProps } from 'antd'; import type { MenuProps } from 'antd';
import { useFullSidebarData, useSidebarData } from 'dumi';
import { Tag, theme } from 'antd'; import { Tag, theme } from 'antd';
import useLocation from './useLocation'; import { useFullSidebarData, useSidebarData } from 'dumi';
import type { ReactNode } from 'react';
import { useMemo } from 'react';
import Link from '../theme/common/Link'; import Link from '../theme/common/Link';
import useLocation from './useLocation';
export type UseMenuOptions = { export type UseMenuOptions = {
before?: ReactNode; before?: ReactNode;

View File

@ -1,18 +1,24 @@
import type { FC } from 'react'; import { ConfigProvider, theme as antdTheme } from 'antd';
import React, { useContext } from 'react';
import { ConfigContext } from 'antd/es/config-provider';
import type { ThemeProviderProps } from 'antd-style'; import type { ThemeProviderProps } from 'antd-style';
import { ThemeProvider } from 'antd-style'; import { ThemeProvider } from 'antd-style';
import { theme } from 'antd'; import type { FC } from 'react';
import React, { useContext } from 'react';
const SiteThemeProvider: FC<ThemeProviderProps> = ({ children, ...rest }) => { const SiteThemeProvider: FC<ThemeProviderProps> = ({ children, theme, ...rest }) => {
const { getPrefixCls, iconPrefixCls } = useContext(ConfigContext); const { getPrefixCls, iconPrefixCls } = useContext(ConfigProvider.ConfigContext);
const rootPrefixCls = getPrefixCls(); const rootPrefixCls = getPrefixCls();
const { token } = theme.useToken(); const { token } = antdTheme.useToken();
React.useEffect(() => {
ConfigProvider.config({
theme,
});
}, [theme]);
return ( return (
<ThemeProvider <ThemeProvider
{...rest} {...rest}
theme={theme}
customToken={{ customToken={{
headerHeight: 64, headerHeight: 64,
menuItemBorder: 2, menuItemBorder: 2,

View File

@ -1,8 +1,7 @@
/* eslint-disable class-methods-use-this */ /* eslint-disable class-methods-use-this */
import KeyCode from 'rc-util/lib/KeyCode'; import KeyCode from 'rc-util/lib/KeyCode';
import React from 'react'; import React from 'react';
import { waitFakeTimer, render, fireEvent } from '../../../tests/utils'; import { fireEvent, render, waitFakeTimer } from '../../../tests/utils';
import getDataOrAriaProps from '../getDataOrAriaProps';
import { isStyleSupport } from '../styleChecker'; import { isStyleSupport } from '../styleChecker';
import throttleByAnimationFrame from '../throttleByAnimationFrame'; import throttleByAnimationFrame from '../throttleByAnimationFrame';
import TransButton from '../transButton'; import TransButton from '../transButton';
@ -46,57 +45,6 @@ describe('Test utils function', () => {
}); });
}); });
describe('getDataOrAriaProps', () => {
it('returns all data-* properties from an object', () => {
const props = {
onClick: () => {},
isOpen: true,
'data-test': 'test-id',
'data-id': 1234,
};
const results = getDataOrAriaProps(props);
expect(results).toEqual({
'data-test': 'test-id',
'data-id': 1234,
});
});
it('does not return data-__ properties from an object', () => {
const props = {
onClick: () => {},
isOpen: true,
'data-__test': 'test-id',
'data-__id': 1234,
};
const results = getDataOrAriaProps(props);
expect(results).toEqual({});
});
it('returns all aria-* properties from an object', () => {
const props = {
onClick: () => {},
isOpen: true,
'aria-labelledby': 'label-id',
'aria-label': 'some-label',
};
const results = getDataOrAriaProps(props);
expect(results).toEqual({
'aria-labelledby': 'label-id',
'aria-label': 'some-label',
});
});
it('returns role property from an object', () => {
const props = {
onClick: () => {},
isOpen: true,
role: 'search',
};
const results = getDataOrAriaProps(props);
expect(results).toEqual({ role: 'search' });
});
});
describe('TransButton', () => { describe('TransButton', () => {
it('can be focus/blur', () => { it('can be focus/blur', () => {
const ref = React.createRef<HTMLDivElement>(); const ref = React.createRef<HTMLDivElement>();

View File

@ -1,11 +0,0 @@
export default function getDataOrAriaProps(props: any) {
return Object.keys(props).reduce((prev: any, key: string) => {
if (
(key.startsWith('data-') || key.startsWith('aria-') || key === 'role') &&
!key.startsWith('data-__')
) {
prev[key] = props[key];
}
return prev;
}, {});
}

View File

@ -5,11 +5,11 @@ import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
import InfoCircleFilled from '@ant-design/icons/InfoCircleFilled'; import InfoCircleFilled from '@ant-design/icons/InfoCircleFilled';
import classNames from 'classnames'; import classNames from 'classnames';
import CSSMotion from 'rc-motion'; import CSSMotion from 'rc-motion';
import pickAttrs from 'rc-util/lib/pickAttrs';
import type { ReactElement } from 'react'; import type { ReactElement } from 'react';
import * as React from 'react'; import * as React from 'react';
import { ConfigContext } from '../config-provider';
import getDataOrAriaProps from '../_util/getDataOrAriaProps';
import { replaceElement } from '../_util/reactNode'; import { replaceElement } from '../_util/reactNode';
import { ConfigContext } from '../config-provider';
import ErrorBoundary from './ErrorBoundary'; import ErrorBoundary from './ErrorBoundary';
// CSSINJS // CSSINJS
@ -117,7 +117,7 @@ const Alert: CompoundedComponent = ({
}) => { }) => {
const [closed, setClosed] = React.useState(false); const [closed, setClosed] = React.useState(false);
const ref = React.useRef<HTMLElement>(); const ref = React.useRef<HTMLDivElement>(null);
const { getPrefixCls, direction } = React.useContext(ConfigContext); const { getPrefixCls, direction } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('alert', customizePrefixCls); const prefixCls = getPrefixCls('alert', customizePrefixCls);
const [wrapSSR, hashId] = useStyle(prefixCls); const [wrapSSR, hashId] = useStyle(prefixCls);
@ -157,7 +157,10 @@ const Alert: CompoundedComponent = ({
hashId, hashId,
); );
const dataOrAriaProps = getDataOrAriaProps(props); const dataOrAriaProps = pickAttrs(props, {
aria: true,
data: true,
});
return wrapSSR( return wrapSSR(
<CSSMotion <CSSMotion

View File

@ -79,6 +79,85 @@ exports[`renders components/anchor/demo/basic.tsx extend context correctly 1`] =
</div> </div>
`; `;
exports[`renders components/anchor/demo/component-token.tsx extend context correctly 1`] = `
<div
class="ant-row"
>
<div
class="ant-col ant-col-16"
>
<div
id="part-1"
style="height: 100vh; background: rgba(255, 0, 0, 0.02);"
/>
<div
id="part-2"
style="height: 100vh; background: rgba(0, 255, 0, 0.02);"
/>
<div
id="part-3"
style="height: 100vh; background: rgba(0, 0, 255, 0.02);"
/>
</div>
<div
class="ant-col ant-col-8"
>
<div>
<div
class=""
>
<div
class="ant-anchor-wrapper"
style="max-height: 100vh;"
>
<div
class="ant-anchor"
>
<span
class="ant-anchor-ink ant-anchor-ink-visible"
style="top: 0px; height: 0px;"
/>
<div
class="ant-anchor-link ant-anchor-link-active"
>
<a
class="ant-anchor-link-title ant-anchor-link-title-active"
href="#part-1"
title="Part 1"
>
Part 1
</a>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#part-2"
title="Part 2"
>
Part 2
</a>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#part-3"
title="Part 3"
>
Part 3
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`renders components/anchor/demo/customizeHighlight.tsx extend context correctly 1`] = ` exports[`renders components/anchor/demo/customizeHighlight.tsx extend context correctly 1`] = `
<div <div
class="ant-anchor-wrapper" class="ant-anchor-wrapper"

View File

@ -78,6 +78,84 @@ exports[`renders components/anchor/demo/basic.tsx correctly 1`] = `
</div> </div>
`; `;
exports[`renders components/anchor/demo/component-token.tsx correctly 1`] = `
<div
class="ant-row"
>
<div
class="ant-col ant-col-16"
>
<div
id="part-1"
style="height:100vh;background:rgba(255,0,0,0.02)"
/>
<div
id="part-2"
style="height:100vh;background:rgba(0,255,0,0.02)"
/>
<div
id="part-3"
style="height:100vh;background:rgba(0,0,255,0.02)"
/>
</div>
<div
class="ant-col ant-col-8"
>
<div>
<div
class=""
>
<div
class="ant-anchor-wrapper"
style="max-height:100vh"
>
<div
class="ant-anchor"
>
<span
class="ant-anchor-ink"
/>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#part-1"
title="Part 1"
>
Part 1
</a>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#part-2"
title="Part 2"
>
Part 2
</a>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#part-3"
title="Part 3"
>
Part 3
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`renders components/anchor/demo/customizeHighlight.tsx correctly 1`] = ` exports[`renders components/anchor/demo/customizeHighlight.tsx correctly 1`] = `
<div <div
class="ant-anchor-wrapper" class="ant-anchor-wrapper"

View File

@ -0,0 +1,7 @@
## zh-CN
Component Token Debug.
## en-US
Component Token Debug

View File

@ -0,0 +1,46 @@
import { Anchor, Col, ConfigProvider, Row } from 'antd';
import React from 'react';
/** Test usage. Do not use in your production. */
export default () => (
<ConfigProvider
theme={{
components: {
Anchor: {
linkPaddingBlock: 100,
linkPaddingInlineStart: 50,
},
},
}}
>
<Row>
<Col span={16}>
<div id="part-1" style={{ height: '100vh', background: 'rgba(255,0,0,0.02)' }} />
<div id="part-2" style={{ height: '100vh', background: 'rgba(0,255,0,0.02)' }} />
<div id="part-3" style={{ height: '100vh', background: 'rgba(0,0,255,0.02)' }} />
</Col>
<Col span={8}>
<Anchor
items={[
{
key: 'part-1',
href: '#part-1',
title: 'Part 1',
},
{
key: 'part-2',
href: '#part-2',
title: 'Part 2',
},
{
key: 'part-3',
href: '#part-3',
title: 'Part 3',
},
]}
/>
</Col>
</Row>
</ConfigProvider>
);

View File

@ -30,6 +30,7 @@ For displaying anchor hyperlinks on page and jumping between them.
<code src="./demo/targetOffset.tsx" iframe="200">Set Anchor scroll offset</code> <code src="./demo/targetOffset.tsx" iframe="200">Set Anchor scroll offset</code>
<code src="./demo/onChange.tsx">Listening for anchor link change</code> <code src="./demo/onChange.tsx">Listening for anchor link change</code>
<code src="./demo/legacy-anchor.tsx" debug>Deprecated JSX demo</code> <code src="./demo/legacy-anchor.tsx" debug>Deprecated JSX demo</code>
<code src="./demo/component-token.tsx" debug>Component Token</code>
## API ## API

View File

@ -31,6 +31,7 @@ group:
<code src="./demo/targetOffset.tsx" iframe="200">设置锚点滚动偏移量</code> <code src="./demo/targetOffset.tsx" iframe="200">设置锚点滚动偏移量</code>
<code src="./demo/onChange.tsx">监听锚点链接改变</code> <code src="./demo/onChange.tsx">监听锚点链接改变</code>
<code src="./demo/legacy-anchor.tsx" debug>废弃的 JSX 示例</code> <code src="./demo/legacy-anchor.tsx" debug>废弃的 JSX 示例</code>
<code src="./demo/component-token.tsx" debug>组件 Token</code>
## API ## API

View File

@ -1,15 +1,16 @@
import type { CSSObject } from '@ant-design/cssinjs'; import type { CSSObject } from '@ant-design/cssinjs';
import { resetComponent, textEllipsis } from '../../style';
import type { FullToken, GenerateStyle } from '../../theme/internal'; import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal'; import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import { resetComponent, textEllipsis } from '../../style';
export interface ComponentToken {} export interface ComponentToken {
linkPaddingBlock: number;
linkPaddingInlineStart: number;
}
interface AnchorToken extends FullToken<'Anchor'> { interface AnchorToken extends FullToken<'Anchor'> {
holderOffsetBlock: number; holderOffsetBlock: number;
anchorPaddingBlock: number;
anchorPaddingBlockSecondary: number; anchorPaddingBlockSecondary: number;
anchorPaddingInline: number;
anchorBallSize: number; anchorBallSize: number;
anchorTitleBlock: number; anchorTitleBlock: number;
} }
@ -34,16 +35,14 @@ const genSharedAnchorStyle: GenerateStyle<AnchorToken> = (token): CSSObject => {
// delete overflow: auto // delete overflow: auto
// overflow: 'auto', // overflow: 'auto',
backgroundColor: 'transparent',
[componentCls]: { [componentCls]: {
...resetComponent(token), ...resetComponent(token),
position: 'relative', position: 'relative',
paddingInlineStart: lineWidthBold, paddingInlineStart: lineWidthBold,
[`${componentCls}-link`]: { [`${componentCls}-link`]: {
paddingBlock: token.anchorPaddingBlock, paddingBlock: token.linkPaddingBlock,
paddingInline: `${token.anchorPaddingInline}px 0`, paddingInline: `${token.linkPaddingInlineStart}px 0`,
'&-title': { '&-title': {
...textEllipsis, ...textEllipsis,
@ -152,16 +151,21 @@ const genSharedAnchorHorizontalStyle: GenerateStyle<AnchorToken> = (token): CSSO
}; };
// ============================== Export ============================== // ============================== Export ==============================
export default genComponentStyleHook('Anchor', (token) => { export default genComponentStyleHook(
const { fontSize, fontSizeLG, padding, paddingXXS } = token; 'Anchor',
(token) => {
const { fontSize, fontSizeLG, paddingXXS } = token;
const anchorToken = mergeToken<AnchorToken>(token, { const anchorToken = mergeToken<AnchorToken>(token, {
holderOffsetBlock: paddingXXS, holderOffsetBlock: paddingXXS,
anchorPaddingBlock: paddingXXS, anchorPaddingBlockSecondary: paddingXXS / 2,
anchorPaddingBlockSecondary: paddingXXS / 2, anchorTitleBlock: (fontSize / 14) * 3,
anchorPaddingInline: padding, anchorBallSize: fontSizeLG / 2,
anchorTitleBlock: (fontSize / 14) * 3, });
anchorBallSize: fontSizeLG / 2, return [genSharedAnchorStyle(anchorToken), genSharedAnchorHorizontalStyle(anchorToken)];
}); },
return [genSharedAnchorStyle(anchorToken), genSharedAnchorHorizontalStyle(anchorToken)]; (token) => ({
}); linkPaddingBlock: token.paddingXXS,
linkPaddingInlineStart: token.padding,
}),
);

View File

@ -342,6 +342,243 @@ exports[`renders components/avatar/demo/basic.tsx extend context correctly 1`] =
</div> </div>
`; `;
exports[`renders components/avatar/demo/component-token.tsx extend context correctly 1`] = `
Array [
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
<div
class="ant-space-item"
>
<span
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="http://abc.com/not-exist.jpg"
/>
</span>
</div>
</div>,
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
<div
class="ant-space-item"
>
<div
class="ant-avatar-group"
>
<span
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://xsgames.co/randomusers/avatar.php?g=pixel&key=2"
/>
</span>
<span
class="ant-avatar ant-avatar-circle"
style="background-color: rgb(245, 106, 0);"
>
<span
class="ant-avatar-string"
style="transform: scale(1) translateX(-50%);"
>
K
</span>
</span>
<span
class="ant-avatar ant-avatar-circle"
style="color: rgb(245, 106, 0); background-color: rgb(253, 227, 207);"
>
<span
class="ant-avatar-string"
style="transform: scale(1) translateX(-50%);"
>
+2
</span>
</span>
<div
class="ant-popover ant-zoom-big-appear ant-zoom-big-appear-prepare ant-zoom-big ant-avatar-group-popover ant-popover-placement-top"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-popover-arrow"
style="position: absolute; bottom: 0px; left: 0px;"
/>
<div
class="ant-popover-content"
>
<div
class="ant-popover-inner"
role="tooltip"
>
<div
class="ant-popover-inner-content"
>
<span
class="ant-avatar ant-avatar-circle ant-avatar-icon"
style="background-color: rgb(135, 208, 104);"
>
<span
aria-label="user"
class="anticon anticon-user"
role="img"
>
<svg
aria-hidden="true"
data-icon="user"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M858.5 763.6a374 374 0 00-80.6-119.5 375.63 375.63 0 00-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 00-80.6 119.5A371.7 371.7 0 00136 901.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 008-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"
/>
</svg>
</span>
</span>
<div
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-tooltip-placement-top"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-tooltip-arrow"
style="position: absolute; bottom: 0px; left: 0px;"
/>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-inner"
role="tooltip"
>
Ant User
</div>
</div>
</div>
<span
class="ant-avatar ant-avatar-circle ant-avatar-icon"
style="background-color: rgb(24, 144, 255);"
>
<span
aria-label="ant-design"
class="anticon anticon-ant-design"
role="img"
>
<svg
aria-hidden="true"
data-icon="ant-design"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M716.3 313.8c19-18.9 19-49.7 0-68.6l-69.9-69.9.1.1c-18.5-18.5-50.3-50.3-95.3-95.2-21.2-20.7-55.5-20.5-76.5.5L80.9 474.2a53.84 53.84 0 000 76.4L474.6 944a54.14 54.14 0 0076.5 0l165.1-165c19-18.9 19-49.7 0-68.6a48.7 48.7 0 00-68.7 0l-125 125.2c-5.2 5.2-13.3 5.2-18.5 0L189.5 521.4c-5.2-5.2-5.2-13.3 0-18.5l314.4-314.2c.4-.4.9-.7 1.3-1.1 5.2-4.1 12.4-3.7 17.2 1.1l125.2 125.1c19 19 49.8 19 68.7 0zM408.6 514.4a106.3 106.2 0 10212.6 0 106.3 106.2 0 10-212.6 0zm536.2-38.6L821.9 353.5c-19-18.9-49.8-18.9-68.7.1a48.4 48.4 0 000 68.6l83 82.9c5.2 5.2 5.2 13.3 0 18.5l-81.8 81.7a48.4 48.4 0 000 68.6 48.7 48.7 0 0068.7 0l121.8-121.7a53.93 53.93 0 00-.1-76.4z"
/>
</svg>
</span>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>,
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
<div
class="ant-space-item"
style="margin-right: 8px;"
>
<span
class="ant-badge"
>
<span
class="ant-avatar ant-avatar-square ant-avatar-icon"
>
<span
aria-label="user"
class="anticon anticon-user"
role="img"
>
<svg
aria-hidden="true"
data-icon="user"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M858.5 763.6a374 374 0 00-80.6-119.5 375.63 375.63 0 00-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 00-80.6 119.5A371.7 371.7 0 00136 901.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 008-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"
/>
</svg>
</span>
</span>
<sup
class="ant-scroll-number ant-badge-count"
data-show="true"
title="1"
>
<span
class="ant-scroll-number-only"
style="transition: none;"
>
<span
class="ant-scroll-number-only-unit current"
>
1
</span>
</span>
</sup>
</span>
</div>
<div
class="ant-space-item"
>
<span
class="ant-badge"
>
<span
class="ant-avatar ant-avatar-square ant-avatar-icon"
>
<span
aria-label="user"
class="anticon anticon-user"
role="img"
>
<svg
aria-hidden="true"
data-icon="user"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M858.5 763.6a374 374 0 00-80.6-119.5 375.63 375.63 0 00-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 00-80.6 119.5A371.7 371.7 0 00136 901.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 008-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"
/>
</svg>
</span>
</span>
<sup
class="ant-scroll-number ant-badge-dot"
data-show="true"
/>
</span>
</div>
</div>,
]
`;
exports[`renders components/avatar/demo/dynamic.tsx extend context correctly 1`] = ` exports[`renders components/avatar/demo/dynamic.tsx extend context correctly 1`] = `
Array [ Array [
<span <span

View File

@ -342,6 +342,154 @@ exports[`renders components/avatar/demo/basic.tsx correctly 1`] = `
</div> </div>
`; `;
exports[`renders components/avatar/demo/component-token.tsx correctly 1`] = `
Array [
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
<div
class="ant-space-item"
>
<span
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="http://abc.com/not-exist.jpg"
/>
</span>
</div>
</div>,
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
<div
class="ant-space-item"
>
<div
class="ant-avatar-group"
>
<span
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://xsgames.co/randomusers/avatar.php?g=pixel&key=2"
/>
</span>
<span
class="ant-avatar ant-avatar-circle"
style="background-color:#f56a00"
>
<span
class="ant-avatar-string"
style="opacity:0"
>
K
</span>
</span>
<span
class="ant-avatar ant-avatar-circle"
style="color:#f56a00;background-color:#fde3cf"
>
<span
class="ant-avatar-string"
style="opacity:0"
>
+2
</span>
</span>
</div>
</div>
</div>,
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
<div
class="ant-space-item"
style="margin-right:8px"
>
<span
class="ant-badge"
>
<span
class="ant-avatar ant-avatar-square ant-avatar-icon"
>
<span
aria-label="user"
class="anticon anticon-user"
role="img"
>
<svg
aria-hidden="true"
data-icon="user"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M858.5 763.6a374 374 0 00-80.6-119.5 375.63 375.63 0 00-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 00-80.6 119.5A371.7 371.7 0 00136 901.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 008-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"
/>
</svg>
</span>
</span>
<sup
class="ant-scroll-number ant-badge-count"
data-show="true"
title="1"
>
<span
class="ant-scroll-number-only"
style="transition:none"
>
<span
class="ant-scroll-number-only-unit current"
>
1
</span>
</span>
</sup>
</span>
</div>
<div
class="ant-space-item"
>
<span
class="ant-badge"
>
<span
class="ant-avatar ant-avatar-square ant-avatar-icon"
>
<span
aria-label="user"
class="anticon anticon-user"
role="img"
>
<svg
aria-hidden="true"
data-icon="user"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M858.5 763.6a374 374 0 00-80.6-119.5 375.63 375.63 0 00-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 00-80.6 119.5A371.7 371.7 0 00136 901.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 008-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"
/>
</svg>
</span>
</span>
<sup
class="ant-scroll-number ant-badge-dot"
data-show="true"
/>
</span>
</div>
</div>,
]
`;
exports[`renders components/avatar/demo/dynamic.tsx correctly 1`] = ` exports[`renders components/avatar/demo/dynamic.tsx correctly 1`] = `
Array [ Array [
<span <span

View File

@ -0,0 +1,7 @@
## zh-CN
Component Token Debug.
## en-US
Component Token Debug

View File

@ -0,0 +1,51 @@
import { AntDesignOutlined, UserOutlined } from '@ant-design/icons';
import { Avatar, Badge, ConfigProvider, Space, Tooltip } from 'antd';
import React from 'react';
const App: React.FC = () => (
<ConfigProvider
theme={{
components: {
Avatar: {
containerSize: 60,
containerSizeLG: 30,
containerSizeSM: 16,
textFontSize: 18,
textFontSizeLG: 28,
textFontSizeSM: 12,
borderRadius: 10,
groupOverlapping: -10,
groupBorderColor: '#eee',
},
},
}}
>
<Space>
<Avatar shape="circle" src="http://abc.com/not-exist.jpg">
A
</Avatar>
</Space>
<Space>
<Avatar.Group maxCount={2} maxStyle={{ color: '#f56a00', backgroundColor: '#fde3cf' }}>
<Avatar src="https://xsgames.co/randomusers/avatar.php?g=pixel&key=2" />
<Avatar style={{ backgroundColor: '#f56a00' }}>K</Avatar>
<Tooltip title="Ant User" placement="top">
<Avatar style={{ backgroundColor: '#87d068' }} icon={<UserOutlined />} />
</Tooltip>
<Avatar style={{ backgroundColor: '#1890ff' }} icon={<AntDesignOutlined />} />
</Avatar.Group>
</Space>
<Space>
<Badge count={1}>
<Avatar shape="square" icon={<UserOutlined />} />
</Badge>
<Badge dot>
<Avatar shape="square" icon={<UserOutlined />} />
</Badge>
</Space>
</ConfigProvider>
);
export default App;

View File

@ -23,6 +23,7 @@ Avatars can be used to represent people or objects. It supports images, `Icon`s,
<code src="./demo/toggle-debug.tsx" debug>Calculate text style when hiding</code> <code src="./demo/toggle-debug.tsx" debug>Calculate text style when hiding</code>
<code src="./demo/responsive.tsx">Responsive Size</code> <code src="./demo/responsive.tsx">Responsive Size</code>
<code src="./demo/fallback.tsx" debug>Fallback</code> <code src="./demo/fallback.tsx" debug>Fallback</code>
<code src="./demo/component-token.tsx" debug>Component Token</code>
## API ## API

View File

@ -28,6 +28,7 @@ group:
<code src="./demo/toggle-debug.tsx" debug>隐藏情况下计算字符对齐</code> <code src="./demo/toggle-debug.tsx" debug>隐藏情况下计算字符对齐</code>
<code src="./demo/responsive.tsx">响应式尺寸</code> <code src="./demo/responsive.tsx">响应式尺寸</code>
<code src="./demo/fallback.tsx" debug>图片不存在时</code> <code src="./demo/fallback.tsx" debug>图片不存在时</code>
<code src="./demo/component-token.tsx" debug>组件 Token</code>
## API ## API

View File

@ -1,23 +1,24 @@
import type { CSSObject } from '@ant-design/cssinjs'; import type { CSSObject } from '@ant-design/cssinjs';
import { resetComponent } from '../../style';
import type { FullToken, GenerateStyle } from '../../theme/internal'; import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal'; import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import { resetComponent } from '../../style';
export interface ComponentToken {} export interface ComponentToken {
containerSize: number;
containerSizeLG: number;
containerSizeSM: number;
textFontSize: number;
textFontSizeLG: number;
textFontSizeSM: number;
groupSpace: number;
groupOverlapping: number;
groupBorderColor: string;
}
type AvatarToken = FullToken<'Avatar'> & { type AvatarToken = FullToken<'Avatar'> & {
avatarBgColor: string;
avatarBg: string; avatarBg: string;
avatarColor: string; avatarColor: string;
avatarSizeBase: number;
avatarSizeLG: number;
avatarSizeSM: number;
avatarFontSizeBase: number;
avatarFontSizeLG: number;
avatarFontSizeSM: number;
avatarGroupOverlapping: number;
avatarGroupSpace: number;
avatarGroupBorderColor: string;
avatarBgColor: string;
}; };
const genBaseStyle: GenerateStyle<AvatarToken> = (token) => { const genBaseStyle: GenerateStyle<AvatarToken> = (token) => {
@ -27,12 +28,12 @@ const genBaseStyle: GenerateStyle<AvatarToken> = (token) => {
iconCls, iconCls,
avatarBg, avatarBg,
avatarColor, avatarColor,
avatarSizeBase, containerSize,
avatarSizeLG, containerSizeLG,
avatarSizeSM, containerSizeSM,
avatarFontSizeBase, textFontSize,
avatarFontSizeLG, textFontSizeLG,
avatarFontSizeSM, textFontSizeSM,
borderRadius, borderRadius,
borderRadiusLG, borderRadiusLG,
borderRadiusSM, borderRadiusSM,
@ -89,14 +90,14 @@ const genBaseStyle: GenerateStyle<AvatarToken> = (token) => {
display: 'block', display: 'block',
}, },
...avatarSizeStyle(avatarSizeBase, avatarFontSizeBase, borderRadius), ...avatarSizeStyle(containerSize, textFontSize, borderRadius),
[`&-lg`]: { [`&-lg`]: {
...avatarSizeStyle(avatarSizeLG, avatarFontSizeLG, borderRadiusLG), ...avatarSizeStyle(containerSizeLG, textFontSizeLG, borderRadiusLG),
}, },
[`&-sm`]: { [`&-sm`]: {
...avatarSizeStyle(avatarSizeSM, avatarFontSizeSM, borderRadiusSM), ...avatarSizeStyle(containerSizeSM, textFontSizeSM, borderRadiusSM),
}, },
'> img': { '> img': {
@ -110,55 +111,65 @@ const genBaseStyle: GenerateStyle<AvatarToken> = (token) => {
}; };
const genGroupStyle: GenerateStyle<AvatarToken> = (token) => { const genGroupStyle: GenerateStyle<AvatarToken> = (token) => {
const { componentCls, avatarGroupBorderColor, avatarGroupSpace } = token; const { componentCls, groupBorderColor, groupOverlapping, groupSpace } = token;
return { return {
[`${componentCls}-group`]: { [`${componentCls}-group`]: {
display: 'inline-flex', display: 'inline-flex',
[`${componentCls}`]: { [`${componentCls}`]: {
borderColor: avatarGroupBorderColor, borderColor: groupBorderColor,
}, },
[`> *:not(:first-child)`]: { [`> *:not(:first-child)`]: {
marginInlineStart: avatarGroupSpace, marginInlineStart: groupOverlapping,
},
},
[`${componentCls}-group-popover`]: {
[`${componentCls} + ${componentCls}`]: {
marginInlineStart: groupSpace,
}, },
}, },
}; };
}; };
export default genComponentStyleHook('Avatar', (token) => { export default genComponentStyleHook(
const { 'Avatar',
colorTextLightSolid, (token) => {
const { colorTextLightSolid, colorTextPlaceholder } = token;
const avatarToken = mergeToken<AvatarToken>(token, {
avatarBg: colorTextPlaceholder,
avatarColor: colorTextLightSolid,
});
return [genBaseStyle(avatarToken), genGroupStyle(avatarToken)];
},
(token) => {
const {
controlHeight,
controlHeightLG,
controlHeightSM,
controlHeight, fontSize,
controlHeightLG, fontSizeLG,
controlHeightSM, fontSizeXL,
fontSizeHeading3,
fontSize, marginXS,
fontSizeLG, marginXXS,
fontSizeXL, colorBorderBg,
fontSizeHeading3, } = token;
return {
containerSize: controlHeight,
containerSizeLG: controlHeightLG,
containerSizeSM: controlHeightSM,
marginXS, textFontSize: Math.round((fontSizeLG + fontSizeXL) / 2),
colorBorderBg, textFontSizeLG: fontSizeHeading3,
colorTextPlaceholder, textFontSizeSM: fontSize,
} = token;
const avatarToken = mergeToken<AvatarToken>(token, { groupSpace: marginXXS,
avatarBg: colorTextPlaceholder, groupOverlapping: -marginXS,
avatarColor: colorTextLightSolid, groupBorderColor: colorBorderBg,
};
avatarSizeBase: controlHeight, },
avatarSizeLG: controlHeightLG, );
avatarSizeSM: controlHeightSM,
avatarFontSizeBase: Math.round((fontSizeLG + fontSizeXL) / 2),
avatarFontSizeLG: fontSizeHeading3,
avatarFontSizeSM: fontSize,
avatarGroupSpace: -marginXS,
avatarGroupBorderColor: colorBorderBg,
});
return [genBaseStyle(avatarToken), genGroupStyle(avatarToken)];
});

View File

@ -63,6 +63,333 @@ exports[`renders components/breadcrumb/demo/basic.tsx extend context correctly 1
</nav> </nav>
`; `;
exports[`renders components/breadcrumb/demo/component-token.tsx extend context correctly 1`] = `
<nav
class="ant-breadcrumb"
>
<ol>
<li>
<span
class="ant-breadcrumb-link"
>
Home
</span>
</li>
<li
aria-hidden="true"
class="ant-breadcrumb-separator"
>
&gt;
</li>
<li>
<span
class="ant-breadcrumb-link"
>
<a
href=""
>
Application Center
</a>
</span>
</li>
<li
aria-hidden="true"
class="ant-breadcrumb-separator"
>
&gt;
</li>
<li>
<span
class="ant-dropdown-trigger ant-breadcrumb-overlay-link"
>
<span
class="ant-breadcrumb-link"
>
<a
href=""
>
General
</a>
</span>
<span
aria-label="down"
class="anticon anticon-down"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
<div
class="ant-dropdown ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up ant-dropdown-placement-bottom"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<ul
class="ant-dropdown-menu ant-dropdown-menu-root ant-dropdown-menu-vertical ant-dropdown-menu-light"
data-menu-list="true"
role="menu"
tabindex="0"
>
<li
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
data-menu-id="rc-menu-uuid-test-1"
role="menuitem"
tabindex="-1"
>
<span
class="ant-dropdown-menu-title-content"
>
<a
href="http://www.alipay.com/"
rel="noopener noreferrer"
target="_blank"
>
General
</a>
</span>
</li>
<div
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-dropdown-menu-inline-collapsed-tooltip ant-tooltip-placement-right"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-tooltip-arrow"
style="position: absolute; top: 0px; left: 0px;"
/>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
<li
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
data-menu-id="rc-menu-uuid-test-2"
role="menuitem"
tabindex="-1"
>
<span
class="ant-dropdown-menu-title-content"
>
<a
href="http://www.taobao.com/"
rel="noopener noreferrer"
target="_blank"
>
Layout
</a>
</span>
</li>
<div
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-dropdown-menu-inline-collapsed-tooltip ant-tooltip-placement-right"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-tooltip-arrow"
style="position: absolute; top: 0px; left: 0px;"
/>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
<li
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
data-menu-id="rc-menu-uuid-test-3"
role="menuitem"
tabindex="-1"
>
<span
class="ant-dropdown-menu-title-content"
>
<a
href="http://www.tmall.com/"
rel="noopener noreferrer"
target="_blank"
>
Navigation
</a>
</span>
</li>
<div
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-dropdown-menu-inline-collapsed-tooltip ant-tooltip-placement-right"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-tooltip-arrow"
style="position: absolute; top: 0px; left: 0px;"
/>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</ul>
<div
aria-hidden="true"
style="display: none;"
>
<div
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-dropdown-menu-inline-collapsed-tooltip ant-tooltip-placement-right"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-tooltip-arrow"
style="position: absolute; top: 0px; left: 0px;"
/>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
<div
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-dropdown-menu-inline-collapsed-tooltip ant-tooltip-placement-right"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-tooltip-arrow"
style="position: absolute; top: 0px; left: 0px;"
/>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
<div
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-dropdown-menu-inline-collapsed-tooltip ant-tooltip-placement-right"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-tooltip-arrow"
style="position: absolute; top: 0px; left: 0px;"
/>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</div>
</div>
</li>
<li
aria-hidden="true"
class="ant-breadcrumb-separator"
>
&gt;
</li>
<li>
<a
class="ant-breadcrumb-link"
href=""
>
Application Center
</a>
</li>
<li
aria-hidden="true"
class="ant-breadcrumb-separator"
>
&gt;
</li>
<li>
<a
class="ant-breadcrumb-link"
href=""
>
<span
aria-label="home"
class="anticon anticon-home"
role="img"
>
<svg
aria-hidden="true"
data-icon="home"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M946.5 505L560.1 118.8l-25.9-25.9a31.5 31.5 0 00-44.4 0L77.5 505a63.9 63.9 0 00-18.8 46c.4 35.2 29.7 63.3 64.9 63.3h42.5V940h691.8V614.3h43.4c17.1 0 33.2-6.7 45.3-18.8a63.6 63.6 0 0018.7-45.3c0-17-6.7-33.1-18.8-45.2zM568 868H456V664h112v204zm217.9-325.7V868H632V640c0-22.1-17.9-40-40-40H432c-22.1 0-40 17.9-40 40v228H238.1V542.3h-96l370-369.7 23.1 23.1L882 542.3h-96.1z"
/>
</svg>
</span>
</a>
</li>
<li
aria-hidden="true"
class="ant-breadcrumb-separator"
>
&gt;
</li>
<li>
<a
class="ant-breadcrumb-link"
href=""
>
<span
aria-label="user"
class="anticon anticon-user"
role="img"
>
<svg
aria-hidden="true"
data-icon="user"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M858.5 763.6a374 374 0 00-80.6-119.5 375.63 375.63 0 00-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 00-80.6 119.5A371.7 371.7 0 00136 901.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 008-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"
/>
</svg>
</span>
<span>
Application List
</span>
</a>
</li>
</ol>
</nav>
`;
exports[`renders components/breadcrumb/demo/debug-routes.tsx extend context correctly 1`] = ` exports[`renders components/breadcrumb/demo/debug-routes.tsx extend context correctly 1`] = `
<nav <nav
class="ant-breadcrumb" class="ant-breadcrumb"

View File

@ -63,6 +63,160 @@ exports[`renders components/breadcrumb/demo/basic.tsx correctly 1`] = `
</nav> </nav>
`; `;
exports[`renders components/breadcrumb/demo/component-token.tsx correctly 1`] = `
<nav
class="ant-breadcrumb"
>
<ol>
<li>
<span
class="ant-breadcrumb-link"
>
Home
</span>
</li>
<li
aria-hidden="true"
class="ant-breadcrumb-separator"
>
&gt;
</li>
<li>
<span
class="ant-breadcrumb-link"
>
<a
href=""
>
Application Center
</a>
</span>
</li>
<li
aria-hidden="true"
class="ant-breadcrumb-separator"
>
&gt;
</li>
<li>
<span
class="ant-dropdown-trigger ant-breadcrumb-overlay-link"
>
<span
class="ant-breadcrumb-link"
>
<a
href=""
>
General
</a>
</span>
<span
aria-label="down"
class="anticon anticon-down"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</li>
<li
aria-hidden="true"
class="ant-breadcrumb-separator"
>
&gt;
</li>
<li>
<a
class="ant-breadcrumb-link"
href=""
>
Application Center
</a>
</li>
<li
aria-hidden="true"
class="ant-breadcrumb-separator"
>
&gt;
</li>
<li>
<a
class="ant-breadcrumb-link"
href=""
>
<span
aria-label="home"
class="anticon anticon-home"
role="img"
>
<svg
aria-hidden="true"
data-icon="home"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M946.5 505L560.1 118.8l-25.9-25.9a31.5 31.5 0 00-44.4 0L77.5 505a63.9 63.9 0 00-18.8 46c.4 35.2 29.7 63.3 64.9 63.3h42.5V940h691.8V614.3h43.4c17.1 0 33.2-6.7 45.3-18.8a63.6 63.6 0 0018.7-45.3c0-17-6.7-33.1-18.8-45.2zM568 868H456V664h112v204zm217.9-325.7V868H632V640c0-22.1-17.9-40-40-40H432c-22.1 0-40 17.9-40 40v228H238.1V542.3h-96l370-369.7 23.1 23.1L882 542.3h-96.1z"
/>
</svg>
</span>
</a>
</li>
<li
aria-hidden="true"
class="ant-breadcrumb-separator"
>
&gt;
</li>
<li>
<a
class="ant-breadcrumb-link"
href=""
>
<span
aria-label="user"
class="anticon anticon-user"
role="img"
>
<svg
aria-hidden="true"
data-icon="user"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M858.5 763.6a374 374 0 00-80.6-119.5 375.63 375.63 0 00-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 00-80.6 119.5A371.7 371.7 0 00136 901.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 008-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"
/>
</svg>
</span>
<span>
Application List
</span>
</a>
</li>
</ol>
</nav>
`;
exports[`renders components/breadcrumb/demo/debug-routes.tsx correctly 1`] = ` exports[`renders components/breadcrumb/demo/debug-routes.tsx correctly 1`] = `
<nav <nav
class="ant-breadcrumb" class="ant-breadcrumb"

View File

@ -0,0 +1,7 @@
## zh-CN
Component Token Debug.
## en-US
Component Token Debug.

View File

@ -0,0 +1,80 @@
import { HomeOutlined, UserOutlined } from '@ant-design/icons';
import { Breadcrumb, ConfigProvider } from 'antd';
import React from 'react';
const menuItems = [
{
key: '1',
label: (
<a target="_blank" rel="noopener noreferrer" href="http://www.alipay.com/">
General
</a>
),
},
{
key: '2',
label: (
<a target="_blank" rel="noopener noreferrer" href="http://www.taobao.com/">
Layout
</a>
),
},
{
key: '3',
label: (
<a target="_blank" rel="noopener noreferrer" href="http://www.tmall.com/">
Navigation
</a>
),
},
];
export default () => (
<ConfigProvider
theme={{
components: {
Breadcrumb: {
itemColor: '#b02121',
lastItemColor: '#0f3a88',
iconFontSize: 28,
linkColor: '#979a42',
linkHoverColor: '#9450c0',
separatorColor: '#b41b60',
separatorMargin: 22,
},
},
}}
>
<Breadcrumb
separator=">"
items={[
{
title: 'Home',
},
{
title: <a href="">Application Center</a>,
},
{
title: <a href="">General</a>,
menu: { items: menuItems },
},
{
title: 'Application Center',
href: '',
},
{
href: '',
title: <HomeOutlined />,
},
{
href: '',
title: (
<>
<UserOutlined />
<span>Application List</span>
</>
),
},
]}
/>
</ConfigProvider>
);

View File

@ -42,6 +42,7 @@ return <Breadcrumb routes={[{ breadcrumbName: 'sample' }]} />;
<code src="./demo/overlay.tsx">Bread crumbs with drop down menu</code> <code src="./demo/overlay.tsx">Bread crumbs with drop down menu</code>
<code src="./demo/separator-component.tsx">Configuring the Separator Independently</code> <code src="./demo/separator-component.tsx">Configuring the Separator Independently</code>
<code src="./demo/debug-routes.tsx">Debug Routes</code> <code src="./demo/debug-routes.tsx">Debug Routes</code>
<code src="./demo/component-token.tsx" debug>Component Token</code>
## API ## API

View File

@ -43,6 +43,7 @@ return <Breadcrumb routes={[{ breadcrumbName: 'sample' }]} />;
<code src="./demo/overlay.tsx">带下拉菜单的面包屑</code> <code src="./demo/overlay.tsx">带下拉菜单的面包屑</code>
<code src="./demo/separator-component.tsx">独立的分隔符</code> <code src="./demo/separator-component.tsx">独立的分隔符</code>
<code src="./demo/debug-routes.tsx">Debug Routes</code> <code src="./demo/debug-routes.tsx">Debug Routes</code>
<code src="./demo/component-token.tsx" debug>组件 Token</code>
## API ## API

View File

@ -1,30 +1,31 @@
import type { CSSObject } from '@ant-design/cssinjs'; import type { CSSObject } from '@ant-design/cssinjs';
import { genFocusStyle, resetComponent } from '../../style';
import type { FullToken, GenerateStyle } from '../../theme/internal'; import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal'; import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import { genFocusStyle, resetComponent } from '../../style';
interface BreadcrumbToken extends FullToken<'Breadcrumb'> { export interface ComponentToken {
breadcrumbBaseColor: string; itemColor: string;
breadcrumbFontSize: number; iconFontSize: number;
breadcrumbIconFontSize: number; linkColor: string;
breadcrumbLinkColor: string; linkHoverColor: string;
breadcrumbLinkColorHover: string; lastItemColor: string;
breadcrumbLastItemColor: string; separatorMargin: number;
breadcrumbSeparatorMargin: number; separatorColor: string;
breadcrumbSeparatorColor: string;
} }
interface BreadcrumbToken extends FullToken<'Breadcrumb'> {}
const genBreadcrumbStyle: GenerateStyle<BreadcrumbToken, CSSObject> = (token) => { const genBreadcrumbStyle: GenerateStyle<BreadcrumbToken, CSSObject> = (token) => {
const { componentCls, iconCls } = token; const { componentCls, iconCls } = token;
return { return {
[componentCls]: { [componentCls]: {
...resetComponent(token), ...resetComponent(token),
color: token.breadcrumbBaseColor, color: token.itemColor,
fontSize: token.breadcrumbFontSize, fontSize: token.fontSize,
[iconCls]: { [iconCls]: {
fontSize: token.breadcrumbIconFontSize, fontSize: token.iconFontSize,
}, },
ol: { ol: {
@ -36,7 +37,7 @@ const genBreadcrumbStyle: GenerateStyle<BreadcrumbToken, CSSObject> = (token) =>
}, },
a: { a: {
color: token.breadcrumbLinkColor, color: token.linkColor,
transition: `color ${token.motionDurationMid}`, transition: `color ${token.motionDurationMid}`,
padding: `0 ${token.paddingXXS}px`, padding: `0 ${token.paddingXXS}px`,
borderRadius: token.borderRadiusSM, borderRadius: token.borderRadiusSM,
@ -45,7 +46,7 @@ const genBreadcrumbStyle: GenerateStyle<BreadcrumbToken, CSSObject> = (token) =>
marginInline: -token.marginXXS, marginInline: -token.marginXXS,
'&:hover': { '&:hover': {
color: token.breadcrumbLinkColorHover, color: token.linkHoverColor,
backgroundColor: token.colorBgTextHover, backgroundColor: token.colorBgTextHover,
}, },
@ -53,12 +54,12 @@ const genBreadcrumbStyle: GenerateStyle<BreadcrumbToken, CSSObject> = (token) =>
}, },
[`li:last-child`]: { [`li:last-child`]: {
color: token.breadcrumbLastItemColor, color: token.lastItemColor,
}, },
[`${componentCls}-separator`]: { [`${componentCls}-separator`]: {
marginInline: token.breadcrumbSeparatorMargin, marginInline: token.separatorMargin,
color: token.breadcrumbSeparatorColor, color: token.separatorColor,
}, },
[`${componentCls}-link`]: { [`${componentCls}-link`]: {
@ -83,11 +84,11 @@ const genBreadcrumbStyle: GenerateStyle<BreadcrumbToken, CSSObject> = (token) =>
}, },
'&:hover': { '&:hover': {
color: token.breadcrumbLinkColorHover, color: token.linkHoverColor,
backgroundColor: token.colorBgTextHover, backgroundColor: token.colorBgTextHover,
a: { a: {
color: token.breadcrumbLinkColorHover, color: token.linkHoverColor,
}, },
}, },
@ -107,17 +108,19 @@ const genBreadcrumbStyle: GenerateStyle<BreadcrumbToken, CSSObject> = (token) =>
}; };
// ============================== Export ============================== // ============================== Export ==============================
export default genComponentStyleHook('Breadcrumb', (token) => { export default genComponentStyleHook(
const BreadcrumbToken = mergeToken<BreadcrumbToken>(token, { 'Breadcrumb',
breadcrumbBaseColor: token.colorTextDescription, (token) => {
breadcrumbFontSize: token.fontSize, const BreadcrumbToken = mergeToken<BreadcrumbToken>(token, {});
breadcrumbIconFontSize: token.fontSize, return [genBreadcrumbStyle(BreadcrumbToken)];
breadcrumbLinkColor: token.colorTextDescription, },
breadcrumbLinkColorHover: token.colorText, (token) => ({
breadcrumbLastItemColor: token.colorText, itemColor: token.colorTextDescription,
breadcrumbSeparatorMargin: token.marginXS, lastItemColor: token.colorText,
breadcrumbSeparatorColor: token.colorTextDescription, iconFontSize: token.fontSize,
}); linkColor: token.colorTextDescription,
linkHoverColor: token.colorText,
return [genBreadcrumbStyle(BreadcrumbToken)]; separatorColor: token.colorTextDescription,
}); separatorMargin: token.marginXS,
}),
);

View File

@ -18,12 +18,12 @@ import DisabledContext from '../config-provider/DisabledContext';
import type { SizeType } from '../config-provider/SizeContext'; import type { SizeType } from '../config-provider/SizeContext';
import useSize from '../config-provider/hooks/useSize'; import useSize from '../config-provider/hooks/useSize';
import { useCompactItemContext } from '../space/Compact'; import { useCompactItemContext } from '../space/Compact';
import IconWrapper from './IconWrapper';
import LoadingIcon from './LoadingIcon'; import LoadingIcon from './LoadingIcon';
import Group, { GroupSizeContext } from './button-group'; import Group, { GroupSizeContext } from './button-group';
import type { ButtonHTMLType, ButtonShape, ButtonType } from './buttonHelpers'; import type { ButtonHTMLType, ButtonShape, ButtonType } from './buttonHelpers';
import { isTwoCNChar, isUnBorderedButtonType, spaceChildren } from './buttonHelpers'; import { isTwoCNChar, isUnBorderedButtonType, spaceChildren } from './buttonHelpers';
import useStyle from './style'; import useStyle from './style';
import IconWrapper from './IconWrapper';
export type LegacyButtonType = ButtonType | 'danger'; export type LegacyButtonType = ButtonType | 'danger';
@ -121,10 +121,11 @@ const InternalButton: React.ForwardRefRenderFunction<
// React does not recognize the `htmlType` prop on a DOM element. Here we pick it out of `rest`. // React does not recognize the `htmlType` prop on a DOM element. Here we pick it out of `rest`.
htmlType = 'button', htmlType = 'button',
classNames: customClassNames, classNames: customClassNames,
style: customStyle = {},
...rest ...rest
} = props; } = props;
const { getPrefixCls, autoInsertSpaceInButton, direction } = useContext(ConfigContext); const { getPrefixCls, autoInsertSpaceInButton, direction, button } = useContext(ConfigContext);
const prefixCls = getPrefixCls('btn', customizePrefixCls); const prefixCls = getPrefixCls('btn', customizePrefixCls);
const [wrapSSR, hashId] = useStyle(prefixCls); const [wrapSSR, hashId] = useStyle(prefixCls);
@ -238,11 +239,17 @@ const InternalButton: React.ForwardRefRenderFunction<
compactItemClassnames, compactItemClassnames,
className, className,
rootClassName, rootClassName,
button?.className,
); );
const fullStyle = { ...button?.style, ...customStyle };
const iconClasses = classNames(customClassNames?.icon, button?.classNames?.icon);
const iconStyle = { ...(styles?.icon || {}), ...(button?.styles?.icon || {}) };
const iconNode = const iconNode =
icon && !innerLoading ? ( icon && !innerLoading ? (
<IconWrapper prefixCls={prefixCls} className={customClassNames?.icon} style={styles?.icon}> <IconWrapper prefixCls={prefixCls} className={iconClasses} style={iconStyle}>
{icon} {icon}
</IconWrapper> </IconWrapper>
) : ( ) : (
@ -257,6 +264,7 @@ const InternalButton: React.ForwardRefRenderFunction<
<a <a
{...linkButtonRestProps} {...linkButtonRestProps}
className={classes} className={classes}
style={fullStyle}
onClick={handleClick} onClick={handleClick}
ref={buttonRef as React.Ref<HTMLAnchorElement>} ref={buttonRef as React.Ref<HTMLAnchorElement>}
> >
@ -271,6 +279,7 @@ const InternalButton: React.ForwardRefRenderFunction<
{...(rest as NativeButtonProps)} {...(rest as NativeButtonProps)}
type={htmlType} type={htmlType}
className={classes} className={classes}
style={fullStyle}
onClick={handleClick} onClick={handleClick}
disabled={mergedDisabled} disabled={mergedDisabled}
ref={buttonRef as React.Ref<HTMLButtonElement>} ref={buttonRef as React.Ref<HTMLButtonElement>}

View File

@ -5,7 +5,7 @@ import { useContext, useMemo } from 'react';
import { FormItemInputContext } from '../form/context'; import { FormItemInputContext } from '../form/context';
import { Button, Group } from '../radio'; import { Button, Group } from '../radio';
import Select from '../select'; import Select from '../select';
import type { CalendarMode } from './generateCalendar'; import type { CalendarMode, SelectInfo } from './generateCalendar';
const YearSelectOffset = 10; const YearSelectOffset = 10;
const YearSelectTotal = 20; const YearSelectTotal = 20;
@ -147,7 +147,7 @@ export interface CalendarHeaderProps<DateType> {
locale: Locale; locale: Locale;
mode: CalendarMode; mode: CalendarMode;
fullscreen: boolean; fullscreen: boolean;
onChange: (date: DateType) => void; onChange: (date: DateType, source: SelectInfo['source']) => void;
onModeChange: (mode: CalendarMode) => void; onModeChange: (mode: CalendarMode) => void;
} }
function CalendarHeader<DateType>(props: CalendarHeaderProps<DateType>) { function CalendarHeader<DateType>(props: CalendarHeaderProps<DateType>) {
@ -165,7 +165,6 @@ function CalendarHeader<DateType>(props: CalendarHeaderProps<DateType>) {
const sharedProps = { const sharedProps = {
...props, ...props,
onChange,
fullscreen, fullscreen,
divRef, divRef,
}; };
@ -173,8 +172,20 @@ function CalendarHeader<DateType>(props: CalendarHeaderProps<DateType>) {
return ( return (
<div className={`${prefixCls}-header`} ref={divRef}> <div className={`${prefixCls}-header`} ref={divRef}>
<FormItemInputContext.Provider value={mergedFormItemInputContext}> <FormItemInputContext.Provider value={mergedFormItemInputContext}>
<YearSelect {...sharedProps} /> <YearSelect
{mode === 'month' && <MonthSelect {...sharedProps} />} {...sharedProps}
onChange={(v) => {
onChange(v, 'year');
}}
/>
{mode === 'month' && (
<MonthSelect
{...sharedProps}
onChange={(v) => {
onChange(v, 'month');
}}
/>
)}
</FormItemInputContext.Provider> </FormItemInputContext.Provider>
<ModeSwitch {...sharedProps} onModeChange={onModeChange} /> <ModeSwitch {...sharedProps} onModeChange={onModeChange} />
</div> </div>

File diff suppressed because it is too large Load Diff

View File

@ -76,7 +76,7 @@ describe('Calendar', () => {
const { container } = render(<Calendar onSelect={onSelect} onChange={onChange} />); const { container } = render(<Calendar onSelect={onSelect} onChange={onChange} />);
fireEvent.click(container.querySelector('.ant-picker-cell')!); fireEvent.click(container.querySelector('.ant-picker-cell')!);
expect(onSelect).toHaveBeenCalledWith(expect.anything()); expect(onSelect).toHaveBeenCalledWith(expect.anything(), { source: 'date' });
const value = onSelect.mock.calls[0][0]; const value = onSelect.mock.calls[0][0];
expect(Dayjs.isDayjs(value)).toBe(true); expect(Dayjs.isDayjs(value)).toBe(true);
@ -270,7 +270,7 @@ describe('Calendar', () => {
const end = Dayjs('2019-11-01'); const end = Dayjs('2019-11-01');
const onValueChange = jest.fn(); const onValueChange = jest.fn();
createWrapper(start, end, value, onValueChange); createWrapper(start, end, value, onValueChange);
expect(onValueChange).toHaveBeenCalledWith(value.year(2019).month(3)); expect(onValueChange).toHaveBeenCalledWith(value.year(2019).month(3), 'year');
}); });
it('if start.month > value.month, set value.month to start.month', () => { it('if start.month > value.month, set value.month to start.month', () => {
@ -279,7 +279,7 @@ describe('Calendar', () => {
const end = Dayjs('2019-03-01'); const end = Dayjs('2019-03-01');
const onValueChange = jest.fn(); const onValueChange = jest.fn();
createWrapper(start, end, value, onValueChange); createWrapper(start, end, value, onValueChange);
expect(onValueChange).toHaveBeenCalledWith(value.year(2019).month(10)); expect(onValueChange).toHaveBeenCalledWith(value.year(2019).month(10), 'year');
}); });
it('if change year and month > end month, set value.month to end.month', () => { it('if change year and month > end month, set value.month to end.month', () => {
@ -302,7 +302,7 @@ describe('Calendar', () => {
fireEvent.click( fireEvent.click(
Array.from(wrapper.container.querySelectorAll('.ant-select-item-option')).at(-1)!, Array.from(wrapper.container.querySelectorAll('.ant-select-item-option')).at(-1)!,
); );
expect(onValueChange).toHaveBeenCalledWith(value.year(2019).month(2)); expect(onValueChange).toHaveBeenCalledWith(value.year(2019).month(2), 'year');
}); });
it('onMonthChange should work correctly', () => { it('onMonthChange should work correctly', () => {
@ -324,7 +324,7 @@ describe('Calendar', () => {
); );
openSelect(wrapper.container, '.ant-picker-calendar-month-select'); openSelect(wrapper.container, '.ant-picker-calendar-month-select');
clickSelectItem(wrapper.container); clickSelectItem(wrapper.container);
expect(onValueChange).toHaveBeenCalledWith(value.month(10)); expect(onValueChange).toHaveBeenCalledWith(value.month(10), 'month');
}); });
it('onTypeChange should work correctly', () => { it('onTypeChange should work correctly', () => {

View File

@ -0,0 +1,99 @@
import Dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
import MockDate from 'mockdate';
import { resetWarned } from 'rc-util/lib/warning';
import React from 'react';
import Calendar from '..';
import { fireEvent, render, waitFakeTimer } from '../../../tests/utils';
describe('Calendar.onSelect', () => {
beforeAll(() => {
MockDate.set(Dayjs('2000-01-01').valueOf());
});
beforeEach(() => {
resetWarned();
jest.useFakeTimers();
jest.clearAllTimers();
});
afterEach(() => {
jest.useRealTimers();
});
it('source of year select', async () => {
const onSelect = jest.fn();
const { container } = render(<Calendar onSelect={onSelect} />);
fireEvent.mouseDown(container.querySelector('.ant-select-selector')!);
await waitFakeTimer();
fireEvent.click(container.querySelector('.ant-select-item-option')!);
await waitFakeTimer();
expect(onSelect).toHaveBeenCalledWith(expect.anything(), { source: 'year' });
});
it('source of month select', async () => {
const onSelect = jest.fn();
const { container } = render(<Calendar onSelect={onSelect} />);
fireEvent.mouseDown(container.querySelectorAll('.ant-select-selector')[1]!);
await waitFakeTimer();
fireEvent.click(container.querySelector('.ant-select-item-option')!);
await waitFakeTimer();
expect(onSelect).toHaveBeenCalledWith(expect.anything(), { source: 'month' });
});
it('source of customize', async () => {
const onSelect = jest.fn();
const { container } = render(
<Calendar
onSelect={onSelect}
headerRender={({ onChange }) => (
<button
className="bamboo"
type="button"
onClick={() => {
onChange(Dayjs('1999-01-01'));
}}
>
Trigger
</button>
)}
/>,
);
fireEvent.click(container.querySelector('.bamboo')!);
await waitFakeTimer();
expect(onSelect).toHaveBeenCalledWith(expect.anything(), { source: 'customize' });
});
it('source of date', () => {
const onSelect = jest.fn();
const { container } = render(<Calendar onSelect={onSelect} />);
fireEvent.click(container.querySelector('.ant-picker-cell')!);
expect(onSelect).toHaveBeenCalledWith(expect.anything(), { source: 'date' });
});
it('source of date with month panel', async () => {
const onSelect = jest.fn();
const onPanelChange = jest.fn();
const { container } = render(<Calendar onSelect={onSelect} onPanelChange={onPanelChange} />);
// Default is month radio
fireEvent.click(container.querySelector('.ant-picker-cell')!);
expect(onSelect).toHaveBeenCalledWith(expect.anything(), { source: 'date' });
// Click year radio
fireEvent.click(container.querySelectorAll('.ant-radio-button-input')[1]!);
expect(onPanelChange).toHaveBeenCalledWith(expect.anything(), 'year');
fireEvent.click(container.querySelector('.ant-picker-cell')!);
expect(onSelect).toHaveBeenCalledWith(expect.anything(), { source: 'month' });
});
});

View File

@ -0,0 +1,7 @@
## zh-CN
Component Token Debug.
## en-US
Component Token Debug.

View File

@ -0,0 +1,29 @@
import { Calendar, ConfigProvider } from 'antd';
import type { CalendarMode } from 'antd/es/calendar/generateCalendar';
import type { Dayjs } from 'dayjs';
import React from 'react';
/** Test usage. Do not use in your production. */
export default () => {
const onPanelChange = (value: Dayjs, mode: CalendarMode) => {
console.log(value.format('YYYY-MM-DD'), mode);
};
return (
<ConfigProvider
theme={{
components: {
Calendar: {
fullBg: 'red',
fullPanelBg: 'green',
itemActiveBg: 'black',
},
},
}}
>
<Calendar onPanelChange={onPanelChange} />
<br />
<Calendar onPanelChange={onPanelChange} fullscreen={false} />
</ConfigProvider>
);
};

View File

@ -1,12 +1,12 @@
import classNames from 'classnames'; import classNames from 'classnames';
import { PickerPanel as RCPickerPanel } from 'rc-picker'; import { PickerPanel as RCPickerPanel } from 'rc-picker';
import type { GenerateConfig } from 'rc-picker/lib/generate';
import type { CellRenderInfo } from 'rc-picker/lib/interface';
import type { import type {
PickerPanelBaseProps as RCPickerPanelBaseProps, PickerPanelBaseProps as RCPickerPanelBaseProps,
PickerPanelDateProps as RCPickerPanelDateProps, PickerPanelDateProps as RCPickerPanelDateProps,
PickerPanelTimeProps as RCPickerPanelTimeProps, PickerPanelTimeProps as RCPickerPanelTimeProps,
} from 'rc-picker/lib/PickerPanel'; } from 'rc-picker/lib/PickerPanel';
import type { GenerateConfig } from 'rc-picker/lib/generate';
import type { CellRenderInfo } from 'rc-picker/lib/interface';
import useMergedState from 'rc-util/lib/hooks/useMergedState'; import useMergedState from 'rc-util/lib/hooks/useMergedState';
import * as React from 'react'; import * as React from 'react';
import { ConfigContext } from '../config-provider'; import { ConfigContext } from '../config-provider';
@ -14,8 +14,8 @@ import { useLocale } from '../locale';
import CalendarHeader from './Header'; import CalendarHeader from './Header';
import enUS from './locale/en_US'; import enUS from './locale/en_US';
import useStyle from './style';
import warning from '../_util/warning'; import warning from '../_util/warning';
import useStyle from './style';
type InjectDefaultProps<Props> = Omit< type InjectDefaultProps<Props> = Omit<
Props, Props,
@ -43,6 +43,10 @@ export type HeaderRender<DateType> = (config: {
onTypeChange: (type: CalendarMode) => void; onTypeChange: (type: CalendarMode) => void;
}) => React.ReactNode; }) => React.ReactNode;
export interface SelectInfo {
source: 'year' | 'month' | 'date' | 'customize';
}
export interface CalendarProps<DateType> { export interface CalendarProps<DateType> {
prefixCls?: string; prefixCls?: string;
className?: string; className?: string;
@ -68,7 +72,7 @@ export interface CalendarProps<DateType> {
fullscreen?: boolean; fullscreen?: boolean;
onChange?: (date: DateType) => void; onChange?: (date: DateType) => void;
onPanelChange?: (date: DateType, mode: CalendarMode) => void; onPanelChange?: (date: DateType, mode: CalendarMode) => void;
onSelect?: (date: DateType) => void; onSelect?: (date: DateType, selectInfo: SelectInfo) => void;
} }
function generateCalendar<DateType>(generateConfig: GenerateConfig<DateType>) { function generateCalendar<DateType>(generateConfig: GenerateConfig<DateType>) {
@ -198,10 +202,10 @@ function generateCalendar<DateType>(generateConfig: GenerateConfig<DateType>) {
triggerPanelChange(mergedValue, newMode); triggerPanelChange(mergedValue, newMode);
}; };
const onInternalSelect = (date: DateType) => { const onInternalSelect = (date: DateType, source: SelectInfo['source']) => {
triggerChange(date); triggerChange(date);
onSelect?.(date); onSelect?.(date, { source });
}; };
// ====================== Locale ====================== // ====================== Locale ======================
@ -310,7 +314,9 @@ function generateCalendar<DateType>(generateConfig: GenerateConfig<DateType>) {
headerRender({ headerRender({
value: mergedValue, value: mergedValue,
type: mergedMode, type: mergedMode,
onChange: onInternalSelect, onChange: (nextDate) => {
onInternalSelect(nextDate, 'customize');
},
onTypeChange: triggerModeChange, onTypeChange: triggerModeChange,
}) })
) : ( ) : (
@ -332,7 +338,9 @@ function generateCalendar<DateType>(generateConfig: GenerateConfig<DateType>) {
locale={contextLocale?.lang} locale={contextLocale?.lang}
generateConfig={generateConfig} generateConfig={generateConfig}
cellRender={mergedCellRender} cellRender={mergedCellRender}
onSelect={onInternalSelect} onSelect={(nextDate) => {
onInternalSelect(nextDate, panelMode);
}}
mode={panelMode} mode={panelMode}
picker={panelMode} picker={panelMode}
disabledDate={mergedDisabledDate} disabledDate={mergedDisabledDate}

View File

@ -20,6 +20,7 @@ When data is in the form of dates, such as schedules, timetables, prices calenda
<code src="./demo/card.tsx" clientOnly>Card</code> <code src="./demo/card.tsx" clientOnly>Card</code>
<code src="./demo/select.tsx" clientOnly>Selectable Calendar</code> <code src="./demo/select.tsx" clientOnly>Selectable Calendar</code>
<code src="./demo/customize-header.tsx" clientOnly>Customize Header</code> <code src="./demo/customize-header.tsx" clientOnly>Customize Header</code>
<code src="./demo/component-token.tsx" debug>Component Token</code>
## API ## API
@ -55,7 +56,7 @@ When data is in the form of dates, such as schedules, timetables, prices calenda
| value | The current selected date | [dayjs](https://day.js.org/) | - | | | value | The current selected date | [dayjs](https://day.js.org/) | - | |
| onChange | Callback for when date changes | function(date: Dayjs) | - | | | onChange | Callback for when date changes | function(date: Dayjs) | - | |
| onPanelChange | Callback for when panel changes | function(date: Dayjs, mode: string) | - | | | onPanelChange | Callback for when panel changes | function(date: Dayjs, mode: string) | - | |
| onSelect | Callback for when a date is selected | function(date: Dayjs | - | | | onSelect | Callback for when a date is selected, include source info | function(date: Dayjs, info: { source: 'year' \| 'month' \| 'date' \| 'customize' }) | - | `info`: 5.6.0 |
## Design Token ## Design Token
@ -74,3 +75,17 @@ See [How to set locale for date-related components](/components/date-picker/#loc
### Date-related components locale is not working? ### Date-related components locale is not working?
See FAQ [Date-related-components-locale-is-not-working?](/docs/react/faq#date-related-components-locale-is-not-working) See FAQ [Date-related-components-locale-is-not-working?](/docs/react/faq#date-related-components-locale-is-not-working)
### How to get date from panel click?
`onSelect` provide `info.source` to help on this:
```tsx
<Calendar
onSelect={(date, { source }) => {
if (source === 'date') {
console.log('Panel Select:', source);
}
}}
/>
```

View File

@ -21,6 +21,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*-p-wQLik200AAA
<code src="./demo/card.tsx" clientOnly>卡片模式</code> <code src="./demo/card.tsx" clientOnly>卡片模式</code>
<code src="./demo/select.tsx" clientOnly>选择功能</code> <code src="./demo/select.tsx" clientOnly>选择功能</code>
<code src="./demo/customize-header.tsx" clientOnly>自定义头部</code> <code src="./demo/customize-header.tsx" clientOnly>自定义头部</code>
<code src="./demo/component-token.tsx" debug>组件 Token</code>
## API ## API
@ -60,7 +61,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*-p-wQLik200AAA
| value | 展示日期 | [dayjs](https://day.js.org/) | - | | | value | 展示日期 | [dayjs](https://day.js.org/) | - | |
| onChange | 日期变化回调 | function(date: Dayjs) | - | | | onChange | 日期变化回调 | function(date: Dayjs) | - | |
| onPanelChange | 日期面板变化回调 | function(date: Dayjs, mode: string) | - | | | onPanelChange | 日期面板变化回调 | function(date: Dayjs, mode: string) | - | |
| onSelect | 点击选择日期回调 | function(date: Dayjs | - | | | onSelect | 选择日期回调,包含来源信息 | function(date: Dayjs, info: { source: 'year' \| 'month' \| 'date' \| 'customize' }) | - | `info`: 5.6.0 |
## Design Token ## Design Token
@ -79,3 +80,17 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*-p-wQLik200AAA
### 为什么时间类组件的国际化 locale 设置不生效? ### 为什么时间类组件的国际化 locale 设置不生效?
参考 FAQ [为什么时间类组件的国际化 locale 设置不生效?](/docs/react/faq#为什么时间类组件的国际化-locale-设置不生效)。 参考 FAQ [为什么时间类组件的国际化 locale 设置不生效?](/docs/react/faq#为什么时间类组件的国际化-locale-设置不生效)。
### 如何仅获取来自面板点击的日期?
`onSelect` 事件提供额外的来源信息,你可以通过 `info.source` 来判断来源:
```tsx
<Calendar
onSelect={(date, { source }) => {
if (source === 'date') {
console.log('Panel Select:', source);
}
}}
/>
```

View File

@ -1,9 +1,9 @@
import type { CSSObject } from '@ant-design/cssinjs'; import type { CSSObject } from '@ant-design/cssinjs';
import { resetComponent } from '../../style';
import type { PickerPanelToken } from '../../date-picker/style'; import type { PickerPanelToken } from '../../date-picker/style';
import { genPanelStyle, initPickerPanelToken } from '../../date-picker/style'; import { genPanelStyle, initPickerPanelToken } from '../../date-picker/style';
import type { InputToken } from '../../input/style'; import type { InputToken } from '../../input/style';
import { initInputToken } from '../../input/style'; import { initInputToken } from '../../input/style';
import { resetComponent } from '../../style';
import type { FullToken } from '../../theme/internal'; import type { FullToken } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal'; import { genComponentStyleHook, mergeToken } from '../../theme/internal';
@ -11,26 +11,25 @@ export interface ComponentToken {
yearControlWidth: number; yearControlWidth: number;
monthControlWidth: number; monthControlWidth: number;
miniContentHeight: number; miniContentHeight: number;
fullBg: string;
fullPanelBg: string;
itemActiveBg: string;
} }
interface CalendarToken extends InputToken<FullToken<'Calendar'>>, PickerPanelToken { interface CalendarToken extends InputToken<FullToken<'Calendar'>>, PickerPanelToken {
calendarCls: string; calendarCls: string;
calendarFullBg: string;
calendarFullPanelBg: string;
calendarItemActiveBg: string;
dateValueHeight: number; dateValueHeight: number;
weekHeight: number; weekHeight: number;
dateContentHeight: number; dateContentHeight: number;
} }
export const genCalendarStyles = (token: CalendarToken): CSSObject => { export const genCalendarStyles = (token: CalendarToken): CSSObject => {
const { calendarCls, componentCls, calendarFullBg, calendarFullPanelBg, calendarItemActiveBg } = const { calendarCls, componentCls, fullBg, fullPanelBg, itemActiveBg } = token;
token;
return { return {
[calendarCls]: { [calendarCls]: {
...genPanelStyle(token), ...genPanelStyle(token),
...resetComponent(token), ...resetComponent(token),
background: calendarFullBg, background: fullBg,
'&-rtl': { '&-rtl': {
direction: 'rtl', direction: 'rtl',
}, },
@ -52,7 +51,7 @@ export const genCalendarStyles = (token: CalendarToken): CSSObject => {
}, },
}, },
[`${calendarCls} ${componentCls}-panel`]: { [`${calendarCls} ${componentCls}-panel`]: {
background: calendarFullPanelBg, background: fullPanelBg,
border: 0, border: 0,
borderTop: `${token.lineWidth}px ${token.lineType} ${token.colorSplit}`, borderTop: `${token.lineWidth}px ${token.lineType} ${token.colorSplit}`,
borderRadius: 0, borderRadius: 0,
@ -92,7 +91,7 @@ export const genCalendarStyles = (token: CalendarToken): CSSObject => {
display: 'block', display: 'block',
width: '100%', width: '100%',
textAlign: 'end', textAlign: 'end',
background: calendarFullBg, background: fullBg,
border: 0, border: 0,
[`${componentCls}-body`]: { [`${componentCls}-body`]: {
'th, td': { 'th, td': {
@ -121,7 +120,7 @@ export const genCalendarStyles = (token: CalendarToken): CSSObject => {
// >>> Selected // >>> Selected
[`&-in-view${componentCls}-cell-selected`]: { [`&-in-view${componentCls}-cell-selected`]: {
[`${calendarCls}-date, ${calendarCls}-date-today`]: { [`${calendarCls}-date, ${calendarCls}-date-today`]: {
background: calendarItemActiveBg, background: itemActiveBg,
}, },
}, },
'&-selected, &-selected:hover': { '&-selected, &-selected:hover': {
@ -198,9 +197,6 @@ export default genComponentStyleHook(
{ {
calendarCls, calendarCls,
pickerCellInnerCls: `${token.componentCls}-cell-inner`, pickerCellInnerCls: `${token.componentCls}-cell-inner`,
calendarFullBg: token.colorBgContainer,
calendarFullPanelBg: token.colorBgContainer,
calendarItemActiveBg: token.controlItemBgActive,
dateValueHeight: token.controlHeightSM, dateValueHeight: token.controlHeightSM,
weekHeight: token.controlHeightSM * 0.75, weekHeight: token.controlHeightSM * 0.75,
dateContentHeight: dateContentHeight:
@ -210,9 +206,12 @@ export default genComponentStyleHook(
return [genCalendarStyles(calendarToken)]; return [genCalendarStyles(calendarToken)];
}, },
{ (token) => ({
fullBg: token.colorBgContainer,
fullPanelBg: token.colorBgContainer,
itemActiveBg: token.controlItemBgActive,
yearControlWidth: 80, yearControlWidth: 80,
monthControlWidth: 70, monthControlWidth: 70,
miniContentHeight: 256, miniContentHeight: 256,
}, }),
); );

View File

@ -1,4 +1,5 @@
import classNames from 'classnames'; import classNames from 'classnames';
import type { Tab } from 'rc-tabs/lib/interface';
import omit from 'rc-util/lib/omit'; import omit from 'rc-util/lib/omit';
import * as React from 'react'; import * as React from 'react';
import { ConfigContext } from '../config-provider'; import { ConfigContext } from '../config-provider';
@ -12,10 +13,11 @@ import useStyle from './style';
export type CardType = 'inner'; export type CardType = 'inner';
export type CardSize = 'default' | 'small'; export type CardSize = 'default' | 'small';
export interface CardTabListType { export interface CardTabListType extends Omit<Tab, 'label'> {
key: string; key: string;
tab: React.ReactNode; /** @deprecated Please use `label` instead */
disabled?: boolean; tab?: React.ReactNode;
label?: React.ReactNode;
} }
export interface CardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> { export interface CardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
@ -123,10 +125,9 @@ const Card = React.forwardRef<HTMLDivElement, CardProps>((props, ref) => {
{...extraProps} {...extraProps}
className={`${prefixCls}-head-tabs`} className={`${prefixCls}-head-tabs`}
onChange={onTabChange} onChange={onTabChange}
items={tabList.map((item) => ({ items={tabList.map(({ tab, ...item }) => ({
label: item.tab, label: tab,
key: item.key, ...item,
disabled: item.disabled ?? false,
}))} }))}
/> />
) : null; ) : null;

View File

@ -130,6 +130,284 @@ exports[`renders components/card/demo/border-less.tsx extend context correctly 1
</div> </div>
`; `;
exports[`renders components/card/demo/component-token.tsx extend context correctly 1`] = `
Array [
<div
class="ant-card ant-card-bordered ant-card-contain-tabs"
>
<div
class="ant-card-head"
>
<div
class="ant-card-head-wrapper"
>
<div
class="ant-card-head-title"
>
Card title
</div>
<div
class="ant-card-extra"
>
More
</div>
</div>
<div
class="ant-tabs ant-tabs-top ant-tabs-large ant-card-head-tabs"
>
<div
class="ant-tabs-nav"
role="tablist"
>
<div
class="ant-tabs-nav-wrap"
>
<div
class="ant-tabs-nav-list"
style="transform: translate(0px, 0px);"
>
<div
class="ant-tabs-tab ant-tabs-tab-active"
data-node-key="tab1"
>
<div
aria-controls="rc-tabs-test-panel-tab1"
aria-selected="true"
class="ant-tabs-tab-btn"
id="rc-tabs-test-tab-tab1"
role="tab"
tabindex="0"
>
tab1
</div>
</div>
<div
class="ant-tabs-tab"
data-node-key="tab2"
>
<div
aria-controls="rc-tabs-test-panel-tab2"
aria-selected="false"
class="ant-tabs-tab-btn"
id="rc-tabs-test-tab-tab2"
role="tab"
tabindex="0"
>
tab2
</div>
</div>
<div
class="ant-tabs-ink-bar ant-tabs-ink-bar-animated"
/>
</div>
</div>
<div
class="ant-tabs-nav-operations ant-tabs-nav-operations-hidden"
>
<button
aria-controls="rc-tabs-test-more-popup"
aria-expanded="false"
aria-haspopup="listbox"
aria-hidden="true"
class="ant-tabs-nav-more"
id="rc-tabs-test-more"
style="visibility: hidden; order: 1;"
tabindex="-1"
type="button"
>
<span
aria-label="ellipsis"
class="anticon anticon-ellipsis"
role="img"
>
<svg
aria-hidden="true"
data-icon="ellipsis"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M176 511a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0z"
/>
</svg>
</span>
</button>
<div
class="ant-tabs-dropdown ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up ant-tabs-dropdown-placement-bottomLeft"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<ul
aria-label="expanded dropdown"
class="ant-tabs-dropdown-menu ant-tabs-dropdown-menu-root ant-tabs-dropdown-menu-vertical"
data-menu-list="true"
id="rc-tabs-test-more-popup"
role="listbox"
tabindex="-1"
/>
<div
aria-hidden="true"
style="display: none;"
/>
</div>
</div>
</div>
<div
class="ant-tabs-content-holder"
>
<div
class="ant-tabs-content ant-tabs-content-top"
>
<div
aria-hidden="false"
aria-labelledby="rc-tabs-test-tab-tab1"
class="ant-tabs-tabpane ant-tabs-tabpane-active"
id="rc-tabs-test-panel-tab1"
role="tabpanel"
tabindex="0"
/>
</div>
</div>
</div>
</div>
<div
class="ant-card-body"
>
<p>
Card content
</p>
<p>
Card content
</p>
<p>
Card content
</p>
</div>
<ul
class="ant-card-actions"
>
<li
style="width: 33.333333333333336%;"
>
<span>
<span
aria-label="setting"
class="anticon anticon-setting"
role="img"
>
<svg
aria-hidden="true"
data-icon="setting"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M924.8 625.7l-65.5-56c3.1-19 4.7-38.4 4.7-57.8s-1.6-38.8-4.7-57.8l65.5-56a32.03 32.03 0 009.3-35.2l-.9-2.6a443.74 443.74 0 00-79.7-137.9l-1.8-2.1a32.12 32.12 0 00-35.1-9.5l-81.3 28.9c-30-24.6-63.5-44-99.7-57.6l-15.7-85a32.05 32.05 0 00-25.8-25.7l-2.7-.5c-52.1-9.4-106.9-9.4-159 0l-2.7.5a32.05 32.05 0 00-25.8 25.7l-15.8 85.4a351.86 351.86 0 00-99 57.4l-81.9-29.1a32 32 0 00-35.1 9.5l-1.8 2.1a446.02 446.02 0 00-79.7 137.9l-.9 2.6c-4.5 12.5-.8 26.5 9.3 35.2l66.3 56.6c-3.1 18.8-4.6 38-4.6 57.1 0 19.2 1.5 38.4 4.6 57.1L99 625.5a32.03 32.03 0 00-9.3 35.2l.9 2.6c18.1 50.4 44.9 96.9 79.7 137.9l1.8 2.1a32.12 32.12 0 0035.1 9.5l81.9-29.1c29.8 24.5 63.1 43.9 99 57.4l15.8 85.4a32.05 32.05 0 0025.8 25.7l2.7.5a449.4 449.4 0 00159 0l2.7-.5a32.05 32.05 0 0025.8-25.7l15.7-85a350 350 0 0099.7-57.6l81.3 28.9a32 32 0 0035.1-9.5l1.8-2.1c34.8-41.1 61.6-87.5 79.7-137.9l.9-2.6c4.5-12.3.8-26.3-9.3-35zM788.3 465.9c2.5 15.1 3.8 30.6 3.8 46.1s-1.3 31-3.8 46.1l-6.6 40.1 74.7 63.9a370.03 370.03 0 01-42.6 73.6L721 702.8l-31.4 25.8c-23.9 19.6-50.5 35-79.3 45.8l-38.1 14.3-17.9 97a377.5 377.5 0 01-85 0l-17.9-97.2-37.8-14.5c-28.5-10.8-55-26.2-78.7-45.7l-31.4-25.9-93.4 33.2c-17-22.9-31.2-47.6-42.6-73.6l75.5-64.5-6.5-40c-2.4-14.9-3.7-30.3-3.7-45.5 0-15.3 1.2-30.6 3.7-45.5l6.5-40-75.5-64.5c11.3-26.1 25.6-50.7 42.6-73.6l93.4 33.2 31.4-25.9c23.7-19.5 50.2-34.9 78.7-45.7l37.9-14.3 17.9-97.2c28.1-3.2 56.8-3.2 85 0l17.9 97 38.1 14.3c28.7 10.8 55.4 26.2 79.3 45.8l31.4 25.8 92.8-32.9c17 22.9 31.2 47.6 42.6 73.6L781.8 426l6.5 39.9zM512 326c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm79.2 255.2A111.6 111.6 0 01512 614c-29.9 0-58-11.7-79.2-32.8A111.6 111.6 0 01400 502c0-29.9 11.7-58 32.8-79.2C454 401.6 482.1 390 512 390c29.9 0 58 11.6 79.2 32.8A111.6 111.6 0 01624 502c0 29.9-11.7 58-32.8 79.2z"
/>
</svg>
</span>
</span>
</li>
<li
style="width: 33.333333333333336%;"
>
<span>
<span
aria-label="edit"
class="anticon anticon-edit"
role="img"
>
<svg
aria-hidden="true"
data-icon="edit"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M257.7 752c2 0 4-.2 6-.5L431.9 722c2-.4 3.9-1.3 5.3-2.8l423.9-423.9a9.96 9.96 0 000-14.1L694.9 114.9c-1.9-1.9-4.4-2.9-7.1-2.9s-5.2 1-7.1 2.9L256.8 538.8c-1.5 1.5-2.4 3.3-2.8 5.3l-29.5 168.2a33.5 33.5 0 009.4 29.8c6.6 6.4 14.9 9.9 23.8 9.9zm67.4-174.4L687.8 215l73.3 73.3-362.7 362.6-88.9 15.7 15.6-89zM880 836H144c-17.7 0-32 14.3-32 32v36c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-36c0-17.7-14.3-32-32-32z"
/>
</svg>
</span>
</span>
</li>
<li
style="width: 33.333333333333336%;"
>
<span>
<span
aria-label="ellipsis"
class="anticon anticon-ellipsis"
role="img"
>
<svg
aria-hidden="true"
data-icon="ellipsis"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M176 511a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0z"
/>
</svg>
</span>
</span>
</li>
</ul>
</div>,
<div
class="ant-card ant-card-bordered ant-card-small"
style="width: 300px;"
>
<div
class="ant-card-head"
>
<div
class="ant-card-head-wrapper"
>
<div
class="ant-card-head-title"
>
Small size card
</div>
<div
class="ant-card-extra"
>
<a
href="#"
>
More
</a>
</div>
</div>
</div>
<div
class="ant-card-body"
>
<p>
Card content
</p>
<p>
Card content
</p>
<p>
Card content
</p>
</div>
</div>,
]
`;
exports[`renders components/card/demo/flexible-content.tsx extend context correctly 1`] = ` exports[`renders components/card/demo/flexible-content.tsx extend context correctly 1`] = `
<div <div
class="ant-card ant-card-bordered ant-card-hoverable" class="ant-card ant-card-bordered ant-card-hoverable"
@ -771,7 +1049,6 @@ Array [
> >
<div <div
aria-controls="rc-tabs-test-panel-tab1" aria-controls="rc-tabs-test-panel-tab1"
aria-disabled="false"
aria-selected="true" aria-selected="true"
class="ant-tabs-tab-btn" class="ant-tabs-tab-btn"
id="rc-tabs-test-tab-tab1" id="rc-tabs-test-tab-tab1"
@ -787,7 +1064,6 @@ Array [
> >
<div <div
aria-controls="rc-tabs-test-panel-tab2" aria-controls="rc-tabs-test-panel-tab2"
aria-disabled="false"
aria-selected="false" aria-selected="false"
class="ant-tabs-tab-btn" class="ant-tabs-tab-btn"
id="rc-tabs-test-tab-tab2" id="rc-tabs-test-tab-tab2"
@ -913,7 +1189,6 @@ Array [
> >
<div <div
aria-controls="rc-tabs-test-panel-article" aria-controls="rc-tabs-test-panel-article"
aria-disabled="false"
aria-selected="false" aria-selected="false"
class="ant-tabs-tab-btn" class="ant-tabs-tab-btn"
id="rc-tabs-test-tab-article" id="rc-tabs-test-tab-article"
@ -929,7 +1204,6 @@ Array [
> >
<div <div
aria-controls="rc-tabs-test-panel-app" aria-controls="rc-tabs-test-panel-app"
aria-disabled="false"
aria-selected="true" aria-selected="true"
class="ant-tabs-tab-btn" class="ant-tabs-tab-btn"
id="rc-tabs-test-tab-app" id="rc-tabs-test-tab-app"
@ -945,7 +1219,6 @@ Array [
> >
<div <div
aria-controls="rc-tabs-test-panel-project" aria-controls="rc-tabs-test-panel-project"
aria-disabled="false"
aria-selected="false" aria-selected="false"
class="ant-tabs-tab-btn" class="ant-tabs-tab-btn"
id="rc-tabs-test-tab-project" id="rc-tabs-test-tab-project"

View File

@ -130,6 +130,261 @@ exports[`renders components/card/demo/border-less.tsx correctly 1`] = `
</div> </div>
`; `;
exports[`renders components/card/demo/component-token.tsx correctly 1`] = `
Array [
<div
class="ant-card ant-card-bordered ant-card-contain-tabs"
>
<div
class="ant-card-head"
>
<div
class="ant-card-head-wrapper"
>
<div
class="ant-card-head-title"
>
Card title
</div>
<div
class="ant-card-extra"
>
More
</div>
</div>
<div
class="ant-tabs ant-tabs-top ant-tabs-large ant-card-head-tabs"
>
<div
class="ant-tabs-nav"
role="tablist"
>
<div
class="ant-tabs-nav-wrap"
>
<div
class="ant-tabs-nav-list"
style="transform:translate(0px, 0px)"
>
<div
class="ant-tabs-tab ant-tabs-tab-active"
data-node-key="tab1"
>
<div
aria-selected="true"
class="ant-tabs-tab-btn"
role="tab"
tabindex="0"
>
tab1
</div>
</div>
<div
class="ant-tabs-tab"
data-node-key="tab2"
>
<div
aria-selected="false"
class="ant-tabs-tab-btn"
role="tab"
tabindex="0"
>
tab2
</div>
</div>
<div
class="ant-tabs-ink-bar ant-tabs-ink-bar-animated"
/>
</div>
</div>
<div
class="ant-tabs-nav-operations ant-tabs-nav-operations-hidden"
>
<button
aria-controls="null-more-popup"
aria-expanded="false"
aria-haspopup="listbox"
aria-hidden="true"
class="ant-tabs-nav-more"
id="null-more"
style="visibility:hidden;order:1"
tabindex="-1"
type="button"
>
<span
aria-label="ellipsis"
class="anticon anticon-ellipsis"
role="img"
>
<svg
aria-hidden="true"
data-icon="ellipsis"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M176 511a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0z"
/>
</svg>
</span>
</button>
</div>
</div>
<div
class="ant-tabs-content-holder"
>
<div
class="ant-tabs-content ant-tabs-content-top"
>
<div
aria-hidden="false"
class="ant-tabs-tabpane ant-tabs-tabpane-active"
role="tabpanel"
tabindex="0"
/>
</div>
</div>
</div>
</div>
<div
class="ant-card-body"
>
<p>
Card content
</p>
<p>
Card content
</p>
<p>
Card content
</p>
</div>
<ul
class="ant-card-actions"
>
<li
style="width:33.333333333333336%"
>
<span>
<span
aria-label="setting"
class="anticon anticon-setting"
role="img"
>
<svg
aria-hidden="true"
data-icon="setting"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M924.8 625.7l-65.5-56c3.1-19 4.7-38.4 4.7-57.8s-1.6-38.8-4.7-57.8l65.5-56a32.03 32.03 0 009.3-35.2l-.9-2.6a443.74 443.74 0 00-79.7-137.9l-1.8-2.1a32.12 32.12 0 00-35.1-9.5l-81.3 28.9c-30-24.6-63.5-44-99.7-57.6l-15.7-85a32.05 32.05 0 00-25.8-25.7l-2.7-.5c-52.1-9.4-106.9-9.4-159 0l-2.7.5a32.05 32.05 0 00-25.8 25.7l-15.8 85.4a351.86 351.86 0 00-99 57.4l-81.9-29.1a32 32 0 00-35.1 9.5l-1.8 2.1a446.02 446.02 0 00-79.7 137.9l-.9 2.6c-4.5 12.5-.8 26.5 9.3 35.2l66.3 56.6c-3.1 18.8-4.6 38-4.6 57.1 0 19.2 1.5 38.4 4.6 57.1L99 625.5a32.03 32.03 0 00-9.3 35.2l.9 2.6c18.1 50.4 44.9 96.9 79.7 137.9l1.8 2.1a32.12 32.12 0 0035.1 9.5l81.9-29.1c29.8 24.5 63.1 43.9 99 57.4l15.8 85.4a32.05 32.05 0 0025.8 25.7l2.7.5a449.4 449.4 0 00159 0l2.7-.5a32.05 32.05 0 0025.8-25.7l15.7-85a350 350 0 0099.7-57.6l81.3 28.9a32 32 0 0035.1-9.5l1.8-2.1c34.8-41.1 61.6-87.5 79.7-137.9l.9-2.6c4.5-12.3.8-26.3-9.3-35zM788.3 465.9c2.5 15.1 3.8 30.6 3.8 46.1s-1.3 31-3.8 46.1l-6.6 40.1 74.7 63.9a370.03 370.03 0 01-42.6 73.6L721 702.8l-31.4 25.8c-23.9 19.6-50.5 35-79.3 45.8l-38.1 14.3-17.9 97a377.5 377.5 0 01-85 0l-17.9-97.2-37.8-14.5c-28.5-10.8-55-26.2-78.7-45.7l-31.4-25.9-93.4 33.2c-17-22.9-31.2-47.6-42.6-73.6l75.5-64.5-6.5-40c-2.4-14.9-3.7-30.3-3.7-45.5 0-15.3 1.2-30.6 3.7-45.5l6.5-40-75.5-64.5c11.3-26.1 25.6-50.7 42.6-73.6l93.4 33.2 31.4-25.9c23.7-19.5 50.2-34.9 78.7-45.7l37.9-14.3 17.9-97.2c28.1-3.2 56.8-3.2 85 0l17.9 97 38.1 14.3c28.7 10.8 55.4 26.2 79.3 45.8l31.4 25.8 92.8-32.9c17 22.9 31.2 47.6 42.6 73.6L781.8 426l6.5 39.9zM512 326c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm79.2 255.2A111.6 111.6 0 01512 614c-29.9 0-58-11.7-79.2-32.8A111.6 111.6 0 01400 502c0-29.9 11.7-58 32.8-79.2C454 401.6 482.1 390 512 390c29.9 0 58 11.6 79.2 32.8A111.6 111.6 0 01624 502c0 29.9-11.7 58-32.8 79.2z"
/>
</svg>
</span>
</span>
</li>
<li
style="width:33.333333333333336%"
>
<span>
<span
aria-label="edit"
class="anticon anticon-edit"
role="img"
>
<svg
aria-hidden="true"
data-icon="edit"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M257.7 752c2 0 4-.2 6-.5L431.9 722c2-.4 3.9-1.3 5.3-2.8l423.9-423.9a9.96 9.96 0 000-14.1L694.9 114.9c-1.9-1.9-4.4-2.9-7.1-2.9s-5.2 1-7.1 2.9L256.8 538.8c-1.5 1.5-2.4 3.3-2.8 5.3l-29.5 168.2a33.5 33.5 0 009.4 29.8c6.6 6.4 14.9 9.9 23.8 9.9zm67.4-174.4L687.8 215l73.3 73.3-362.7 362.6-88.9 15.7 15.6-89zM880 836H144c-17.7 0-32 14.3-32 32v36c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-36c0-17.7-14.3-32-32-32z"
/>
</svg>
</span>
</span>
</li>
<li
style="width:33.333333333333336%"
>
<span>
<span
aria-label="ellipsis"
class="anticon anticon-ellipsis"
role="img"
>
<svg
aria-hidden="true"
data-icon="ellipsis"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M176 511a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0z"
/>
</svg>
</span>
</span>
</li>
</ul>
</div>,
<div
class="ant-card ant-card-bordered ant-card-small"
style="width:300px"
>
<div
class="ant-card-head"
>
<div
class="ant-card-head-wrapper"
>
<div
class="ant-card-head-title"
>
Small size card
</div>
<div
class="ant-card-extra"
>
<a
href="#"
>
More
</a>
</div>
</div>
</div>
<div
class="ant-card-body"
>
<p>
Card content
</p>
<p>
Card content
</p>
<p>
Card content
</p>
</div>
</div>,
]
`;
exports[`renders components/card/demo/flexible-content.tsx correctly 1`] = ` exports[`renders components/card/demo/flexible-content.tsx correctly 1`] = `
<div <div
class="ant-card ant-card-bordered ant-card-hoverable" class="ant-card ant-card-bordered ant-card-hoverable"
@ -770,7 +1025,6 @@ Array [
data-node-key="tab1" data-node-key="tab1"
> >
<div <div
aria-disabled="false"
aria-selected="true" aria-selected="true"
class="ant-tabs-tab-btn" class="ant-tabs-tab-btn"
role="tab" role="tab"
@ -784,7 +1038,6 @@ Array [
data-node-key="tab2" data-node-key="tab2"
> >
<div <div
aria-disabled="false"
aria-selected="false" aria-selected="false"
class="ant-tabs-tab-btn" class="ant-tabs-tab-btn"
role="tab" role="tab"
@ -889,7 +1142,6 @@ Array [
data-node-key="article" data-node-key="article"
> >
<div <div
aria-disabled="false"
aria-selected="false" aria-selected="false"
class="ant-tabs-tab-btn" class="ant-tabs-tab-btn"
role="tab" role="tab"
@ -903,7 +1155,6 @@ Array [
data-node-key="app" data-node-key="app"
> >
<div <div
aria-disabled="false"
aria-selected="true" aria-selected="true"
class="ant-tabs-tab-btn" class="ant-tabs-tab-btn"
role="tab" role="tab"
@ -917,7 +1168,6 @@ Array [
data-node-key="project" data-node-key="project"
> >
<div <div
aria-disabled="false"
aria-selected="false" aria-selected="false"
class="ant-tabs-tab-btn" class="ant-tabs-tab-btn"
role="tab" role="tab"

View File

@ -1,5 +1,268 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Card correct pass tabList props 1`] = `
<div
class="ant-card ant-card-bordered ant-card-contain-tabs"
>
<div
class="ant-card-head"
>
<div
class="ant-card-head-wrapper"
/>
<div
class="ant-tabs ant-tabs-top ant-tabs-editable ant-tabs-large ant-tabs-card ant-tabs-editable-card ant-card-head-tabs"
>
<div
class="ant-tabs-nav"
role="tablist"
>
<div
class="ant-tabs-nav-wrap"
>
<div
class="ant-tabs-nav-list"
style="transform: translate(0px, 0px);"
>
<div
class="ant-tabs-tab ant-tabs-tab-with-remove ant-tabs-tab-active"
data-node-key="basic"
>
<div
aria-controls="rc-tabs-test-panel-basic"
aria-selected="true"
class="ant-tabs-tab-btn"
id="rc-tabs-test-tab-basic"
role="tab"
tabindex="0"
>
Basic
</div>
<button
aria-label="remove"
class="ant-tabs-tab-remove"
tabindex="0"
type="button"
>
<span
aria-label="close"
class="anticon anticon-close"
role="img"
>
<svg
aria-hidden="true"
data-icon="close"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
/>
</svg>
</span>
</button>
</div>
<div
class="ant-tabs-tab ant-tabs-tab-with-remove"
data-node-key="deprecated"
>
<div
aria-controls="rc-tabs-test-panel-deprecated"
aria-selected="false"
class="ant-tabs-tab-btn"
id="rc-tabs-test-tab-deprecated"
role="tab"
tabindex="0"
>
Deprecated
</div>
<button
aria-label="remove"
class="ant-tabs-tab-remove"
tabindex="0"
type="button"
>
<span
aria-label="close"
class="anticon anticon-close"
role="img"
>
<svg
aria-hidden="true"
data-icon="close"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
/>
</svg>
</span>
</button>
</div>
<div
class="ant-tabs-tab ant-tabs-tab-disabled"
data-node-key="disabled"
>
<div
aria-controls="rc-tabs-test-panel-disabled"
aria-disabled="true"
aria-selected="false"
class="ant-tabs-tab-btn"
id="rc-tabs-test-tab-disabled"
role="tab"
>
Disabled
</div>
</div>
<div
class="ant-tabs-tab"
data-node-key="notClosable"
>
<div
aria-controls="rc-tabs-test-panel-notClosable"
aria-selected="false"
class="ant-tabs-tab-btn"
id="rc-tabs-test-tab-notClosable"
role="tab"
tabindex="0"
>
NotClosable
</div>
</div>
<button
aria-label="Add tab"
class="ant-tabs-nav-add"
type="button"
>
<span
aria-label="plus"
class="anticon anticon-plus"
role="img"
>
<svg
aria-hidden="true"
data-icon="plus"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<defs>
<style />
</defs>
<path
d="M482 152h60q8 0 8 8v704q0 8-8 8h-60q-8 0-8-8V160q0-8 8-8z"
/>
<path
d="M176 474h672q8 0 8 8v60q0 8-8 8H176q-8 0-8-8v-60q0-8 8-8z"
/>
</svg>
</span>
</button>
<div
class="ant-tabs-ink-bar ant-tabs-ink-bar-animated"
/>
</div>
</div>
<div
class="ant-tabs-nav-operations ant-tabs-nav-operations-hidden"
>
<button
aria-controls="rc-tabs-test-more-popup"
aria-expanded="false"
aria-haspopup="listbox"
aria-hidden="true"
class="ant-tabs-nav-more"
id="rc-tabs-test-more"
style="visibility: hidden; order: 1;"
tabindex="-1"
type="button"
>
<span
aria-label="ellipsis"
class="anticon anticon-ellipsis"
role="img"
>
<svg
aria-hidden="true"
data-icon="ellipsis"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M176 511a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0z"
/>
</svg>
</span>
</button>
<button
aria-label="Add tab"
class="ant-tabs-nav-add"
type="button"
>
<span
aria-label="plus"
class="anticon anticon-plus"
role="img"
>
<svg
aria-hidden="true"
data-icon="plus"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<defs>
<style />
</defs>
<path
d="M482 152h60q8 0 8 8v704q0 8-8 8h-60q-8 0-8-8V160q0-8 8-8z"
/>
<path
d="M176 474h672q8 0 8 8v60q0 8-8 8H176q-8 0-8-8v-60q0-8 8-8z"
/>
</svg>
</span>
</button>
</div>
</div>
<div
class="ant-tabs-content-holder"
>
<div
class="ant-tabs-content ant-tabs-content-top"
>
<div
aria-hidden="false"
aria-labelledby="rc-tabs-test-tab-basic"
class="ant-tabs-tabpane ant-tabs-tabpane-active"
id="rc-tabs-test-panel-basic"
role="tabpanel"
tabindex="0"
/>
</div>
</div>
</div>
</div>
<div
class="ant-card-body"
/>
</div>
`;
exports[`Card rtl render component should be rendered correctly in RTL direction 1`] = ` exports[`Card rtl render component should be rendered correctly in RTL direction 1`] = `
<div <div
class="ant-card ant-card-bordered ant-card-rtl" class="ant-card ant-card-bordered ant-card-rtl"

View File

@ -1,11 +1,11 @@
import React from 'react'; import '@testing-library/jest-dom';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import React from 'react';
import mountTest from '../../../tests/shared/mountTest'; import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest'; import rtlTest from '../../../tests/shared/rtlTest';
import { screen, render } from '../../../tests/utils'; import { render, screen } from '../../../tests/utils';
import Button from '../../button/index'; import Button from '../../button/index';
import Card from '../index'; import Card from '../index';
import '@testing-library/jest-dom';
describe('Card', () => { describe('Card', () => {
mountTest(Card); mountTest(Card);
@ -112,7 +112,7 @@ describe('Card', () => {
tab: 'tab', tab: 'tab',
}, },
]} ]}
size='small' size="small"
> >
<p>Card content</p> <p>Card content</p>
</Card>, </Card>,
@ -131,4 +131,36 @@ describe('Card', () => {
expect(cardRef.current).toHaveClass('ant-card'); expect(cardRef.current).toHaveClass('ant-card');
}); });
it('correct pass tabList props', () => {
const { container } = render(
<Card
tabList={[
{
label: 'Basic',
key: 'basic',
},
{
tab: 'Deprecated',
key: 'deprecated',
},
{
tab: 'Disabled',
key: 'disabled',
disabled: true,
},
{
tab: 'NotClosable',
key: 'notClosable',
closable: false,
},
]}
tabProps={{
type: 'editable-card',
}}
/>,
);
expect(container.firstChild).toMatchSnapshot();
});
}); });

View File

@ -0,0 +1,7 @@
## zh-CN
Component Token Debug.
## en-US
Component Token Debug.

View File

@ -0,0 +1,52 @@
import { EditOutlined, EllipsisOutlined, SettingOutlined } from '@ant-design/icons';
import { Card, ConfigProvider } from 'antd';
import React from 'react';
export default () => (
<ConfigProvider
theme={{
components: {
Card: {
headerBg: '#e6f4ff',
headerFontSize: 20,
headerFontSizeSM: 20,
headerHeight: 60,
headerHeightSM: 60,
actionsBg: '#e6f4ff',
actionsLiMargin: `2px 0`,
tabsMarginBottom: 0,
extraColor: 'rgba(0,0,0,0.25)',
},
},
}}
>
<Card
title="Card title"
actions={[
<SettingOutlined key="setting" />,
<EditOutlined key="edit" />,
<EllipsisOutlined key="ellipsis" />,
]}
extra="More"
tabList={[
{
key: 'tab1',
label: 'tab1',
},
{
key: 'tab2',
label: 'tab2',
},
]}
>
<p>Card content</p>
<p>Card content</p>
<p>Card content</p>
</Card>
<Card size="small" title="Small size card" extra={<a href="#">More</a>} style={{ width: 300 }}>
<p>Card content</p>
<p>Card content</p>
<p>Card content</p>
</Card>
</ConfigProvider>
);

View File

@ -20,15 +20,15 @@ const contentList: Record<string, React.ReactNode> = {
const tabListNoTitle = [ const tabListNoTitle = [
{ {
key: 'article', key: 'article',
tab: 'article', label: 'article',
}, },
{ {
key: 'app', key: 'app',
tab: 'app', label: 'app',
}, },
{ {
key: 'project', key: 'project',
tab: 'project', label: 'project',
}, },
]; ];

View File

@ -25,6 +25,7 @@ A card can be used to display content related to a single subject. The content c
<code src="./demo/inner.tsx">Inner card</code> <code src="./demo/inner.tsx">Inner card</code>
<code src="./demo/tabs.tsx">With tabs</code> <code src="./demo/tabs.tsx">With tabs</code>
<code src="./demo/meta.tsx">Support more content configuration</code> <code src="./demo/meta.tsx">Support more content configuration</code>
<code src="./demo/component-token.tsx" debug>Component Token</code>
## API ## API
@ -48,7 +49,7 @@ A card can be used to display content related to a single subject. The content c
| loading | Shows a loading indicator while the contents of the card are being fetched | boolean | false | | | loading | Shows a loading indicator while the contents of the card are being fetched | boolean | false | |
| size | Size of card | `default` \| `small` | `default` | | | size | Size of card | `default` \| `small` | `default` | |
| tabBarExtraContent | Extra content in tab bar | ReactNode | - | | | tabBarExtraContent | Extra content in tab bar | ReactNode | - | |
| tabList | List of TabPane's head | Array&lt;{key: string, tab: ReactNode}> | - | | | tabList | List of TabPane's head | [TabItemType](/components/tabs#tabitemtype)[] | - | |
| tabProps | [Tabs](/components/tabs/#tabs) | - | - | | | tabProps | [Tabs](/components/tabs/#tabs) | - | - | |
| title | Card title | ReactNode | - | | | title | Card title | ReactNode | - | |
| type | Card style type, can be set to `inner` or not set | string | - | | | type | Card style type, can be set to `inner` or not set | string | - | |

View File

@ -26,6 +26,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*a-8zR6rrupgAAA
<code src="./demo/inner.tsx">内部卡片</code> <code src="./demo/inner.tsx">内部卡片</code>
<code src="./demo/tabs.tsx">带页签的卡片</code> <code src="./demo/tabs.tsx">带页签的卡片</code>
<code src="./demo/meta.tsx">支持更多内容配置</code> <code src="./demo/meta.tsx">支持更多内容配置</code>
<code src="./demo/component-token.tsx" debug>组件 Token</code>
## API ## API
@ -49,7 +50,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*a-8zR6rrupgAAA
| loading | 当卡片内容还在加载中时,可以用 loading 展示一个占位 | boolean | false | | | loading | 当卡片内容还在加载中时,可以用 loading 展示一个占位 | boolean | false | |
| size | card 的尺寸 | `default` \| `small` | `default` | | | size | card 的尺寸 | `default` \| `small` | `default` | |
| tabBarExtraContent | tab bar 上额外的元素 | ReactNode | - | | | tabBarExtraContent | tab bar 上额外的元素 | ReactNode | - | |
| tabList | 页签标题列表 | Array&lt;{key: string, tab: ReactNode}> | - | | | tabList | 页签标题列表 | [TabItemType](/components/tabs#tabitemtype)[] | - | |
| tabProps | [Tabs](/components/tabs-cn#tabs) | - | - | | | tabProps | [Tabs](/components/tabs-cn#tabs) | - | - | |
| title | 卡片标题 | ReactNode | - | | | title | 卡片标题 | ReactNode | - | |
| type | 卡片类型,可设置为 `inner` 或 不设置 | string | - | | | type | 卡片类型,可设置为 `inner` 或 不设置 | string | - | |

View File

@ -1,20 +1,26 @@
import type { CSSObject } from '@ant-design/cssinjs'; import type { CSSObject } from '@ant-design/cssinjs';
import { clearFix, resetComponent, textEllipsis } from '../../style';
import type { FullToken, GenerateStyle } from '../../theme/internal'; import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal'; import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import { clearFix, resetComponent, textEllipsis } from '../../style';
export interface ComponentToken {} export interface ComponentToken {
headerBg: string;
headerFontSize: number;
headerFontSizeSM: number;
headerHeight: number;
headerHeightSM: number;
actionsBg: string;
actionsLiMargin: string;
tabsMarginBottom: number;
extraColor: string;
}
interface CardToken extends FullToken<'Card'> { interface CardToken extends FullToken<'Card'> {
cardHeadHeight: number;
cardHeadHeightSM: number;
cardShadow: string; cardShadow: string;
cardHeadPadding: number; cardHeadPadding: number;
cardPaddingSM: number; cardPaddingSM: number;
cardPaddingBase: number; cardPaddingBase: number;
cardHeadTabsMarginBottom: number;
cardActionsLiMargin: string;
cardActionsIconSize: number; cardActionsIconSize: number;
} }
@ -22,19 +28,19 @@ interface CardToken extends FullToken<'Card'> {
// ============================== Head ============================== // ============================== Head ==============================
const genCardHeadStyle: GenerateStyle<CardToken> = (token): CSSObject => { const genCardHeadStyle: GenerateStyle<CardToken> = (token): CSSObject => {
const { antCls, componentCls, cardHeadHeight, cardPaddingBase, cardHeadTabsMarginBottom } = token; const { antCls, componentCls, headerHeight, cardPaddingBase, tabsMarginBottom } = token;
return { return {
display: 'flex', display: 'flex',
justifyContent: 'center', justifyContent: 'center',
flexDirection: 'column', flexDirection: 'column',
minHeight: cardHeadHeight, minHeight: headerHeight,
marginBottom: -1, // Fix card grid overflow bug: https://gw.alipayobjects.com/zos/rmsportal/XonYxBikwpgbqIQBeuhk.png marginBottom: -1, // Fix card grid overflow bug: https://gw.alipayobjects.com/zos/rmsportal/XonYxBikwpgbqIQBeuhk.png
padding: `0 ${cardPaddingBase}px`, padding: `0 ${cardPaddingBase}px`,
color: token.colorTextHeading, color: token.colorTextHeading,
fontWeight: token.fontWeightStrong, fontWeight: token.fontWeightStrong,
fontSize: token.fontSizeLG, fontSize: token.headerFontSize,
background: 'transparent', background: token.headerBg,
borderBottom: `${token.lineWidth}px ${token.lineType} ${token.colorBorderSecondary}`, borderBottom: `${token.lineWidth}px ${token.lineType} ${token.colorBorderSecondary}`,
borderRadius: `${token.borderRadiusLG}px ${token.borderRadiusLG}px 0 0`, borderRadius: `${token.borderRadiusLG}px ${token.borderRadiusLG}px 0 0`,
@ -63,7 +69,7 @@ const genCardHeadStyle: GenerateStyle<CardToken> = (token): CSSObject => {
[`${antCls}-tabs-top`]: { [`${antCls}-tabs-top`]: {
clear: 'both', clear: 'both',
marginBottom: cardHeadTabsMarginBottom, marginBottom: tabsMarginBottom,
color: token.colorText, color: token.colorText,
fontWeight: 'normal', fontWeight: 'normal',
fontSize: token.fontSize, fontSize: token.fontSize,
@ -102,20 +108,26 @@ const genCardGridStyle: GenerateStyle<CardToken> = (token): CSSObject => {
// ============================== Actions ============================== // ============================== Actions ==============================
const genCardActionsStyle: GenerateStyle<CardToken> = (token): CSSObject => { const genCardActionsStyle: GenerateStyle<CardToken> = (token): CSSObject => {
const { componentCls, iconCls, cardActionsLiMargin, cardActionsIconSize, colorBorderSecondary } = const {
token; componentCls,
iconCls,
actionsLiMargin,
cardActionsIconSize,
colorBorderSecondary,
actionsBg,
} = token;
return { return {
margin: 0, margin: 0,
padding: 0, padding: 0,
listStyle: 'none', listStyle: 'none',
background: token.colorBgContainer, background: actionsBg,
borderTop: `${token.lineWidth}px ${token.lineType} ${colorBorderSecondary}`, borderTop: `${token.lineWidth}px ${token.lineType} ${colorBorderSecondary}`,
display: 'flex', display: 'flex',
borderRadius: `0 0 ${token.borderRadiusLG}px ${token.borderRadiusLG}px `, borderRadius: `0 0 ${token.borderRadiusLG}px ${token.borderRadiusLG}px `,
...clearFix(), ...clearFix(),
'& > li': { '& > li': {
margin: cardActionsLiMargin, margin: actionsLiMargin,
color: token.colorTextDescription, color: token.colorTextDescription,
textAlign: 'center', textAlign: 'center',
@ -231,6 +243,7 @@ const genCardStyle: GenerateStyle<CardToken> = (token): CSSObject => {
colorBorderSecondary, colorBorderSecondary,
boxShadowTertiary, boxShadowTertiary,
cardPaddingBase, cardPaddingBase,
extraColor,
} = token; } = token;
return { return {
@ -250,7 +263,7 @@ const genCardStyle: GenerateStyle<CardToken> = (token): CSSObject => {
[`${componentCls}-extra`]: { [`${componentCls}-extra`]: {
// https://stackoverflow.com/a/22429853/3040605 // https://stackoverflow.com/a/22429853/3040605
marginInlineStart: 'auto', marginInlineStart: 'auto',
color: '', color: extraColor,
fontWeight: 'normal', fontWeight: 'normal',
fontSize: token.fontSize, fontSize: token.fontSize,
}, },
@ -332,14 +345,14 @@ const genCardStyle: GenerateStyle<CardToken> = (token): CSSObject => {
// ============================== Size ============================== // ============================== Size ==============================
const genCardSizeStyle: GenerateStyle<CardToken> = (token): CSSObject => { const genCardSizeStyle: GenerateStyle<CardToken> = (token): CSSObject => {
const { componentCls, cardPaddingSM, cardHeadHeightSM } = token; const { componentCls, cardPaddingSM, headerHeightSM, headerFontSizeSM } = token;
return { return {
[`${componentCls}-small`]: { [`${componentCls}-small`]: {
[`> ${componentCls}-head`]: { [`> ${componentCls}-head`]: {
minHeight: cardHeadHeightSM, minHeight: headerHeightSM,
padding: `0 ${cardPaddingSM}px`, padding: `0 ${cardPaddingSM}px`,
fontSize: token.fontSize, fontSize: headerFontSizeSM,
[`> ${componentCls}-head-wrapper`]: { [`> ${componentCls}-head-wrapper`]: {
[`> ${componentCls}-extra`]: { [`> ${componentCls}-extra`]: {
@ -355,7 +368,7 @@ const genCardSizeStyle: GenerateStyle<CardToken> = (token): CSSObject => {
[`${componentCls}-small${componentCls}-contain-tabs`]: { [`${componentCls}-small${componentCls}-contain-tabs`]: {
[`> ${componentCls}-head`]: { [`> ${componentCls}-head`]: {
[`${componentCls}-head-title, ${componentCls}-extra`]: { [`${componentCls}-head-title, ${componentCls}-extra`]: {
minHeight: cardHeadHeightSM, minHeight: headerHeightSM,
paddingTop: 0, paddingTop: 0,
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
@ -366,24 +379,34 @@ const genCardSizeStyle: GenerateStyle<CardToken> = (token): CSSObject => {
}; };
// ============================== Export ============================== // ============================== Export ==============================
export default genComponentStyleHook('Card', (token) => { export default genComponentStyleHook(
const cardToken = mergeToken<CardToken>(token, { 'Card',
cardShadow: token.boxShadowCard, (token) => {
cardHeadHeight: token.fontSizeLG * token.lineHeightLG + token.padding * 2, const cardToken = mergeToken<CardToken>(token, {
cardHeadHeightSM: token.fontSize * token.lineHeight + token.paddingXS * 2, cardShadow: token.boxShadowCard,
cardHeadPadding: token.padding, cardHeadPadding: token.padding,
cardPaddingBase: token.paddingLG, cardPaddingBase: token.paddingLG,
cardHeadTabsMarginBottom: -token.padding - token.lineWidth, cardActionsIconSize: token.fontSize,
cardActionsLiMargin: `${token.paddingSM}px 0`, cardPaddingSM: 12, // Fixed padding.
cardActionsIconSize: token.fontSize, });
cardPaddingSM: 12, // Fixed padding.
});
return [ return [
// Style // Style
genCardStyle(cardToken), genCardStyle(cardToken),
// Size // Size
genCardSizeStyle(cardToken), genCardSizeStyle(cardToken),
]; ];
}); },
(token) => ({
headerBg: 'transparent',
headerFontSize: token.fontSizeLG,
headerFontSizeSM: token.fontSize,
headerHeight: token.fontSizeLG * token.lineHeightLG + token.padding * 2,
headerHeightSM: token.fontSize * token.lineHeight + token.paddingXS * 2,
actionsBg: token.colorBgContainer,
actionsLiMargin: `${token.paddingSM}px 0`,
tabsMarginBottom: -token.padding - token.lineWidth,
extraColor: token.colorText,
}),
);

View File

@ -468,6 +468,240 @@ exports[`renders components/carousel/demo/basic.tsx extend context correctly 1`]
</div> </div>
`; `;
exports[`renders components/carousel/demo/component-token.tsx extend context correctly 1`] = `
<div
class="ant-carousel"
>
<div
class="slick-slider slick-initialized"
dir="ltr"
>
<div
class="slick-list"
>
<div
class="slick-track"
style="opacity: 1; transform: translate3d(0px, 0px, 0px);"
>
<div
aria-hidden="true"
class="slick-slide slick-cloned"
data-index="-1"
style="width: 0px;"
tabindex="-1"
>
<div>
<div
style="width: 100%; display: inline-block;"
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
4
</h3>
</div>
</div>
</div>
<div
aria-hidden="false"
class="slick-slide slick-active slick-current"
data-index="0"
style="outline: none; width: 0px;"
tabindex="-1"
>
<div>
<div
style="width: 100%; display: inline-block;"
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
1
</h3>
</div>
</div>
</div>
<div
aria-hidden="true"
class="slick-slide"
data-index="1"
style="outline: none; width: 0px;"
tabindex="-1"
>
<div>
<div
style="width: 100%; display: inline-block;"
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
2
</h3>
</div>
</div>
</div>
<div
aria-hidden="true"
class="slick-slide"
data-index="2"
style="outline: none; width: 0px;"
tabindex="-1"
>
<div>
<div
style="width: 100%; display: inline-block;"
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
3
</h3>
</div>
</div>
</div>
<div
aria-hidden="true"
class="slick-slide"
data-index="3"
style="outline: none; width: 0px;"
tabindex="-1"
>
<div>
<div
style="width: 100%; display: inline-block;"
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
4
</h3>
</div>
</div>
</div>
<div
aria-hidden="true"
class="slick-slide slick-cloned"
data-index="4"
style="width: 0px;"
tabindex="-1"
>
<div>
<div
style="width: 100%; display: inline-block;"
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
1
</h3>
</div>
</div>
</div>
<div
aria-hidden="true"
class="slick-slide slick-cloned"
data-index="5"
style="width: 0px;"
tabindex="-1"
>
<div>
<div
style="width: 100%; display: inline-block;"
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
2
</h3>
</div>
</div>
</div>
<div
aria-hidden="true"
class="slick-slide slick-cloned"
data-index="6"
style="width: 0px;"
tabindex="-1"
>
<div>
<div
style="width: 100%; display: inline-block;"
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
3
</h3>
</div>
</div>
</div>
<div
aria-hidden="true"
class="slick-slide slick-cloned"
data-index="7"
style="width: 0px;"
tabindex="-1"
>
<div>
<div
style="width: 100%; display: inline-block;"
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
4
</h3>
</div>
</div>
</div>
</div>
</div>
<ul
class="slick-dots slick-dots-bottom"
style="display: block;"
>
<li
class="slick-active"
>
<button>
1
</button>
</li>
<li
class=""
>
<button>
2
</button>
</li>
<li
class=""
>
<button>
3
</button>
</li>
<li
class=""
>
<button>
4
</button>
</li>
</ul>
</div>
</div>
`;
exports[`renders components/carousel/demo/fade.tsx extend context correctly 1`] = ` exports[`renders components/carousel/demo/fade.tsx extend context correctly 1`] = `
<div <div
class="ant-carousel" class="ant-carousel"

View File

@ -468,6 +468,240 @@ exports[`renders components/carousel/demo/basic.tsx correctly 1`] = `
</div> </div>
`; `;
exports[`renders components/carousel/demo/component-token.tsx correctly 1`] = `
<div
class="ant-carousel"
>
<div
class="slick-slider slick-initialized"
dir="ltr"
>
<div
class="slick-list"
>
<div
class="slick-track"
style="width:900%;left:-100%"
>
<div
aria-hidden="true"
class="slick-slide slick-cloned"
data-index="-1"
style="width:11.11111111111111%"
tabindex="-1"
>
<div>
<div
style="width:100%;display:inline-block"
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
4
</h3>
</div>
</div>
</div>
<div
aria-hidden="false"
class="slick-slide slick-active slick-current"
data-index="0"
style="outline:none;width:11.11111111111111%"
tabindex="-1"
>
<div>
<div
style="width:100%;display:inline-block"
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
1
</h3>
</div>
</div>
</div>
<div
aria-hidden="true"
class="slick-slide"
data-index="1"
style="outline:none;width:11.11111111111111%"
tabindex="-1"
>
<div>
<div
style="width:100%;display:inline-block"
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
2
</h3>
</div>
</div>
</div>
<div
aria-hidden="true"
class="slick-slide"
data-index="2"
style="outline:none;width:11.11111111111111%"
tabindex="-1"
>
<div>
<div
style="width:100%;display:inline-block"
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
3
</h3>
</div>
</div>
</div>
<div
aria-hidden="true"
class="slick-slide"
data-index="3"
style="outline:none;width:11.11111111111111%"
tabindex="-1"
>
<div>
<div
style="width:100%;display:inline-block"
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
4
</h3>
</div>
</div>
</div>
<div
aria-hidden="true"
class="slick-slide slick-cloned"
data-index="4"
style="width:11.11111111111111%"
tabindex="-1"
>
<div>
<div
style="width:100%;display:inline-block"
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
1
</h3>
</div>
</div>
</div>
<div
aria-hidden="true"
class="slick-slide slick-cloned"
data-index="5"
style="width:11.11111111111111%"
tabindex="-1"
>
<div>
<div
style="width:100%;display:inline-block"
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
2
</h3>
</div>
</div>
</div>
<div
aria-hidden="true"
class="slick-slide slick-cloned"
data-index="6"
style="width:11.11111111111111%"
tabindex="-1"
>
<div>
<div
style="width:100%;display:inline-block"
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
3
</h3>
</div>
</div>
</div>
<div
aria-hidden="true"
class="slick-slide slick-cloned"
data-index="7"
style="width:11.11111111111111%"
tabindex="-1"
>
<div>
<div
style="width:100%;display:inline-block"
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
4
</h3>
</div>
</div>
</div>
</div>
</div>
<ul
class="slick-dots slick-dots-bottom"
style="display:block"
>
<li
class="slick-active"
>
<button>
1
</button>
</li>
<li
class=""
>
<button>
2
</button>
</li>
<li
class=""
>
<button>
3
</button>
</li>
<li
class=""
>
<button>
4
</button>
</li>
</ul>
</div>
</div>
`;
exports[`renders components/carousel/demo/fade.tsx correctly 1`] = ` exports[`renders components/carousel/demo/fade.tsx correctly 1`] = `
<div <div
class="ant-carousel" class="ant-carousel"

View File

@ -0,0 +1,7 @@
## zh-CN
Component Token Debug.
## en-US
Component Token Debug.

View File

@ -0,0 +1,41 @@
import { Carousel, ConfigProvider } from 'antd';
import React from 'react';
/** Test usage. Do not use in your production. */
const contentStyle: React.CSSProperties = {
margin: 0,
height: '160px',
color: '#fff',
lineHeight: '160px',
textAlign: 'center',
background: '#364d79',
};
export default () => (
<ConfigProvider
theme={{
components: {
Carousel: {
dotWidth: 50,
dotHeight: 50,
dotActiveWidth: 80,
},
},
}}
>
<Carousel>
<div>
<h3 style={contentStyle}>1</h3>
</div>
<div>
<h3 style={contentStyle}>2</h3>
</div>
<div>
<h3 style={contentStyle}>3</h3>
</div>
<div>
<h3 style={contentStyle}>4</h3>
</div>
</Carousel>
</ConfigProvider>
);

View File

@ -23,6 +23,7 @@ A carousel component. Scales with its container.
<code src="./demo/position.tsx">Position</code> <code src="./demo/position.tsx">Position</code>
<code src="./demo/autoplay.tsx">Scroll automatically</code> <code src="./demo/autoplay.tsx">Scroll automatically</code>
<code src="./demo/fade.tsx">Fade in</code> <code src="./demo/fade.tsx">Fade in</code>
<code src="./demo/component-token.tsx" debug>Component Token</code>
## API ## API

View File

@ -24,6 +24,7 @@ demo:
<code src="./demo/position.tsx">位置</code> <code src="./demo/position.tsx">位置</code>
<code src="./demo/autoplay.tsx">自动切换</code> <code src="./demo/autoplay.tsx">自动切换</code>
<code src="./demo/fade.tsx">渐显</code> <code src="./demo/fade.tsx">渐显</code>
<code src="./demo/component-token.tsx" debug>组件 Token</code>
## API ## API

View File

@ -1,11 +1,13 @@
import { resetComponent } from '../../style';
import type { FullToken, GenerateStyle } from '../../theme/internal'; import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal'; import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import { resetComponent } from '../../style';
export interface ComponentToken { export interface ComponentToken {
dotWidth: number; dotWidth: number;
dotHeight: number; dotHeight: number;
/** @deprecated Use `dotActiveWidth` instead. */
dotWidthActive: number; dotWidthActive: number;
dotActiveWidth: number;
} }
interface CarouselToken extends FullToken<'Carousel'> { interface CarouselToken extends FullToken<'Carousel'> {
@ -233,7 +235,7 @@ const genCarouselStyle: GenerateStyle<CarouselToken> = (token) => {
}, },
'&.slick-active': { '&.slick-active': {
width: token.dotWidthActive, width: token.dotActiveWidth,
'& button': { '& button': {
background: token.colorBgContainer, background: token.colorBgContainer,
@ -342,9 +344,17 @@ export default genComponentStyleHook(
genCarouselRtlStyle(carouselToken), genCarouselRtlStyle(carouselToken),
]; ];
}, },
() => {
const dotActiveWidth = 24;
return {
dotWidth: 16,
dotHeight: 3,
dotWidthActive: dotActiveWidth,
dotActiveWidth,
};
},
{ {
dotWidth: 16, deprecatedTokens: [['dotWidthActive', 'dotActiveWidth']],
dotHeight: 3,
dotWidthActive: 24,
}, },
); );

View File

@ -5,9 +5,9 @@ import { fireEvent, render } from '../../../tests/utils';
import Collapse from '../../collapse'; import Collapse from '../../collapse';
import Input from '../../input'; import Input from '../../input';
import Table from '../../table'; import Table from '../../table';
import Checkbox from '../index';
import type { CheckboxValueType } from '../Group'; import type { CheckboxValueType } from '../Group';
import type { CheckboxGroupProps } from '../index'; import type { CheckboxGroupProps } from '../index';
import Checkbox from '../index';
describe('CheckboxGroup', () => { describe('CheckboxGroup', () => {
mountTest(Checkbox.Group); mountTest(Checkbox.Group);
@ -167,13 +167,20 @@ describe('CheckboxGroup', () => {
it('should work when checkbox is wrapped by other components', () => { it('should work when checkbox is wrapped by other components', () => {
const { container } = render( const { container } = render(
<Checkbox.Group> <Checkbox.Group>
<Collapse bordered={false}> <Collapse
<Collapse.Panel key="test panel" header="test panel"> items={[
<div> {
<Checkbox value="1">item</Checkbox> key: 'test panel',
</div> label: 'test panel',
</Collapse.Panel> children: (
</Collapse> <div>
<Checkbox value="1">item</Checkbox>
</div>
),
},
]}
bordered={false}
/>
</Checkbox.Group>, </Checkbox.Group>,
); );

View File

@ -1,5 +1,6 @@
import RightOutlined from '@ant-design/icons/RightOutlined'; import RightOutlined from '@ant-design/icons/RightOutlined';
import classNames from 'classnames'; import classNames from 'classnames';
import type { CollapseProps as RcCollapseProps } from 'rc-collapse';
import RcCollapse from 'rc-collapse'; import RcCollapse from 'rc-collapse';
import type { CSSMotionProps } from 'rc-motion'; import type { CSSMotionProps } from 'rc-motion';
import toArray from 'rc-util/lib/Children/toArray'; import toArray from 'rc-util/lib/Children/toArray';
@ -20,6 +21,7 @@ type ExpandIconPositionLegacy = 'left' | 'right';
export type ExpandIconPosition = 'start' | 'end' | ExpandIconPositionLegacy | undefined; export type ExpandIconPosition = 'start' | 'end' | ExpandIconPositionLegacy | undefined;
export interface CollapseProps { export interface CollapseProps {
items: RcCollapseProps['items'];
activeKey?: Array<string | number> | string | number; activeKey?: Array<string | number> | string | number;
defaultActiveKey?: Array<string | number> | string | number; defaultActiveKey?: Array<string | number> | string | number;
/** 手风琴效果 */ /** 手风琴效果 */
@ -36,6 +38,9 @@ export interface CollapseProps {
ghost?: boolean; ghost?: boolean;
size?: SizeType; size?: SizeType;
collapsible?: CollapsibleType; collapsible?: CollapsibleType;
/**
* @deprecated use `items` instead
*/
children?: React.ReactNode; children?: React.ReactNode;
} }
@ -125,7 +130,7 @@ const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) =>
if (child.props?.disabled) { if (child.props?.disabled) {
const key = child.key ?? String(index); const key = child.key ?? String(index);
const { disabled, collapsible } = child.props; const { disabled, collapsible } = child.props;
const childProps: CollapseProps & { key: React.Key } = { const childProps: Omit<CollapseProps, 'items'> & { key: React.Key } = {
...omit(child.props, ['disabled']), ...omit(child.props, ['disabled']),
key, key,
collapsible: collapsible ?? (disabled ? 'disabled' : undefined), collapsible: collapsible ?? (disabled ? 'disabled' : undefined),

View File

@ -1,8 +1,8 @@
import classNames from 'classnames'; import classNames from 'classnames';
import RcCollapse from 'rc-collapse'; import RcCollapse from 'rc-collapse';
import * as React from 'react'; import * as React from 'react';
import { ConfigContext } from '../config-provider';
import warning from '../_util/warning'; import warning from '../_util/warning';
import { ConfigContext } from '../config-provider';
export type CollapsibleType = 'header' | 'icon' | 'disabled'; export type CollapsibleType = 'header' | 'icon' | 'disabled';
@ -21,6 +21,7 @@ export interface CollapsePanelProps {
collapsible?: CollapsibleType; collapsible?: CollapsibleType;
children?: React.ReactNode; children?: React.ReactNode;
} }
const CollapsePanel = React.forwardRef<HTMLDivElement, CollapsePanelProps>((props, ref) => { const CollapsePanel = React.forwardRef<HTMLDivElement, CollapsePanelProps>((props, ref) => {
warning( warning(
!('disabled' in props), !('disabled' in props),

View File

@ -1420,7 +1420,7 @@ exports[`renders components/collapse/demo/noarrow.tsx extend context correctly 1
</div> </div>
</div> </div>
<div <div
class="ant-collapse-item ant-collapse-no-arrow" class="ant-collapse-item"
> >
<div <div
aria-disabled="false" aria-disabled="false"

View File

@ -1339,7 +1339,7 @@ exports[`renders components/collapse/demo/noarrow.tsx correctly 1`] = `
</div> </div>
</div> </div>
<div <div
class="ant-collapse-item ant-collapse-no-arrow" class="ant-collapse-item"
> >
<div <div
aria-disabled="false" aria-disabled="false"

View File

@ -1,7 +1,6 @@
import React from 'react'; import type { CollapseProps } from 'antd';
import { Collapse } from 'antd'; import { Collapse } from 'antd';
import React from 'react';
const { Panel } = Collapse;
const text = ` const text = `
A dog is a type of domesticated animal. A dog is a type of domesticated animal.
@ -9,18 +8,24 @@ const text = `
it can be found as a welcome guest in many households across the world. it can be found as a welcome guest in many households across the world.
`; `;
const App: React.FC = () => ( const items: CollapseProps['items'] = [
<Collapse accordion> {
<Panel header="This is panel header 1" key="1"> key: '1',
<p>{text}</p> label: 'This is panel header 1',
</Panel> children: <p>{text}</p>,
<Panel header="This is panel header 2" key="2"> },
<p>{text}</p> {
</Panel> key: '2',
<Panel header="This is panel header 3" key="3"> label: 'This is panel header 2',
<p>{text}</p> children: <p>{text}</p>,
</Panel> },
</Collapse> {
); key: '3',
label: 'This is panel header 3',
children: <p>{text}</p>,
},
];
const App: React.FC = () => <Collapse accordion items={items} />;
export default App; export default App;

View File

@ -1,7 +1,6 @@
import React from 'react'; import type { CollapseProps } from 'antd';
import { Collapse } from 'antd'; import { Collapse } from 'antd';
import React from 'react';
const { Panel } = Collapse;
const text = ` const text = `
A dog is a type of domesticated animal. A dog is a type of domesticated animal.
@ -9,24 +8,30 @@ const text = `
it can be found as a welcome guest in many households across the world. it can be found as a welcome guest in many households across the world.
`; `;
const items: CollapseProps['items'] = [
{
key: '1',
label: 'This is panel header 1',
children: <p>{text}</p>,
},
{
key: '2',
label: 'This is panel header 2',
children: <p>{text}</p>,
},
{
key: '3',
label: 'This is panel header 3',
children: <p>{text}</p>,
},
];
const App: React.FC = () => { const App: React.FC = () => {
const onChange = (key: string | string[]) => { const onChange = (key: string | string[]) => {
console.log(key); console.log(key);
}; };
return ( return <Collapse items={items} defaultActiveKey={['1']} onChange={onChange} />;
<Collapse defaultActiveKey={['1']} onChange={onChange}>
<Panel header="This is panel header 1" key="1">
<p>{text}</p>
</Panel>
<Panel header="This is panel header 2" key="2">
<p>{text}</p>
</Panel>
<Panel header="This is panel header 3" key="3">
<p>{text}</p>
</Panel>
</Collapse>
);
}; };
export default App; export default App;

View File

@ -1,7 +1,6 @@
import React from 'react'; import type { CollapseProps } from 'antd';
import { Collapse } from 'antd'; import { Collapse } from 'antd';
import React from 'react';
const { Panel } = Collapse;
const text = ( const text = (
<p style={{ paddingLeft: 24 }}> <p style={{ paddingLeft: 24 }}>
@ -10,18 +9,24 @@ const text = (
</p> </p>
); );
const App: React.FC = () => ( const items: CollapseProps['items'] = [
<Collapse bordered={false} defaultActiveKey={['1']}> {
<Panel header="This is panel header 1" key="1"> key: '1',
{text} label: 'This is panel header 1',
</Panel> children: text,
<Panel header="This is panel header 2" key="2"> },
{text} {
</Panel> key: '2',
<Panel header="This is panel header 3" key="3"> label: 'This is panel header 2',
{text} children: text,
</Panel> },
</Collapse> {
); key: '3',
label: 'This is panel header 3',
children: text,
},
];
const App: React.FC = () => <Collapse items={items} bordered={false} defaultActiveKey={['1']} />;
export default App; export default App;

View File

@ -1,7 +1,5 @@
import React from 'react';
import { Collapse, Space } from 'antd'; import { Collapse, Space } from 'antd';
import React from 'react';
const { Panel } = Collapse;
const text = ` const text = `
A dog is a type of domesticated animal. A dog is a type of domesticated animal.
@ -11,21 +9,38 @@ const text = `
const App: React.FC = () => ( const App: React.FC = () => (
<Space direction="vertical"> <Space direction="vertical">
<Collapse collapsible="header" defaultActiveKey={['1']}> <Collapse
<Panel header="This panel can only be collapsed by clicking text" key="1"> collapsible="header"
<p>{text}</p> defaultActiveKey={['1']}
</Panel> items={[
</Collapse> {
<Collapse collapsible="icon" defaultActiveKey={['1']}> key: '1',
<Panel header="This panel can only be collapsed by clicking icon" key="1"> label: 'This panel can only be collapsed by clicking text',
<p>{text}</p> children: <p>{text}</p>,
</Panel> },
</Collapse> ]}
<Collapse collapsible="disabled"> />
<Panel header="This panel can't be collapsed" key="1"> <Collapse
<p>{text}</p> collapsible="icon"
</Panel> defaultActiveKey={['1']}
</Collapse> items={[
{
key: '1',
label: 'This panel can only be collapsed by clicking icon',
children: <p>{text}</p>,
},
]}
/>
<Collapse
collapsible="disabled"
items={[
{
key: '1',
label: "This panel can't be collapsed",
children: <p>{text}</p>,
},
]}
/>
</Space> </Space>
); );

View File

@ -1,8 +1,8 @@
import React from 'react';
import { CaretRightOutlined } from '@ant-design/icons'; import { CaretRightOutlined } from '@ant-design/icons';
import type { CollapseProps } from 'antd';
import { Collapse, theme } from 'antd'; import { Collapse, theme } from 'antd';
import type { CSSProperties } from 'react';
const { Panel } = Collapse; import React from 'react';
const text = ` const text = `
A dog is a type of domesticated animal. A dog is a type of domesticated animal.
@ -10,6 +10,27 @@ const text = `
it can be found as a welcome guest in many households across the world. it can be found as a welcome guest in many households across the world.
`; `;
const getItems: (panelStyle: CSSProperties) => CollapseProps['items'] = (panelStyle) => [
{
key: '1',
label: 'This is panel header 1',
children: <p>{text}</p>,
style: panelStyle,
},
{
key: '2',
label: 'This is panel header 2',
children: <p>{text}</p>,
style: panelStyle,
},
{
key: '3',
label: 'This is panel header 3',
children: <p>{text}</p>,
style: panelStyle,
},
];
const App: React.FC = () => { const App: React.FC = () => {
const { token } = theme.useToken(); const { token } = theme.useToken();
@ -26,17 +47,8 @@ const App: React.FC = () => {
defaultActiveKey={['1']} defaultActiveKey={['1']}
expandIcon={({ isActive }) => <CaretRightOutlined rotate={isActive ? 90 : 0} />} expandIcon={({ isActive }) => <CaretRightOutlined rotate={isActive ? 90 : 0} />}
style={{ background: token.colorBgContainer }} style={{ background: token.colorBgContainer }}
> items={getItems(panelStyle)}
<Panel header="This is panel header 1" key="1" style={panelStyle}> />
<p>{text}</p>
</Panel>
<Panel header="This is panel header 2" key="2" style={panelStyle}>
<p>{text}</p>
</Panel>
<Panel header="This is panel header 3" key="3" style={panelStyle}>
<p>{text}</p>
</Panel>
</Collapse>
); );
}; };

View File

@ -1,8 +1,8 @@
import React, { useState } from 'react';
import { SettingOutlined } from '@ant-design/icons'; import { SettingOutlined } from '@ant-design/icons';
import type { CollapseProps } from 'antd';
import { Collapse, Select } from 'antd'; import { Collapse, Select } from 'antd';
import React, { useState } from 'react';
const { Panel } = Collapse;
const { Option } = Select; const { Option } = Select;
const text = ` const text = `
@ -33,23 +33,35 @@ const App: React.FC = () => {
/> />
); );
const items: CollapseProps['items'] = [
{
key: '1',
label: 'This is panel header 1',
children: <div>{text}</div>,
extra: genExtra(),
},
{
key: '2',
label: 'This is panel header 2',
children: <div>{text}</div>,
extra: genExtra(),
},
{
key: '3',
label: 'This is panel header 3',
children: <div>{text}</div>,
extra: genExtra(),
},
];
return ( return (
<> <>
<Collapse <Collapse
defaultActiveKey={['1']} defaultActiveKey={['1']}
onChange={onChange} onChange={onChange}
expandIconPosition={expandIconPosition} expandIconPosition={expandIconPosition}
> items={items}
<Panel header="This is panel header 1" key="1" extra={genExtra()}> />
<div>{text}</div>
</Panel>
<Panel header="This is panel header 2" key="2" extra={genExtra()}>
<div>{text}</div>
</Panel>
<Panel header="This is panel header 3" key="3" extra={genExtra()}>
<div>{text}</div>
</Panel>
</Collapse>
<br /> <br />
<span>Expand Icon Position: </span> <span>Expand Icon Position: </span>
<Select value={expandIconPosition} style={{ margin: '0 8px' }} onChange={onPositionChange}> <Select value={expandIconPosition} style={{ margin: '0 8px' }} onChange={onPositionChange}>

View File

@ -1,7 +1,6 @@
import React from 'react'; import type { CollapseProps } from 'antd';
import { Collapse } from 'antd'; import { Collapse } from 'antd';
import React from 'react';
const { Panel } = Collapse;
const text = ` const text = `
A dog is a type of domesticated animal. A dog is a type of domesticated animal.
@ -9,18 +8,24 @@ const text = `
it can be found as a welcome guest in many households across the world. it can be found as a welcome guest in many households across the world.
`; `;
const App: React.FC = () => ( const items: CollapseProps['items'] = [
<Collapse defaultActiveKey={['1']} ghost> {
<Panel header="This is panel header 1" key="1"> key: '1',
<p>{text}</p> label: 'This is panel header 1',
</Panel> children: <p>{text}</p>,
<Panel header="This is panel header 2" key="2"> },
<p>{text}</p> {
</Panel> key: '2',
<Panel header="This is panel header 3" key="3"> label: 'This is panel header 2',
<p>{text}</p> children: <p>{text}</p>,
</Panel> },
</Collapse> {
); key: '3',
label: 'This is panel header 3',
children: <p>{text}</p>,
},
];
const App: React.FC = () => <Collapse defaultActiveKey={['1']} ghost items={items} />;
export default App; export default App;

View File

@ -1,7 +1,6 @@
import React from 'react'; import type { CollapseProps } from 'antd';
import { Collapse } from 'antd'; import { Collapse } from 'antd';
import React from 'react';
const { Panel } = Collapse;
const text = ` const text = `
A dog is a type of domesticated animal. A dog is a type of domesticated animal.
@ -9,28 +8,38 @@ const text = `
it can be found as a welcome guest in many households across the world. it can be found as a welcome guest in many households across the world.
`; `;
const itemsNest: CollapseProps['items'] = [
{
key: '1',
label: 'This is panel nest panel',
children: <p>{text}</p>,
},
];
const items: CollapseProps['items'] = [
{
key: '1',
label: 'This is panel header 1',
children: <Collapse defaultActiveKey="1" items={itemsNest} />,
},
{
key: '2',
label: 'This is panel header 2',
children: <p>{text}</p>,
},
{
key: '3',
label: 'This is panel header 3',
children: <p>{text}</p>,
},
];
const App: React.FC = () => { const App: React.FC = () => {
const onChange = (key: string | string[]) => { const onChange = (key: string | string[]) => {
console.log(key); console.log(key);
}; };
return ( return <Collapse onChange={onChange} items={items} />;
<Collapse onChange={onChange}>
<Panel header="This is panel header 1" key="1">
<Collapse defaultActiveKey="1">
<Panel header="This is panel nest panel" key="1">
<p>{text}</p>
</Panel>
</Collapse>
</Panel>
<Panel header="This is panel header 2" key="2">
<p>{text}</p>
</Panel>
<Panel header="This is panel header 3" key="3">
<p>{text}</p>
</Panel>
</Collapse>
);
}; };
export default App; export default App;

View File

@ -1,7 +1,6 @@
import React from 'react'; import type { CollapseProps } from 'antd';
import { Collapse } from 'antd'; import { Collapse } from 'antd';
import React from 'react';
const { Panel } = Collapse;
const text = ` const text = `
A dog is a type of domesticated animal. A dog is a type of domesticated animal.
@ -9,21 +8,26 @@ const text = `
it can be found as a welcome guest in many households across the world. it can be found as a welcome guest in many households across the world.
`; `;
const items: CollapseProps['items'] = [
{
key: '1',
label: 'This is panel header with arrow icon',
children: <p>{text}</p>,
},
{
key: '2',
label: 'This is panel header with no arrow icon',
children: <p>{text}</p>,
showArrow: false,
},
];
const App: React.FC = () => { const App: React.FC = () => {
const onChange = (key: string | string[]) => { const onChange = (key: string | string[]) => {
console.log(key); console.log(key);
}; };
return ( return <Collapse defaultActiveKey={['1']} onChange={onChange} items={items} />;
<Collapse defaultActiveKey={['1']} onChange={onChange}>
<Panel header="This is panel header with arrow icon" key="1">
<p>{text}</p>
</Panel>
<Panel showArrow={false} header="This is panel header with no arrow icon" key="2">
<p>{text}</p>
</Panel>
</Collapse>
);
}; };
export default App; export default App;

View File

@ -1,7 +1,5 @@
import React from 'react';
import { Collapse, Divider } from 'antd'; import { Collapse, Divider } from 'antd';
import React from 'react';
const { Panel } = Collapse;
const text = ` const text = `
A dog is a type of domesticated animal. A dog is a type of domesticated animal.
@ -12,23 +10,19 @@ const text = `
const App: React.FC = () => ( const App: React.FC = () => (
<> <>
<Divider orientation="left">Default Size</Divider> <Divider orientation="left">Default Size</Divider>
<Collapse> <Collapse
<Panel header="This is default size panel header" key="1"> items={[{ key: '1', label: 'This is default size panel header', children: <p>{text}</p> }]}
<p>{text}</p> />
</Panel>
</Collapse>
<Divider orientation="left">Small Size</Divider> <Divider orientation="left">Small Size</Divider>
<Collapse size="small"> <Collapse
<Panel header="This is small size panel header" key="1"> size="small"
<p>{text}</p> items={[{ key: '1', label: 'This is small size panel header', children: <p>{text}</p> }]}
</Panel> />
</Collapse>
<Divider orientation="left">Large Size</Divider> <Divider orientation="left">Large Size</Divider>
<Collapse size="large"> <Collapse
<Panel header="This is large size panel header" key="1"> size="large"
<p>{text}</p> items={[{ key: '1', label: 'This is large size panel header', children: <p>{text}</p> }]}
</Panel> />
</Collapse>
</> </>
); );

View File

@ -13,6 +13,49 @@ A content area which can be collapsed and expanded.
- Can be used to group or hide complex regions to keep the page clean. - Can be used to group or hide complex regions to keep the page clean.
- `Accordion` is a special kind of `Collapse`, which allows only one panel to be expanded at a time. - `Accordion` is a special kind of `Collapse`, which allows only one panel to be expanded at a time.
```tsx | pure
// works when >= 5.6.0, recommended ✅
const text = `
A dog is a type of domesticated animal.
Known for its loyalty and faithfulness,
it can be found as a welcome guest in many households across the world.
`;
const items: CollapseProps['items'] = [
{
key: '1',
label: 'This is panel header 1',
children: <p>{text}</p>,
},
{
key: '2',
label: 'This is panel header 2',
children: <p>{text}</p>,
},
{
key: '3',
label: 'This is panel header 3',
children: <p>{text}</p>,
},
];
<Collapse items={items} defaultActiveKey={['1']} />;
// works when <5.6.0 , deprecated when >=5.6.0 🙅🏻‍♀️
<Collapse defaultActiveKey={['1']} onChange={onChange}>
<Panel header="This is panel header 1" key="1">
<p>{text}</p>
</Panel>
<Panel header="This is panel header 2" key="2">
<p>{text}</p>
</Panel>
<Panel header="This is panel header 3" key="3">
<p>{text}</p>
</Panel>
</Collapse>;
```
## Examples ## Examples
<!-- prettier-ignore --> <!-- prettier-ignore -->
@ -44,9 +87,12 @@ A content area which can be collapsed and expanded.
| ghost | Make the collapse borderless and its background transparent | boolean | false | 4.4.0 | | ghost | Make the collapse borderless and its background transparent | boolean | false | 4.4.0 |
| size | Set the size of collapse | `large` \| `middle` \| `small` | `middle` | 5.2.0 | | size | Set the size of collapse | `large` \| `middle` \| `small` | `middle` | 5.2.0 |
| onChange | Callback function executed when active panel is changed | function | - | | | onChange | Callback function executed when active panel is changed | function | - | |
| items | collapse items content | [ItemType](https://github.com/react-component/collapse/blob/27250ca5415faab16db412b9bff2c131bb4f32fc/src/interface.ts#L6) | - | 5.6.0 |
### Collapse.Panel ### Collapse.Panel
<Alert message="&gt;= 5.6.0 configure the panel by `items`."></Alert>
| Property | Description | Type | Default | Version | | Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| collapsible | Specify whether the panel be collapsible or the trigger area of collapsible | `header` \| `icon` \| `disabled` | - | 4.9.0 (icon: 4.24.0) | | collapsible | Specify whether the panel be collapsible or the trigger area of collapsible | `header` \| `icon` \| `disabled` | - | 4.9.0 (icon: 4.24.0) |

View File

@ -14,6 +14,49 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*sir-TK0HkWcAAA
- 对复杂区域进行分组和隐藏,保持页面的整洁。 - 对复杂区域进行分组和隐藏,保持页面的整洁。
- `手风琴` 是一种特殊的折叠面板,只允许单个内容区域展开。 - `手风琴` 是一种特殊的折叠面板,只允许单个内容区域展开。
```tsx | pure
// >= 5.6.0 可用,推荐的写法 ✅
const text = `
A dog is a type of domesticated animal.
Known for its loyalty and faithfulness,
it can be found as a welcome guest in many households across the world.
`;
const items: CollapseProps['items'] = [
{
key: '1',
label: 'This is panel header 1',
children: <p>{text}</p>,
},
{
key: '2',
label: 'This is panel header 2',
children: <p>{text}</p>,
},
{
key: '3',
label: 'This is panel header 3',
children: <p>{text}</p>,
},
];
<Collapse items={items} defaultActiveKey={['1']} />;
// <5.6.0 可用>=5.6.0 时不推荐 🙅🏻‍♀️
<Collapse defaultActiveKey={['1']} onChange={onChange}>
<Panel header="This is panel header 1" key="1">
<p>{text}</p>
</Panel>
<Panel header="This is panel header 2" key="2">
<p>{text}</p>
</Panel>
<Panel header="This is panel header 3" key="3">
<p>{text}</p>
</Panel>
</Collapse>;
```
## 代码演示 ## 代码演示
<!-- prettier-ignore --> <!-- prettier-ignore -->
@ -45,9 +88,12 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*sir-TK0HkWcAAA
| ghost | 使折叠面板透明且无边框 | boolean | false | 4.4.0 | | ghost | 使折叠面板透明且无边框 | boolean | false | 4.4.0 |
| size | 设置折叠面板大小 | `large` \| `middle` \| `small` | `middle` | 5.2.0 | | size | 设置折叠面板大小 | `large` \| `middle` \| `small` | `middle` | 5.2.0 |
| onChange | 切换面板的回调 | function | - | | | onChange | 切换面板的回调 | function | - | |
| items | 折叠项目内容 | [ItemType](https://github.com/react-component/collapse/blob/27250ca5415faab16db412b9bff2c131bb4f32fc/src/interface.ts#L6) | - | 5.6.0 |
### Collapse.Panel ### Collapse.Panel
<Alert message="&gt;= 5.6.0 请使用 items 方式配置面板."></Alert>
| 参数 | 说明 | 类型 | 默认值 | 版本 | | 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| collapsible | 是否可折叠或指定可折叠触发区域 | `header` \| `icon` \| `disabled` | - | 4.9.0 (icon: 4.24.0) | | collapsible | 是否可折叠或指定可折叠触发区域 | `header` \| `icon` \| `disabled` | - | 4.9.0 (icon: 4.24.0) |

View File

@ -1,7 +1,7 @@
import { resetComponent, resetIcon } from '../../style';
import { genCollapseMotion } from '../../style/motion'; import { genCollapseMotion } from '../../style/motion';
import type { FullToken, GenerateStyle } from '../../theme/internal'; import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal'; import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import { resetComponent, resetIcon } from '../../style';
export interface ComponentToken {} export interface ComponentToken {}
@ -39,6 +39,7 @@ export const genBaseStyle: GenerateStyle<CollapseToken> = (token) => {
marginSM, marginSM,
paddingSM, paddingSM,
paddingLG, paddingLG,
paddingXS,
motionDurationSlow, motionDurationSlow,
fontSizeIcon, fontSizeIcon,
} = token; } = token;
@ -73,6 +74,7 @@ export const genBaseStyle: GenerateStyle<CollapseToken> = (token) => {
flexWrap: 'nowrap', flexWrap: 'nowrap',
alignItems: 'flex-start', alignItems: 'flex-start',
padding: collapseHeaderPadding, padding: collapseHeaderPadding,
paddingInlineStart: paddingSM,
color: colorTextHeading, color: colorTextHeading,
lineHeight, lineHeight,
cursor: 'pointer', cursor: 'pointer',
@ -92,6 +94,8 @@ export const genBaseStyle: GenerateStyle<CollapseToken> = (token) => {
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
paddingInlineEnd: marginSM, paddingInlineEnd: marginSM,
// Arrow offset
marginInlineStart: padding - paddingSM,
}, },
[`${componentCls}-arrow`]: { [`${componentCls}-arrow`]: {
@ -125,12 +129,6 @@ export const genBaseStyle: GenerateStyle<CollapseToken> = (token) => {
cursor: 'pointer', cursor: 'pointer',
}, },
}, },
[`&${componentCls}-no-arrow`]: {
[`> ${componentCls}-header`]: {
paddingInlineStart: paddingSM,
},
},
}, },
[`${componentCls}-content`]: { [`${componentCls}-content`]: {
@ -151,6 +149,12 @@ export const genBaseStyle: GenerateStyle<CollapseToken> = (token) => {
[`> ${componentCls}-item`]: { [`> ${componentCls}-item`]: {
[`> ${componentCls}-header`]: { [`> ${componentCls}-header`]: {
padding: collapseHeaderPaddingSM, padding: collapseHeaderPaddingSM,
paddingInlineStart: paddingXS,
[`> ${componentCls}-expand-icon`]: {
// Arrow offset
marginInlineStart: paddingSM - paddingXS,
},
}, },
[`> ${componentCls}-content > ${componentCls}-content-box`]: { [`> ${componentCls}-content > ${componentCls}-content-box`]: {
padding: paddingSM, padding: paddingSM,
@ -164,9 +168,12 @@ export const genBaseStyle: GenerateStyle<CollapseToken> = (token) => {
[`> ${componentCls}-header`]: { [`> ${componentCls}-header`]: {
padding: collapseHeaderPaddingLG, padding: collapseHeaderPaddingLG,
paddingInlineStart: padding,
[`> ${componentCls}-expand-icon`]: { [`> ${componentCls}-expand-icon`]: {
height: fontSizeLG * lineHeight, height: fontSizeLG * lineHeight,
// Arrow offset
marginInlineStart: paddingLG - padding,
}, },
}, },
[`> ${componentCls}-content > ${componentCls}-content-box`]: { [`> ${componentCls}-content > ${componentCls}-content-box`]: {

View File

@ -5,7 +5,7 @@ import type {
import classNames from 'classnames'; import classNames from 'classnames';
import useMergedState from 'rc-util/lib/hooks/useMergedState'; import useMergedState from 'rc-util/lib/hooks/useMergedState';
import type { CSSProperties } from 'react'; import type { CSSProperties } from 'react';
import React, { useContext, useEffect, useRef, useState } from 'react'; import React, { useContext, useRef, useState } from 'react';
import genPurePanel from '../_util/PurePanel'; import genPurePanel from '../_util/PurePanel';
import type { ConfigConsumerProps } from '../config-provider/context'; import type { ConfigConsumerProps } from '../config-provider/context';
import { ConfigContext } from '../config-provider/context'; import { ConfigContext } from '../config-provider/context';
@ -44,6 +44,7 @@ export interface ColorPickerProps
onOpenChange?: (open: boolean) => void; onOpenChange?: (open: boolean) => void;
onFormatChange?: (format: ColorFormat) => void; onFormatChange?: (format: ColorFormat) => void;
onChange?: (value: Color, hex: string) => void; onChange?: (value: Color, hex: string) => void;
onClear?: () => void;
getPopupContainer?: PopoverProps['getPopupContainer']; getPopupContainer?: PopoverProps['getPopupContainer'];
autoAdjustOverflow?: PopoverProps['autoAdjustOverflow']; autoAdjustOverflow?: PopoverProps['autoAdjustOverflow'];
} }
@ -71,6 +72,7 @@ const ColorPicker: CompoundedComponent = (props) => {
styles, styles,
onFormatChange, onFormatChange,
onChange, onChange,
onClear,
onOpenChange, onOpenChange,
getPopupContainer, getPopupContainer,
autoAdjustOverflow = true, autoAdjustOverflow = true,
@ -121,8 +123,9 @@ const ColorPicker: CompoundedComponent = (props) => {
onChange?.(color, color.toHexString()); onChange?.(color, color.toHexString());
}; };
const handleClear = (clear: boolean) => { const handleClear = () => {
setColorCleared(clear); setColorCleared(true);
onClear?.();
}; };
const handleChangeComplete = () => { const handleChangeComplete = () => {
@ -150,12 +153,6 @@ const ColorPicker: CompoundedComponent = (props) => {
onFormatChange, onFormatChange,
}; };
useEffect(() => {
if (colorCleared) {
setPopupOpen(false);
}
}, [colorCleared]);
return wrapSSR( return wrapSSR(
<Popover <Popover
style={styles?.popup} style={styles?.popup}

View File

@ -12,7 +12,7 @@ import type { ColorPickerBaseProps } from './interface';
interface ColorPickerPanelProps extends ColorPickerBaseProps { interface ColorPickerPanelProps extends ColorPickerBaseProps {
onChange?: (value?: Color, type?: HsbaColorType, pickColor?: boolean) => void; onChange?: (value?: Color, type?: HsbaColorType, pickColor?: boolean) => void;
onChangeComplete?: (type?: HsbaColorType) => void; onChangeComplete?: (type?: HsbaColorType) => void;
onClear?: (clear?: boolean) => void; onClear?: () => void;
} }
const ColorPickerPanel: FC<ColorPickerPanelProps> = (props) => { const ColorPickerPanel: FC<ColorPickerPanelProps> = (props) => {
@ -36,7 +36,7 @@ const ColorPickerPanel: FC<ColorPickerPanelProps> = (props) => {
value={color} value={color}
onChange={(clearColor) => { onChange={(clearColor) => {
onChange?.(clearColor); onChange?.(clearColor);
onClear?.(true); onClear?.();
}} }}
{...injectProps} {...injectProps}
/> />

View File

@ -102,16 +102,16 @@ describe('ColorPicker', () => {
expect(container.querySelector('.ant-color-picker')).toBeFalsy(); expect(container.querySelector('.ant-color-picker')).toBeFalsy();
}); });
it('Should allowClear work', async () => { it('Should allowClear and onClear work', async () => {
const { container } = render(<ColorPicker allowClear />); const onClear = jest.fn();
const { container } = render(<ColorPicker allowClear onClear={onClear} />);
fireEvent.click(container.querySelector('.ant-color-picker-trigger')!); fireEvent.click(container.querySelector('.ant-color-picker-trigger')!);
await waitFakeTimer(); await waitFakeTimer();
expect(container.querySelector('.ant-popover-hidden')).toBeFalsy();
expect(container.querySelector('.ant-color-picker-clear')).toBeTruthy(); expect(container.querySelector('.ant-color-picker-clear')).toBeTruthy();
fireEvent.click(container.querySelector('.ant-color-picker-clear')!); fireEvent.click(container.querySelector('.ant-color-picker-clear')!);
expect(onClear).toHaveBeenCalledTimes(1);
await waitFakeTimer(); await waitFakeTimer();
expect(container.querySelector('.ant-popover-hidden')).toBeTruthy();
expect( expect(
container.querySelector('.ant-color-picker-alpha-input input')?.getAttribute('value'), container.querySelector('.ant-color-picker-alpha-input input')?.getAttribute('value'),
).toEqual('0%'); ).toEqual('0%');
@ -119,12 +119,6 @@ describe('ColorPicker', () => {
container.querySelector('.ant-color-picker-trigger .ant-color-picker-clear'), container.querySelector('.ant-color-picker-trigger .ant-color-picker-clear'),
).toBeTruthy(); ).toBeTruthy();
fireEvent.click(container.querySelector('.ant-color-picker-trigger')!);
await waitFakeTimer();
expect(
container.querySelector('.ant-color-picker-alpha-input input')?.getAttribute('value'),
).toEqual('0%');
fireEvent.change(container.querySelector('.ant-color-picker-hex-input input')!, { fireEvent.change(container.querySelector('.ant-color-picker-hex-input input')!, {
target: { value: '#273B57' }, target: { value: '#273B57' },
}); });

View File

@ -3,14 +3,13 @@ import classNames from 'classnames';
import useMergedState from 'rc-util/lib/hooks/useMergedState'; import useMergedState from 'rc-util/lib/hooks/useMergedState';
import type { FC } from 'react'; import type { FC } from 'react';
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import type { CollapseProps } from '../../collapse';
import Collapse from '../../collapse'; import Collapse from '../../collapse';
import { useLocale } from '../../locale'; import { useLocale } from '../../locale';
import type { Color } from '../color'; import type { Color } from '../color';
import type { ColorPickerBaseProps, PresetsItem } from '../interface'; import type { ColorPickerBaseProps, PresetsItem } from '../interface';
import { generateColor } from '../util'; import { generateColor } from '../util';
const { Panel } = Collapse;
interface ColorPresetsProps extends Pick<ColorPickerBaseProps, 'prefixCls'> { interface ColorPresetsProps extends Pick<ColorPickerBaseProps, 'prefixCls'> {
presets: PresetsItem[]; presets: PresetsItem[];
value?: Color; value?: Color;
@ -48,36 +47,39 @@ const ColorPresets: FC<ColorPresetsProps> = ({ prefixCls, presets, value: color,
onChange?.(colorValue); onChange?.(colorValue);
}; };
const items: CollapseProps['items'] = useMemo(
() =>
presetsValue.map((preset) => ({
key: `panel-${preset.label}`,
label: <div className={`${colorPresetsPrefixCls}-label`}>{preset?.label}</div>,
children: (
<div className={`${colorPresetsPrefixCls}-items`}>
{Array.isArray(preset?.colors) && preset.colors?.length > 0 ? (
preset.colors.map((presetColor: Color) => (
<ColorBlock
key={`preset-${presetColor.toHexString()}`}
color={generateColor(presetColor).toRgbString()}
prefixCls={prefixCls}
className={classNames(`${colorPresetsPrefixCls}-color`, {
[`${colorPresetsPrefixCls}-color-checked`]:
presetColor.toHexString() === color?.toHexString(),
[`${colorPresetsPrefixCls}-color-bright`]: isBright(presetColor),
})}
onClick={() => handleClick(presetColor)}
/>
))
) : (
<span className={`${colorPresetsPrefixCls}-empty`}>{locale.presetEmpty}</span>
)}
</div>
),
})),
[],
);
return ( return (
<div className={colorPresetsPrefixCls}> <div className={colorPresetsPrefixCls}>
<Collapse defaultActiveKey={activeKeys} ghost> <Collapse defaultActiveKey={activeKeys} ghost items={items} />
{presetsValue.map((preset) => (
<Panel
header={<div className={`${colorPresetsPrefixCls}-label`}>{preset?.label}</div>}
key={`panel-${preset?.label}`}
>
<div className={`${colorPresetsPrefixCls}-items`}>
{Array.isArray(preset?.colors) && preset.colors?.length > 0 ? (
preset.colors.map((presetColor: Color) => (
<ColorBlock
key={`preset-${presetColor.toHexString()}`}
color={generateColor(presetColor).toRgbString()}
prefixCls={prefixCls}
className={classNames(`${colorPresetsPrefixCls}-color`, {
[`${colorPresetsPrefixCls}-color-checked`]:
presetColor.toHexString() === color?.toHexString(),
[`${colorPresetsPrefixCls}-color-bright`]: isBright(presetColor),
})}
onClick={() => handleClick(presetColor)}
/>
))
) : (
<span className={`${colorPresetsPrefixCls}-empty`}>{locale.presetEmpty}</span>
)}
</div>
</Panel>
))}
</Collapse>
</div> </div>
); );
}; };

View File

@ -37,19 +37,20 @@ Used when the user needs to customize the color selection.
| Property | Description | Type | Default | | Property | Description | Type | Default |
| :-- | :-- | :-- | :-- | | :-- | :-- | :-- | :-- |
| format | Format of color | `rgb` \| `hex` \| `hsb` | `hex` | | format | Format of color | `rgb` \| `hex` \| `hsb` | `hex` |
| onFormatChange | Callback when `format` is changed | `(format: 'hex' \| 'rgb' \| 'hsb') => void` | - |
| value | Value of color | string \| `Color` | - | | value | Value of color | string \| `Color` | - |
| defaultValue | Default value of color | string \| `Color` | - | | defaultValue | Default value of color | string \| `Color` | - |
| onChange | Callback when `value` is changed | `(value: Color, hex: string) => void` | - |
| allowClear | Allow clearing color selected | boolean | false | | allowClear | Allow clearing color selected | boolean | false |
| presets | Preset colors | `{ label: ReactNode, colors: Array<string \| Color> }[]` | - | | presets | Preset colors | `{ label: ReactNode, colors: Array<string \| Color> }[]` | - |
| children | Trigger of ColorPicker | React.ReactNode | - | | children | Trigger of ColorPicker | React.ReactNode | - |
| trigger | ColorPicker trigger mode | `hover` \| `click` | `click` | | trigger | ColorPicker trigger mode | `hover` \| `click` | `click` |
| open | Whether to show popup | boolean | - | | open | Whether to show popup | boolean | - |
| onOpenChange | Callback when `open` is changed | `(open: boolean) => void` | - |
| disabled | Disable ColorPicker | boolean | - | | disabled | Disable ColorPicker | boolean | - |
| placement | Placement of popup | `top` \| `topLeft` \| `topRight` \| `bottom` \| `bottomLeft` \| `bottomRight` | `bottomLeft` | | placement | Placement of popup | `top` \| `topLeft` \| `topRight` \| `bottom` \| `bottomLeft` \| `bottomRight` | `bottomLeft` |
| arrow | Configuration for popup arrow | `boolean \| { pointAtCenter: boolean }` | `true` | - | | arrow | Configuration for popup arrow | `boolean \| { pointAtCenter: boolean }` | `true` | - |
| onChange | Callback when `value` is changed | `(value: Color, hex: string) => void` | - |
| onFormatChange | Callback when `format` is changed | `(format: 'hex' \| 'rgb' \| 'hsb') => void` | - |
| onOpenChange | Callback when `open` is changed | `(open: boolean) => void` | - |
| onClear | Called when clear | `() => void` | - |
### Color ### Color

View File

@ -38,19 +38,20 @@ group:
| 参数 | 说明 | 类型 | 默认值 | | 参数 | 说明 | 类型 | 默认值 |
| :-- | :-- | :-- | :-- | | :-- | :-- | :-- | :-- |
| format | 颜色格式 | `rgb` \| `hex` \| `hsb` | `hex` | | format | 颜色格式 | `rgb` \| `hex` \| `hsb` | `hex` |
| onFormatChange | 颜色格式变化的回调 | `(format: 'hex' \| 'rgb' \| 'hsb') => void` | - |
| value | 颜色的值 | string \| `Color` | - | | value | 颜色的值 | string \| `Color` | - |
| defaultValue | 颜色默认的值 | string \| `Color` | - | | defaultValue | 颜色默认的值 | string \| `Color` | - |
| onChange | 颜色变化的回调 | `(value: Color, hex: string) => void` | - |
| allowClear | 允许清除选择的颜色 | boolean | false | | allowClear | 允许清除选择的颜色 | boolean | false |
| presets | 预设的颜色 | `{ label: ReactNode, colors: Array<string \| Color> }[]` | - | | presets | 预设的颜色 | `{ label: ReactNode, colors: Array<string \| Color> }[]` | - |
| children | 颜色选择器的触发器 | React.ReactNode | - | | children | 颜色选择器的触发器 | React.ReactNode | - |
| trigger | 颜色选择器的触发模式 | `hover` \| `click` | `click` | | trigger | 颜色选择器的触发模式 | `hover` \| `click` | `click` |
| open | 是否显示弹出窗口 | boolean | - | | open | 是否显示弹出窗口 | boolean | - |
| onOpenChange | 当 `open` 被改变时的回调 | `(open: boolean) => void` | - |
| disabled | 禁用颜色选择器 | boolean | - | | disabled | 禁用颜色选择器 | boolean | - |
| placement | 弹出窗口的位置 | `top` \| `topLeft` \| `topRight` \| `bottom` \| `bottomLeft` \| `bottomRight` | `bottomLeft` | | placement | 弹出窗口的位置 | `top` \| `topLeft` \| `topRight` \| `bottom` \| `bottomLeft` \| `bottomRight` | `bottomLeft` |
| arrow | 配置弹出的箭头 | `boolean \| { pointAtCenter: boolean }` | `true` | - | | arrow | 配置弹出的箭头 | `boolean \| { pointAtCenter: boolean }` | `true` | - |
| onChange | 颜色变化的回调 | `(value: Color, hex: string) => void` | - |
| onFormatChange | 颜色格式变化的回调 | `(format: 'hex' \| 'rgb' \| 'hsb') => void` | - |
| onOpenChange | 当 `open` 被改变时的回调 | `(open: boolean) => void` | - |
| onClear | 清除的回调 | `() => void` | - |
### Color ### Color

View File

@ -18988,7 +18988,7 @@ exports[`ConfigProvider components Pagination configProvider 1`] = `
> >
<div <div
aria-label="Page Size" aria-label="Page Size"
class="config-select config-pagination-options-size-changer config-select-single config-select-show-arrow" class="config-select config-pagination-options-size-changer config-select-single config-select-show-arrow config-select-show-search"
> >
<div <div
class="config-select-selector" class="config-select-selector"
@ -19007,11 +19007,8 @@ exports[`ConfigProvider components Pagination configProvider 1`] = `
autocomplete="off" autocomplete="off"
class="config-select-selection-search-input" class="config-select-selection-search-input"
id="rc_select_TEST_OR_SSR" id="rc_select_TEST_OR_SSR"
readonly=""
role="combobox" role="combobox"
style="opacity: 0;"
type="search" type="search"
unselectable="on"
value="" value=""
/> />
</span> </span>
@ -19134,7 +19131,7 @@ exports[`ConfigProvider components Pagination configProvider 1`] = `
> >
<div <div
aria-label="Page Size" aria-label="Page Size"
class="config-select config-select-sm config-pagination-options-size-changer config-select-single config-select-show-arrow" class="config-select config-select-sm config-pagination-options-size-changer config-select-single config-select-show-arrow config-select-show-search"
> >
<div <div
class="config-select-selector" class="config-select-selector"
@ -19153,11 +19150,8 @@ exports[`ConfigProvider components Pagination configProvider 1`] = `
autocomplete="off" autocomplete="off"
class="config-select-selection-search-input" class="config-select-selection-search-input"
id="rc_select_TEST_OR_SSR" id="rc_select_TEST_OR_SSR"
readonly=""
role="combobox" role="combobox"
style="opacity: 0;"
type="search" type="search"
unselectable="on"
value="" value=""
/> />
</span> </span>
@ -19285,7 +19279,7 @@ exports[`ConfigProvider components Pagination configProvider componentDisabled 1
> >
<div <div
aria-label="Page Size" aria-label="Page Size"
class="config-select config-pagination-options-size-changer config-select-single config-select-show-arrow config-select-disabled" class="config-select config-pagination-options-size-changer config-select-single config-select-show-arrow config-select-disabled config-select-show-search"
> >
<div <div
class="config-select-selector" class="config-select-selector"
@ -19305,11 +19299,8 @@ exports[`ConfigProvider components Pagination configProvider componentDisabled 1
class="config-select-selection-search-input" class="config-select-selection-search-input"
disabled="" disabled=""
id="rc_select_TEST_OR_SSR" id="rc_select_TEST_OR_SSR"
readonly=""
role="combobox" role="combobox"
style="opacity: 0;"
type="search" type="search"
unselectable="on"
value="" value=""
/> />
</span> </span>
@ -19432,7 +19423,7 @@ exports[`ConfigProvider components Pagination configProvider componentDisabled 1
> >
<div <div
aria-label="Page Size" aria-label="Page Size"
class="config-select config-select-sm config-pagination-options-size-changer config-select-single config-select-show-arrow config-select-disabled" class="config-select config-select-sm config-pagination-options-size-changer config-select-single config-select-show-arrow config-select-disabled config-select-show-search"
> >
<div <div
class="config-select-selector" class="config-select-selector"
@ -19452,11 +19443,8 @@ exports[`ConfigProvider components Pagination configProvider componentDisabled 1
class="config-select-selection-search-input" class="config-select-selection-search-input"
disabled="" disabled=""
id="rc_select_TEST_OR_SSR" id="rc_select_TEST_OR_SSR"
readonly=""
role="combobox" role="combobox"
style="opacity: 0;"
type="search" type="search"
unselectable="on"
value="" value=""
/> />
</span> </span>
@ -19584,7 +19572,7 @@ exports[`ConfigProvider components Pagination configProvider componentSize large
> >
<div <div
aria-label="Page Size" aria-label="Page Size"
class="config-select config-pagination-options-size-changer config-select-single config-select-show-arrow" class="config-select config-pagination-options-size-changer config-select-single config-select-show-arrow config-select-show-search"
> >
<div <div
class="config-select-selector" class="config-select-selector"
@ -19603,11 +19591,8 @@ exports[`ConfigProvider components Pagination configProvider componentSize large
autocomplete="off" autocomplete="off"
class="config-select-selection-search-input" class="config-select-selection-search-input"
id="rc_select_TEST_OR_SSR" id="rc_select_TEST_OR_SSR"
readonly=""
role="combobox" role="combobox"
style="opacity: 0;"
type="search" type="search"
unselectable="on"
value="" value=""
/> />
</span> </span>
@ -19730,7 +19715,7 @@ exports[`ConfigProvider components Pagination configProvider componentSize large
> >
<div <div
aria-label="Page Size" aria-label="Page Size"
class="config-select config-select-sm config-pagination-options-size-changer config-select-single config-select-show-arrow" class="config-select config-select-sm config-pagination-options-size-changer config-select-single config-select-show-arrow config-select-show-search"
> >
<div <div
class="config-select-selector" class="config-select-selector"
@ -19749,11 +19734,8 @@ exports[`ConfigProvider components Pagination configProvider componentSize large
autocomplete="off" autocomplete="off"
class="config-select-selection-search-input" class="config-select-selection-search-input"
id="rc_select_TEST_OR_SSR" id="rc_select_TEST_OR_SSR"
readonly=""
role="combobox" role="combobox"
style="opacity: 0;"
type="search" type="search"
unselectable="on"
value="" value=""
/> />
</span> </span>
@ -19881,7 +19863,7 @@ exports[`ConfigProvider components Pagination configProvider componentSize middl
> >
<div <div
aria-label="Page Size" aria-label="Page Size"
class="config-select config-pagination-options-size-changer config-select-single config-select-show-arrow" class="config-select config-pagination-options-size-changer config-select-single config-select-show-arrow config-select-show-search"
> >
<div <div
class="config-select-selector" class="config-select-selector"
@ -19900,11 +19882,8 @@ exports[`ConfigProvider components Pagination configProvider componentSize middl
autocomplete="off" autocomplete="off"
class="config-select-selection-search-input" class="config-select-selection-search-input"
id="rc_select_TEST_OR_SSR" id="rc_select_TEST_OR_SSR"
readonly=""
role="combobox" role="combobox"
style="opacity: 0;"
type="search" type="search"
unselectable="on"
value="" value=""
/> />
</span> </span>
@ -20027,7 +20006,7 @@ exports[`ConfigProvider components Pagination configProvider componentSize middl
> >
<div <div
aria-label="Page Size" aria-label="Page Size"
class="config-select config-select-sm config-pagination-options-size-changer config-select-single config-select-show-arrow" class="config-select config-select-sm config-pagination-options-size-changer config-select-single config-select-show-arrow config-select-show-search"
> >
<div <div
class="config-select-selector" class="config-select-selector"
@ -20046,11 +20025,8 @@ exports[`ConfigProvider components Pagination configProvider componentSize middl
autocomplete="off" autocomplete="off"
class="config-select-selection-search-input" class="config-select-selection-search-input"
id="rc_select_TEST_OR_SSR" id="rc_select_TEST_OR_SSR"
readonly=""
role="combobox" role="combobox"
style="opacity: 0;"
type="search" type="search"
unselectable="on"
value="" value=""
/> />
</span> </span>
@ -20178,7 +20154,7 @@ exports[`ConfigProvider components Pagination configProvider componentSize small
> >
<div <div
aria-label="Page Size" aria-label="Page Size"
class="config-select config-select-sm config-pagination-options-size-changer config-select-single config-select-show-arrow" class="config-select config-select-sm config-pagination-options-size-changer config-select-single config-select-show-arrow config-select-show-search"
> >
<div <div
class="config-select-selector" class="config-select-selector"
@ -20197,11 +20173,8 @@ exports[`ConfigProvider components Pagination configProvider componentSize small
autocomplete="off" autocomplete="off"
class="config-select-selection-search-input" class="config-select-selection-search-input"
id="rc_select_TEST_OR_SSR" id="rc_select_TEST_OR_SSR"
readonly=""
role="combobox" role="combobox"
style="opacity: 0;"
type="search" type="search"
unselectable="on"
value="" value=""
/> />
</span> </span>
@ -20324,7 +20297,7 @@ exports[`ConfigProvider components Pagination configProvider componentSize small
> >
<div <div
aria-label="Page Size" aria-label="Page Size"
class="config-select config-select-sm config-pagination-options-size-changer config-select-single config-select-show-arrow" class="config-select config-select-sm config-pagination-options-size-changer config-select-single config-select-show-arrow config-select-show-search"
> >
<div <div
class="config-select-selector" class="config-select-selector"
@ -20343,11 +20316,8 @@ exports[`ConfigProvider components Pagination configProvider componentSize small
autocomplete="off" autocomplete="off"
class="config-select-selection-search-input" class="config-select-selection-search-input"
id="rc_select_TEST_OR_SSR" id="rc_select_TEST_OR_SSR"
readonly=""
role="combobox" role="combobox"
style="opacity: 0;"
type="search" type="search"
unselectable="on"
value="" value=""
/> />
</span> </span>
@ -20475,7 +20445,7 @@ exports[`ConfigProvider components Pagination normal 1`] = `
> >
<div <div
aria-label="Page Size" aria-label="Page Size"
class="ant-select ant-pagination-options-size-changer ant-select-single ant-select-show-arrow" class="ant-select ant-pagination-options-size-changer ant-select-single ant-select-show-arrow ant-select-show-search"
> >
<div <div
class="ant-select-selector" class="ant-select-selector"
@ -20494,11 +20464,8 @@ exports[`ConfigProvider components Pagination normal 1`] = `
autocomplete="off" autocomplete="off"
class="ant-select-selection-search-input" class="ant-select-selection-search-input"
id="rc_select_TEST_OR_SSR" id="rc_select_TEST_OR_SSR"
readonly=""
role="combobox" role="combobox"
style="opacity: 0;"
type="search" type="search"
unselectable="on"
value="" value=""
/> />
</span> </span>
@ -20621,7 +20588,7 @@ exports[`ConfigProvider components Pagination normal 1`] = `
> >
<div <div
aria-label="Page Size" aria-label="Page Size"
class="ant-select ant-select-sm ant-pagination-options-size-changer ant-select-single ant-select-show-arrow" class="ant-select ant-select-sm ant-pagination-options-size-changer ant-select-single ant-select-show-arrow ant-select-show-search"
> >
<div <div
class="ant-select-selector" class="ant-select-selector"
@ -20640,11 +20607,8 @@ exports[`ConfigProvider components Pagination normal 1`] = `
autocomplete="off" autocomplete="off"
class="ant-select-selection-search-input" class="ant-select-selection-search-input"
id="rc_select_TEST_OR_SSR" id="rc_select_TEST_OR_SSR"
readonly=""
role="combobox" role="combobox"
style="opacity: 0;"
type="search" type="search"
unselectable="on"
value="" value=""
/> />
</span> </span>
@ -20772,7 +20736,7 @@ exports[`ConfigProvider components Pagination prefixCls 1`] = `
> >
<div <div
aria-label="Page Size" aria-label="Page Size"
class="ant-select prefix-Pagination-options-size-changer ant-select-single ant-select-show-arrow" class="ant-select prefix-Pagination-options-size-changer ant-select-single ant-select-show-arrow ant-select-show-search"
> >
<div <div
class="ant-select-selector" class="ant-select-selector"
@ -20791,11 +20755,8 @@ exports[`ConfigProvider components Pagination prefixCls 1`] = `
autocomplete="off" autocomplete="off"
class="ant-select-selection-search-input" class="ant-select-selection-search-input"
id="rc_select_TEST_OR_SSR" id="rc_select_TEST_OR_SSR"
readonly=""
role="combobox" role="combobox"
style="opacity: 0;"
type="search" type="search"
unselectable="on"
value="" value=""
/> />
</span> </span>
@ -20918,7 +20879,7 @@ exports[`ConfigProvider components Pagination prefixCls 1`] = `
> >
<div <div
aria-label="Page Size" aria-label="Page Size"
class="ant-select ant-select-sm prefix-Pagination-options-size-changer ant-select-single ant-select-show-arrow" class="ant-select ant-select-sm prefix-Pagination-options-size-changer ant-select-single ant-select-show-arrow ant-select-show-search"
> >
<div <div
class="ant-select-selector" class="ant-select-selector"
@ -20937,11 +20898,8 @@ exports[`ConfigProvider components Pagination prefixCls 1`] = `
autocomplete="off" autocomplete="off"
class="ant-select-selection-search-input" class="ant-select-selection-search-input"
id="rc_select_TEST_OR_SSR" id="rc_select_TEST_OR_SSR"
readonly=""
role="combobox" role="combobox"
style="opacity: 0;"
type="search" type="search"
unselectable="on"
value="" value=""
/> />
</span> </span>

View File

@ -0,0 +1,53 @@
import { SearchOutlined } from '@ant-design/icons';
import Button from 'antd/es/button';
import React from 'react';
import ConfigProvider from '..';
import { render } from '../../../tests/utils';
describe('ConfigProvider.button', () => {
beforeEach(() => {
(global as any).triggerProps = null;
});
it('ConfigProvider button style', () => {
const { container } = render(
<ConfigProvider>
<Button style={{ fontSize: '14px' }} />
</ConfigProvider>,
);
const item = container.querySelector('button') as HTMLElement;
expect(getComputedStyle(item)?.fontSize).toBe('14px');
});
it('ConfigProvider button className', () => {
const { container } = render(
<ConfigProvider>
<Button className="custom-class" />
</ConfigProvider>,
);
expect(container.querySelector('button')?.className.includes('custom-class')).toBe(true);
});
it('ConfigProvider button styles', () => {
const { container } = render(
<ConfigProvider button={{ styles: { icon: { color: '#333' } } }}>
<Button icon={<SearchOutlined />} />
</ConfigProvider>,
);
const item = container.querySelector('.ant-btn-icon') as HTMLElement;
expect(getComputedStyle(item)?.fontSize).toBe('14px');
});
it('ConfigProvider button classNames', () => {
const { container } = render(
<ConfigProvider button={{ classNames: { icon: 'icon-custom-class' } }}>
<Button icon={<SearchOutlined />} />
</ConfigProvider>,
);
expect(container.querySelector('.ant-btn-icon')?.className.includes('custom-class')).toBe(true);
});
});

View File

@ -6,8 +6,9 @@ import mountTest from '../../../tests/shared/mountTest';
import { fireEvent, render } from '../../../tests/utils'; import { fireEvent, render } from '../../../tests/utils';
import Button from '../../button'; import Button from '../../button';
import Input from '../../input'; import Input from '../../input';
import Table from '../../table';
import Select from '../../select'; import Select from '../../select';
import Space from '../../space';
import Table from '../../table';
describe('ConfigProvider', () => { describe('ConfigProvider', () => {
mountTest(() => ( mountTest(() => (
@ -123,4 +124,78 @@ describe('ConfigProvider', () => {
expect(rendered).toBeTruthy(); expect(rendered).toBeTruthy();
expect(cacheRenderEmpty).toBeFalsy(); expect(cacheRenderEmpty).toBeFalsy();
}); });
it('Should Space classNames works', () => {
const { container } = render(
<ConfigProvider
space={{
classNames: {
item: 'test-classNames',
},
}}
>
<Space>
<span>Text1</span>
<span>Text2</span>
</Space>
</ConfigProvider>,
);
expect(container.querySelector('.ant-space-item.test-classNames')).toBeTruthy();
});
it('Should Space className works', () => {
const { container } = render(
<ConfigProvider
space={{
className: 'test-classNames',
}}
>
<Space>
<span>Text1</span>
<span>Text2</span>
</Space>
</ConfigProvider>,
);
expect(container.querySelector('.ant-space.test-classNames')).toBeTruthy();
});
it('Should Space styles works', () => {
const { container } = render(
<ConfigProvider
space={{
styles: {
item: {
color: 'red',
},
},
}}
>
<Space>
<span>Text1</span>
<span>Text2</span>
</Space>
</ConfigProvider>,
);
expect(container.querySelector('.ant-space-item')?.getAttribute('style')).toEqual(
'margin-right: 8px; color: red;',
);
});
it('Should Space style works', () => {
const { container } = render(
<ConfigProvider
space={{
style: {
color: 'red',
},
}}
>
<Space>
<span>Text1</span>
<span>Text2</span>
</Space>
</ConfigProvider>,
);
expect(container.querySelector('.ant-space')?.getAttribute('style')).toEqual('color: red;');
});
}); });

View File

@ -9,4 +9,27 @@ describe('ConfigProvider.config', () => {
}); });
expect(globalConfig().getRootPrefixCls()).toEqual('light'); expect(globalConfig().getRootPrefixCls()).toEqual('light');
}); });
it('theme', () => {
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
expect(globalConfig().getTheme()).toBeFalsy();
ConfigProvider.config({
theme: {
infoColor: 'red',
},
});
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: ConfigProvider] `config` of css variable theme is not work in v5. Please use new `theme` config instead.',
);
ConfigProvider.config({
theme: {
token: {},
},
});
expect(globalConfig().getTheme()).toEqual({ token: {} });
});
}); });

View File

@ -49,7 +49,9 @@ describe('ConfigProvider.Theme', () => {
expect(canUseDom()).toBeFalsy(); expect(canUseDom()).toBeFalsy();
ConfigProvider.config({ ConfigProvider.config({
theme: {}, theme: {
infoColor: 'red',
},
}); });
expect(errorSpy).toHaveBeenCalledWith( expect(errorSpy).toHaveBeenCalledWith(

View File

@ -1,11 +1,13 @@
import * as React from 'react';
import type { DerivativeFunc } from '@ant-design/cssinjs'; import type { DerivativeFunc } from '@ant-design/cssinjs';
import * as React from 'react';
import type { Options } from 'scroll-into-view-if-needed'; import type { Options } from 'scroll-into-view-if-needed';
import type { ButtonProps } from '../button';
import type { RequiredMark } from '../form/Form'; import type { RequiredMark } from '../form/Form';
import type { Locale } from '../locale'; import type { Locale } from '../locale';
import type { SpaceProps } from '../space';
import type { AliasToken, MapToken, OverrideToken, SeedToken } from '../theme/interface'; import type { AliasToken, MapToken, OverrideToken, SeedToken } from '../theme/interface';
import type { RenderEmptyHandler } from './defaultRenderEmpty';
import type { SizeType } from './SizeContext'; import type { SizeType } from './SizeContext';
import type { RenderEmptyHandler } from './defaultRenderEmpty';
export const defaultIconPrefixCls = 'anticon'; export const defaultIconPrefixCls = 'anticon';
@ -34,6 +36,15 @@ export interface ThemeConfig {
inherit?: boolean; inherit?: boolean;
} }
interface componentStyleConfig {
className?: string;
style?: React.CSSProperties;
classNames?: ButtonProps['classNames'];
styles?: ButtonProps['styles'];
}
export interface ButtonConfig extends componentStyleConfig {}
export type PopupOverflow = 'viewport' | 'scroll'; export type PopupOverflow = 'viewport' | 'scroll';
export interface ConfigConsumerProps { export interface ConfigConsumerProps {
@ -58,6 +69,10 @@ export interface ConfigConsumerProps {
direction?: DirectionType; direction?: DirectionType;
space?: { space?: {
size?: SizeType | number; size?: SizeType | number;
className?: SpaceProps['className'];
classNames?: SpaceProps['classNames'];
style?: SpaceProps['style'];
styles?: SpaceProps['styles'];
}; };
virtual?: boolean; virtual?: boolean;
popupMatchSelectWidth?: boolean; popupMatchSelectWidth?: boolean;
@ -71,6 +86,7 @@ export interface ConfigConsumerProps {
select?: { select?: {
showSearch?: boolean; showSearch?: boolean;
}; };
button?: ButtonConfig;
} }
const defaultGetPrefixCls = (suffixCls?: string, customizePrefixCls?: string) => { const defaultGetPrefixCls = (suffixCls?: string, customizePrefixCls?: string) => {

View File

@ -63,21 +63,26 @@ Some components use dynamic style to support wave effect. You can config `csp` p
| iconPrefixCls | Set icon prefix className | string | `anticon` | 4.11.0 | | iconPrefixCls | Set icon prefix className | string | `anticon` | 4.11.0 |
| input | Set Input common props | { autoComplete?: string } | - | 4.2.0 | | input | Set Input common props | { autoComplete?: string } | - | 4.2.0 |
| select | Set Select common props | { showSearch?: boolean } | - | | | select | Set Select common props | { showSearch?: boolean } | - | |
| button | Set Select common props | { className?: string, style?: React.CSSProperties, classNames?: { icon: string }, styles?: { icon: React.CSSProperties } } | - | 5.6.0 |
| locale | Language package setting, you can find the packages in [antd/locale](http://unpkg.com/antd/locale/) | object | - | | | locale | Language package setting, you can find the packages in [antd/locale](http://unpkg.com/antd/locale/) | object | - | |
| prefixCls | Set prefix className | string | `ant` | | | prefixCls | Set prefix className | string | `ant` | |
| renderEmpty | Set empty content of components. Ref [Empty](/components/empty/) | function(componentName: string): ReactNode | - | | | renderEmpty | Set empty content of components. Ref [Empty](/components/empty/) | function(componentName: string): ReactNode | - | |
| space | Set Space `size`, ref [Space](/components/space) | { size: `small` \| `middle` \| `large` \| `number` } | - | 4.1.0 | | space | Set Space common props, ref [Space](/components/space) | { size: `small` \| `middle` \| `large` \| `number`, className?: string, style?: React.CSSProperties, classNames?: { item: string }, styles?: { item: React.CSSProperties } } | - | 5.6.0 |
| theme | Set theme, ref [Customize Theme](/docs/react/customize-theme) | - | - | 5.0.0 | | theme | Set theme, ref [Customize Theme](/docs/react/customize-theme) | - | - | 5.0.0 |
| virtual | Disable virtual scroll when set to `false` | boolean | - | 4.3.0 | | virtual | Disable virtual scroll when set to `false` | boolean | - | 4.3.0 |
### ConfigProvider.config() `4.13.0+` ### ConfigProvider.config()
Setting `Modal`、`Message`、`Notification` rootPrefixCls. Setting `Modal`、`Message`、`Notification` static config. Not work on hooks.
```ts ```ts
ConfigProvider.config({ ConfigProvider.config({
prefixCls: 'ant', // 4.13.0+ prefixCls: 'ant',
iconPrefixCls: 'anticon', // 4.17.0+ iconPrefixCls: 'anticon',
// 5.6.0+
// Please use hooks version first
theme: { token: { colorPrimary: 'red' } },
}); });
``` ```

View File

@ -14,9 +14,11 @@ import LocaleProvider, { ANT_MARK } from '../locale';
import type { LocaleContextProps } from '../locale/context'; import type { LocaleContextProps } from '../locale/context';
import LocaleContext from '../locale/context'; import LocaleContext from '../locale/context';
import defaultLocale from '../locale/en_US'; import defaultLocale from '../locale/en_US';
import type { SpaceProps } from '../space';
import { DesignTokenContext } from '../theme/internal'; import { DesignTokenContext } from '../theme/internal';
import defaultSeedToken from '../theme/themes/seed'; import defaultSeedToken from '../theme/themes/seed';
import type { import type {
ButtonConfig,
ConfigConsumerProps, ConfigConsumerProps,
CSPConfig, CSPConfig,
DirectionType, DirectionType,
@ -86,6 +88,7 @@ const PASSED_PROPS: Exclude<keyof ConfigConsumerProps, 'rootPrefixCls' | 'getPre
'pagination', 'pagination',
'form', 'form',
'select', 'select',
'button',
]; ];
export interface ConfigProviderProps { export interface ConfigProviderProps {
@ -121,6 +124,10 @@ export interface ConfigProviderProps {
direction?: DirectionType; direction?: DirectionType;
space?: { space?: {
size?: SizeType | number; size?: SizeType | number;
className?: SpaceProps['className'];
classNames?: SpaceProps['classNames'];
style?: SpaceProps['style'];
styles?: SpaceProps['styles'];
}; };
virtual?: boolean; virtual?: boolean;
/** @deprecated Please use `popupMatchSelectWidth` instead */ /** @deprecated Please use `popupMatchSelectWidth` instead */
@ -128,6 +135,7 @@ export interface ConfigProviderProps {
popupMatchSelectWidth?: boolean; popupMatchSelectWidth?: boolean;
popupOverflow?: PopupOverflow; popupOverflow?: PopupOverflow;
theme?: ThemeConfig; theme?: ThemeConfig;
button?: ButtonConfig;
} }
interface ProviderChildrenProps extends ConfigProviderProps { interface ProviderChildrenProps extends ConfigProviderProps {
@ -138,6 +146,7 @@ interface ProviderChildrenProps extends ConfigProviderProps {
export const defaultPrefixCls = 'ant'; export const defaultPrefixCls = 'ant';
let globalPrefixCls: string; let globalPrefixCls: string;
let globalIconPrefixCls: string; let globalIconPrefixCls: string;
let globalTheme: ThemeConfig;
function getGlobalPrefixCls() { function getGlobalPrefixCls() {
return globalPrefixCls || defaultPrefixCls; return globalPrefixCls || defaultPrefixCls;
@ -147,11 +156,15 @@ function getGlobalIconPrefixCls() {
return globalIconPrefixCls || defaultIconPrefixCls; return globalIconPrefixCls || defaultIconPrefixCls;
} }
function isLegacyTheme(theme: Theme | ThemeConfig): theme is Theme {
return Object.keys(theme).some((key) => key.endsWith('Color'));
}
const setGlobalConfig = ({ const setGlobalConfig = ({
prefixCls, prefixCls,
iconPrefixCls, iconPrefixCls,
theme, theme,
}: Pick<ConfigProviderProps, 'prefixCls' | 'iconPrefixCls'> & { theme?: Theme }) => { }: Pick<ConfigProviderProps, 'prefixCls' | 'iconPrefixCls'> & { theme?: Theme | ThemeConfig }) => {
if (prefixCls !== undefined) { if (prefixCls !== undefined) {
globalPrefixCls = prefixCls; globalPrefixCls = prefixCls;
} }
@ -160,7 +173,16 @@ const setGlobalConfig = ({
} }
if (theme) { if (theme) {
registerTheme(getGlobalPrefixCls(), theme); if (isLegacyTheme(theme)) {
warning(
false,
'ConfigProvider',
'`config` of css variable theme is not work in v5. Please use new `theme` config instead.',
);
registerTheme(getGlobalPrefixCls(), theme);
} else {
globalTheme = theme;
}
} }
}; };
@ -179,6 +201,7 @@ export const globalConfig = () => ({
// Fallback to default prefixCls // Fallback to default prefixCls
return getGlobalPrefixCls(); return getGlobalPrefixCls();
}, },
getTheme: () => globalTheme,
}); });
const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => { const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {

View File

@ -64,21 +64,26 @@ export default Demo;
| iconPrefixCls | 设置图标统一样式前缀 | string | `anticon` | 4.11.0 | | iconPrefixCls | 设置图标统一样式前缀 | string | `anticon` | 4.11.0 |
| input | 设置 Input 组件的通用属性 | { autoComplete?: string } | - | 4.2.0 | | input | 设置 Input 组件的通用属性 | { autoComplete?: string } | - | 4.2.0 |
| select | 设置 Select 组件的通用属性 | { showSearch?: boolean } | - | | | select | 设置 Select 组件的通用属性 | { showSearch?: boolean } | - | |
| button | 设置 Button 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: { icon: string }, styles?: { icon: React.CSSProperties } } | - | 5.6.0 |
| locale | 语言包配置,语言包可到 [antd/locale](http://unpkg.com/antd/locale/) 目录下寻找 | object | - | | | locale | 语言包配置,语言包可到 [antd/locale](http://unpkg.com/antd/locale/) 目录下寻找 | object | - | |
| prefixCls | 设置统一样式前缀 | string | `ant` | | | prefixCls | 设置统一样式前缀 | string | `ant` | |
| renderEmpty | 自定义组件空状态。参考 [空状态](/components/empty-cn) | function(componentName: string): ReactNode | - | | | renderEmpty | 自定义组件空状态。参考 [空状态](/components/empty-cn) | function(componentName: string): ReactNode | - | |
| space | 设置 Space 的 `size`,参考 [Space](/components/space-cn) | { size: `small` \| `middle` \| `large` \| `number` } | - | 4.1.0 | | space | 设置 Space 的通用属性,参考 [Space](/components/space-cn) | { size: `small` \| `middle` \| `large` \| `number`, className?: string, style?: React.CSSProperties, classNames?: { item: string }, styles?: { item: React.CSSProperties } } | - | 5.6.0 |
| theme | 设置主题,参考 [定制主题](/docs/react/customize-theme-cn) | - | - | 5.0.0 | | theme | 设置主题,参考 [定制主题](/docs/react/customize-theme-cn) | - | - | 5.0.0 |
| virtual | 设置 `false` 时关闭虚拟滚动 | boolean | - | 4.3.0 | | virtual | 设置 `false` 时关闭虚拟滚动 | boolean | - | 4.3.0 |
### ConfigProvider.config() `4.13.0+` ### ConfigProvider.config()
设置 `Modal`、`Message`、`Notification` rootPrefixCls 设置 `Modal`、`Message`、`Notification` 静态方法配置,只会对非 hooks 的静态方法调用生效
```ts ```ts
ConfigProvider.config({ ConfigProvider.config({
prefixCls: 'ant', // 4.13.0+ prefixCls: 'ant',
iconPrefixCls: 'anticon', // 4.17.0+ iconPrefixCls: 'anticon',
// 5.6.0+
// 请优先考虑使用 hooks 版本
theme: { token: { colorPrimary: 'red' } },
}); });
``` ```

View File

@ -1,9 +1,9 @@
import type { TriggerProps } from '@rc-component/trigger';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import 'dayjs/locale/mk'; // to test local in 'prop locale should works' test case import 'dayjs/locale/mk'; // to test local in 'prop locale should works' test case
import customParseFormat from 'dayjs/plugin/customParseFormat'; import customParseFormat from 'dayjs/plugin/customParseFormat';
import MockDate from 'mockdate'; import MockDate from 'mockdate';
import dayJsGenerateConfig from 'rc-picker/lib/generate/dayjs'; import dayJsGenerateConfig from 'rc-picker/lib/generate/dayjs';
import type { TriggerProps } from 'rc-trigger';
import React from 'react'; import React from 'react';
import DatePicker from '..'; import DatePicker from '..';
import focusTest from '../../../tests/shared/focusTest'; import focusTest from '../../../tests/shared/focusTest';
@ -295,4 +295,21 @@ describe('DatePicker', () => {
const { container } = render(<MyDatePicker />); const { container } = render(<MyDatePicker />);
expect(container.firstChild).toMatchSnapshot(); expect(container.firstChild).toMatchSnapshot();
}); });
it('kk:mm format', () => {
const { container } = render(
<DatePicker defaultValue={dayjs()} format="kk:mm" showTime open />,
);
expect(container.querySelectorAll('.ant-picker-time-panel-column').length).toBe(2);
expect(
container
.querySelectorAll('.ant-picker-time-panel-column')?.[0]
.querySelectorAll('.ant-picker-time-panel-cell').length,
).toBe(24);
expect(
container
.querySelectorAll('.ant-picker-time-panel-column')?.[1]
.querySelectorAll('.ant-picker-time-panel-cell').length,
).toBe(60);
});
}); });

View File

@ -30,7 +30,20 @@
"previousCentury": "Last century", "previousCentury": "Last century",
"nextCentury": "Next century", "nextCentury": "Next century",
"shortWeekDays": ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], "shortWeekDays": ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
"shortMonths": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] "shortMonths": [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
]
}, },
"timePickerLocale": { "timePickerLocale": {
"placeholder": "Select time" "placeholder": "Select time"

View File

@ -385,6 +385,427 @@ exports[`renders components/descriptions/demo/border.tsx extend context correctl
</div> </div>
`; `;
exports[`renders components/descriptions/demo/component-token.tsx extend context correctly 1`] = `
<div>
<div
class="ant-radio-group ant-radio-group-outline"
>
<label
class="ant-radio-wrapper ant-radio-wrapper-checked"
>
<span
class="ant-radio ant-radio-checked"
>
<input
checked=""
class="ant-radio-input"
type="radio"
value="default"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
default
</span>
</label>
<label
class="ant-radio-wrapper"
>
<span
class="ant-radio"
>
<input
class="ant-radio-input"
type="radio"
value="middle"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
middle
</span>
</label>
<label
class="ant-radio-wrapper"
>
<span
class="ant-radio"
>
<input
class="ant-radio-input"
type="radio"
value="small"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
small
</span>
</label>
</div>
<br />
<br />
<div
class="ant-descriptions ant-descriptions-bordered"
>
<div
class="ant-descriptions-header"
>
<div
class="ant-descriptions-title"
>
Custom Size
</div>
<div
class="ant-descriptions-extra"
>
<div>
extra color: blue
</div>
</div>
</div>
<div
class="ant-descriptions-view"
>
<table>
<tbody>
<tr
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label"
colspan="1"
>
<span>
Product
</span>
</th>
<td
class="ant-descriptions-item-content"
colspan="1"
>
<span>
Cloud Database
</span>
</td>
</tr>
<tr
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label"
colspan="1"
>
<span>
Billing
</span>
</th>
<td
class="ant-descriptions-item-content"
colspan="1"
>
<span>
Prepaid
</span>
</td>
</tr>
<tr
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label"
colspan="1"
>
<span>
time
</span>
</th>
<td
class="ant-descriptions-item-content"
colspan="1"
>
<span>
18:00:00
</span>
</td>
</tr>
<tr
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label"
colspan="1"
>
<span>
Amount
</span>
</th>
<td
class="ant-descriptions-item-content"
colspan="1"
>
<span>
$80.00
</span>
</td>
</tr>
<tr
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label"
colspan="1"
>
<span>
Discount
</span>
</th>
<td
class="ant-descriptions-item-content"
colspan="1"
>
<span>
$20.00
</span>
</td>
</tr>
<tr
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label"
colspan="1"
>
<span>
Official
</span>
</th>
<td
class="ant-descriptions-item-content"
colspan="1"
>
<span>
$60.00
</span>
</td>
</tr>
<tr
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label"
colspan="1"
>
<span>
Config Info
</span>
</th>
<td
class="ant-descriptions-item-content"
colspan="1"
>
<span>
Data disk type: MongoDB
<br />
Database version: 3.4
<br />
Package: dds.mongo.mid
<br />
Storage space: 10 GB
<br />
Replication factor: 3
<br />
Region: East China 1
<br />
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<br />
<br />
<div
class="ant-descriptions"
>
<div
class="ant-descriptions-header"
>
<div
class="ant-descriptions-title"
>
Custom Size
</div>
<div
class="ant-descriptions-extra"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Edit
</span>
</button>
</div>
</div>
<div
class="ant-descriptions-view"
>
<table>
<tbody>
<tr
class="ant-descriptions-row"
>
<td
class="ant-descriptions-item"
colspan="1"
>
<div
class="ant-descriptions-item-container"
>
<span
class="ant-descriptions-item-label"
>
Product
</span>
<span
class="ant-descriptions-item-content"
>
Cloud Database
</span>
</div>
</td>
</tr>
<tr
class="ant-descriptions-row"
>
<td
class="ant-descriptions-item"
colspan="1"
>
<div
class="ant-descriptions-item-container"
>
<span
class="ant-descriptions-item-label"
>
Billing
</span>
<span
class="ant-descriptions-item-content"
>
Prepaid
</span>
</div>
</td>
</tr>
<tr
class="ant-descriptions-row"
>
<td
class="ant-descriptions-item"
colspan="1"
>
<div
class="ant-descriptions-item-container"
>
<span
class="ant-descriptions-item-label"
>
time
</span>
<span
class="ant-descriptions-item-content"
>
18:00:00
</span>
</div>
</td>
</tr>
<tr
class="ant-descriptions-row"
>
<td
class="ant-descriptions-item"
colspan="1"
>
<div
class="ant-descriptions-item-container"
>
<span
class="ant-descriptions-item-label"
>
Amount
</span>
<span
class="ant-descriptions-item-content"
>
$80.00
</span>
</div>
</td>
</tr>
<tr
class="ant-descriptions-row"
>
<td
class="ant-descriptions-item"
colspan="1"
>
<div
class="ant-descriptions-item-container"
>
<span
class="ant-descriptions-item-label"
>
Discount
</span>
<span
class="ant-descriptions-item-content"
>
$20.00
</span>
</div>
</td>
</tr>
<tr
class="ant-descriptions-row"
>
<td
class="ant-descriptions-item"
colspan="1"
>
<div
class="ant-descriptions-item-container"
>
<span
class="ant-descriptions-item-label"
>
Official
</span>
<span
class="ant-descriptions-item-content"
>
$60.00
</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
`;
exports[`renders components/descriptions/demo/responsive.tsx extend context correctly 1`] = ` exports[`renders components/descriptions/demo/responsive.tsx extend context correctly 1`] = `
<div> <div>
<div <div

View File

@ -353,6 +353,395 @@ exports[`renders components/descriptions/demo/border.tsx correctly 1`] = `
</div> </div>
`; `;
exports[`renders components/descriptions/demo/component-token.tsx correctly 1`] = `
<div>
<div
class="ant-radio-group ant-radio-group-outline"
>
<label
class="ant-radio-wrapper ant-radio-wrapper-checked"
>
<span
class="ant-radio ant-radio-checked"
>
<input
checked=""
class="ant-radio-input"
type="radio"
value="default"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
default
</span>
</label>
<label
class="ant-radio-wrapper"
>
<span
class="ant-radio"
>
<input
class="ant-radio-input"
type="radio"
value="middle"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
middle
</span>
</label>
<label
class="ant-radio-wrapper"
>
<span
class="ant-radio"
>
<input
class="ant-radio-input"
type="radio"
value="small"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
small
</span>
</label>
</div>
<br />
<br />
<div
class="ant-descriptions ant-descriptions-bordered"
>
<div
class="ant-descriptions-header"
>
<div
class="ant-descriptions-title"
>
Custom Size
</div>
<div
class="ant-descriptions-extra"
>
<div>
extra color: blue
</div>
</div>
</div>
<div
class="ant-descriptions-view"
>
<table>
<tbody>
<tr
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label"
colspan="1"
>
<span>
Product
</span>
</th>
<td
class="ant-descriptions-item-content"
colspan="1"
>
<span>
Cloud Database
</span>
</td>
<th
class="ant-descriptions-item-label"
colspan="1"
>
<span>
Billing
</span>
</th>
<td
class="ant-descriptions-item-content"
colspan="1"
>
<span>
Prepaid
</span>
</td>
<th
class="ant-descriptions-item-label"
colspan="1"
>
<span>
time
</span>
</th>
<td
class="ant-descriptions-item-content"
colspan="1"
>
<span>
18:00:00
</span>
</td>
</tr>
<tr
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label"
colspan="1"
>
<span>
Amount
</span>
</th>
<td
class="ant-descriptions-item-content"
colspan="1"
>
<span>
$80.00
</span>
</td>
<th
class="ant-descriptions-item-label"
colspan="1"
>
<span>
Discount
</span>
</th>
<td
class="ant-descriptions-item-content"
colspan="1"
>
<span>
$20.00
</span>
</td>
<th
class="ant-descriptions-item-label"
colspan="1"
>
<span>
Official
</span>
</th>
<td
class="ant-descriptions-item-content"
colspan="1"
>
<span>
$60.00
</span>
</td>
</tr>
<tr
class="ant-descriptions-row"
>
<th
class="ant-descriptions-item-label"
colspan="1"
>
<span>
Config Info
</span>
</th>
<td
class="ant-descriptions-item-content"
colspan="5"
>
<span>
Data disk type: MongoDB
<br />
Database version: 3.4
<br />
Package: dds.mongo.mid
<br />
Storage space: 10 GB
<br />
Replication factor: 3
<br />
Region: East China 1
<br />
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<br />
<br />
<div
class="ant-descriptions"
>
<div
class="ant-descriptions-header"
>
<div
class="ant-descriptions-title"
>
Custom Size
</div>
<div
class="ant-descriptions-extra"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Edit
</span>
</button>
</div>
</div>
<div
class="ant-descriptions-view"
>
<table>
<tbody>
<tr
class="ant-descriptions-row"
>
<td
class="ant-descriptions-item"
colspan="1"
>
<div
class="ant-descriptions-item-container"
>
<span
class="ant-descriptions-item-label"
>
Product
</span>
<span
class="ant-descriptions-item-content"
>
Cloud Database
</span>
</div>
</td>
<td
class="ant-descriptions-item"
colspan="1"
>
<div
class="ant-descriptions-item-container"
>
<span
class="ant-descriptions-item-label"
>
Billing
</span>
<span
class="ant-descriptions-item-content"
>
Prepaid
</span>
</div>
</td>
<td
class="ant-descriptions-item"
colspan="1"
>
<div
class="ant-descriptions-item-container"
>
<span
class="ant-descriptions-item-label"
>
time
</span>
<span
class="ant-descriptions-item-content"
>
18:00:00
</span>
</div>
</td>
</tr>
<tr
class="ant-descriptions-row"
>
<td
class="ant-descriptions-item"
colspan="1"
>
<div
class="ant-descriptions-item-container"
>
<span
class="ant-descriptions-item-label"
>
Amount
</span>
<span
class="ant-descriptions-item-content"
>
$80.00
</span>
</div>
</td>
<td
class="ant-descriptions-item"
colspan="1"
>
<div
class="ant-descriptions-item-container"
>
<span
class="ant-descriptions-item-label"
>
Discount
</span>
<span
class="ant-descriptions-item-content"
>
$20.00
</span>
</div>
</td>
<td
class="ant-descriptions-item"
colspan="1"
>
<div
class="ant-descriptions-item-container"
>
<span
class="ant-descriptions-item-label"
>
Official
</span>
<span
class="ant-descriptions-item-content"
>
$60.00
</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
`;
exports[`renders components/descriptions/demo/responsive.tsx correctly 1`] = ` exports[`renders components/descriptions/demo/responsive.tsx correctly 1`] = `
<div> <div>
<div <div

View File

@ -0,0 +1,7 @@
## zh-CN
Component Token Debug.
## en-US
Component Token Debug.

View File

@ -0,0 +1,73 @@
import type { RadioChangeEvent } from 'antd';
import { Button, ConfigProvider, Descriptions, Radio } from 'antd';
import React, { useState } from 'react';
const App: React.FC = () => {
const [size, setSize] = useState<'default' | 'middle' | 'small'>('default');
const onChange = (e: RadioChangeEvent) => {
console.log('size checked', e.target.value);
setSize(e.target.value);
};
return (
<ConfigProvider
theme={{
components: {
Descriptions: {
labelBg: 'red',
titleMarginBottom: 2,
itemPaddingBottom: 8,
colonMarginRight: 10,
colonMarginLeft: 20,
extraColor: 'blue',
},
},
}}
>
<div>
<Radio.Group onChange={onChange} value={size}>
<Radio value="default">default</Radio>
<Radio value="middle">middle</Radio>
<Radio value="small">small</Radio>
</Radio.Group>
<br />
<br />
<Descriptions bordered title="Custom Size" size={size} extra={<div>extra color: blue</div>}>
<Descriptions.Item label="Product">Cloud Database</Descriptions.Item>
<Descriptions.Item label="Billing">Prepaid</Descriptions.Item>
<Descriptions.Item label="time">18:00:00</Descriptions.Item>
<Descriptions.Item label="Amount">$80.00</Descriptions.Item>
<Descriptions.Item label="Discount">$20.00</Descriptions.Item>
<Descriptions.Item label="Official">$60.00</Descriptions.Item>
<Descriptions.Item label="Config Info">
Data disk type: MongoDB
<br />
Database version: 3.4
<br />
Package: dds.mongo.mid
<br />
Storage space: 10 GB
<br />
Replication factor: 3
<br />
Region: East China 1
<br />
</Descriptions.Item>
</Descriptions>
<br />
<br />
<Descriptions title="Custom Size" size={size} extra={<Button type="primary">Edit</Button>}>
<Descriptions.Item label="Product">Cloud Database</Descriptions.Item>
<Descriptions.Item label="Billing">Prepaid</Descriptions.Item>
<Descriptions.Item label="time">18:00:00</Descriptions.Item>
<Descriptions.Item label="Amount">$80.00</Descriptions.Item>
<Descriptions.Item label="Discount">$20.00</Descriptions.Item>
<Descriptions.Item label="Official">$60.00</Descriptions.Item>
</Descriptions>
</div>
</ConfigProvider>
);
};
export default App;

View File

@ -23,6 +23,7 @@ Commonly displayed on the details page.
<code src="./demo/vertical.tsx">Vertical</code> <code src="./demo/vertical.tsx">Vertical</code>
<code src="./demo/vertical-border.tsx">Vertical border</code> <code src="./demo/vertical-border.tsx">Vertical border</code>
<code src="./demo/style.tsx" debug>Customize label & wrapper style</code> <code src="./demo/style.tsx" debug>Customize label & wrapper style</code>
<code src="./demo/component-token.tsx" debug>Component Token</code>
## API ## API

View File

@ -24,6 +24,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*d27AQJrowGAAAA
<code src="./demo/vertical.tsx">垂直</code> <code src="./demo/vertical.tsx">垂直</code>
<code src="./demo/vertical-border.tsx">垂直带边框的</code> <code src="./demo/vertical-border.tsx">垂直带边框的</code>
<code src="./demo/style.tsx" debug>自定义 label & wrapper 样式</code> <code src="./demo/style.tsx" debug>自定义 label & wrapper 样式</code>
<code src="./demo/component-token.tsx" debug>组件 Token</code>
## API ## API

View File

@ -1,28 +1,23 @@
import type { CSSObject } from '@ant-design/cssinjs'; import type { CSSObject } from '@ant-design/cssinjs';
import { resetComponent, textEllipsis } from '../../style';
import type { FullToken, GenerateStyle } from '../../theme/internal'; import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal'; import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import { resetComponent, textEllipsis } from '../../style';
interface DescriptionsToken extends FullToken<'Descriptions'> { /** Component only token. Which will handle additional calculation of alias token */
descriptionsTitleMarginBottom: number; export interface ComponentToken {
descriptionsExtraColor: string; // Component token here
descriptionItemPaddingBottom: number; labelBg: string;
descriptionsDefaultPadding: string; titleMarginBottom: number;
descriptionsBg: string; itemPaddingBottom: number;
descriptionsMiddlePadding: string; colonMarginRight: number;
descriptionsSmallPadding: string; colonMarginLeft: number;
descriptionsItemLabelColonMarginRight: number; extraColor: string;
descriptionsItemLabelColonMarginLeft: number;
} }
interface DescriptionsToken extends FullToken<'Descriptions'> {}
const genBorderedStyle = (token: DescriptionsToken): CSSObject => { const genBorderedStyle = (token: DescriptionsToken): CSSObject => {
const { const { componentCls, labelBg } = token;
componentCls,
descriptionsSmallPadding,
descriptionsDefaultPadding,
descriptionsMiddlePadding,
descriptionsBg,
} = token;
return { return {
[`&${componentCls}-bordered`]: { [`&${componentCls}-bordered`]: {
[`${componentCls}-view`]: { [`${componentCls}-view`]: {
@ -33,7 +28,7 @@ const genBorderedStyle = (token: DescriptionsToken): CSSObject => {
}, },
}, },
[`${componentCls}-item-label, ${componentCls}-item-content`]: { [`${componentCls}-item-label, ${componentCls}-item-content`]: {
padding: descriptionsDefaultPadding, padding: `${token.padding}px ${token.paddingLG}px`,
borderInlineEnd: `${token.lineWidth}px ${token.lineType} ${token.colorSplit}`, borderInlineEnd: `${token.lineWidth}px ${token.lineType} ${token.colorSplit}`,
'&:last-child': { '&:last-child': {
borderInlineEnd: 'none', borderInlineEnd: 'none',
@ -41,7 +36,7 @@ const genBorderedStyle = (token: DescriptionsToken): CSSObject => {
}, },
[`${componentCls}-item-label`]: { [`${componentCls}-item-label`]: {
color: token.colorTextSecondary, color: token.colorTextSecondary,
backgroundColor: descriptionsBg, backgroundColor: labelBg,
'&::after': { '&::after': {
display: 'none', display: 'none',
}, },
@ -54,26 +49,26 @@ const genBorderedStyle = (token: DescriptionsToken): CSSObject => {
}, },
[`&${componentCls}-middle`]: { [`&${componentCls}-middle`]: {
[`${componentCls}-item-label, ${componentCls}-item-content`]: { [`${componentCls}-item-label, ${componentCls}-item-content`]: {
padding: descriptionsMiddlePadding, padding: `${token.paddingSM}px ${token.paddingLG}px`,
}, },
}, },
[`&${componentCls}-small`]: { [`&${componentCls}-small`]: {
[`${componentCls}-item-label, ${componentCls}-item-content`]: { [`${componentCls}-item-label, ${componentCls}-item-content`]: {
padding: descriptionsSmallPadding, padding: `${token.paddingXS}px ${token.padding}px`,
}, },
}, },
}, },
}; };
}; };
const genDescriptionStyles: GenerateStyle<DescriptionsToken> = (token: DescriptionsToken) => { const genDescriptionStyles: GenerateStyle<DescriptionsToken> = (token) => {
const { const {
componentCls, componentCls,
descriptionsExtraColor, extraColor,
descriptionItemPaddingBottom, itemPaddingBottom,
descriptionsItemLabelColonMarginRight, colonMarginRight,
descriptionsItemLabelColonMarginLeft, colonMarginLeft,
descriptionsTitleMarginBottom, titleMarginBottom,
} = token; } = token;
return { return {
[componentCls]: { [componentCls]: {
@ -85,7 +80,7 @@ const genDescriptionStyles: GenerateStyle<DescriptionsToken> = (token: Descripti
[`${componentCls}-header`]: { [`${componentCls}-header`]: {
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
marginBottom: descriptionsTitleMarginBottom, marginBottom: titleMarginBottom,
}, },
[`${componentCls}-title`]: { [`${componentCls}-title`]: {
...textEllipsis, ...textEllipsis,
@ -97,7 +92,7 @@ const genDescriptionStyles: GenerateStyle<DescriptionsToken> = (token: Descripti
}, },
[`${componentCls}-extra`]: { [`${componentCls}-extra`]: {
marginInlineStart: 'auto', marginInlineStart: 'auto',
color: descriptionsExtraColor, color: extraColor,
fontSize: token.fontSize, fontSize: token.fontSize,
}, },
[`${componentCls}-view`]: { [`${componentCls}-view`]: {
@ -110,7 +105,7 @@ const genDescriptionStyles: GenerateStyle<DescriptionsToken> = (token: Descripti
}, },
[`${componentCls}-row`]: { [`${componentCls}-row`]: {
'> th, > td': { '> th, > td': {
paddingBottom: descriptionItemPaddingBottom, paddingBottom: itemPaddingBottom,
}, },
'&:last-child': { '&:last-child': {
borderBottom: 'none', borderBottom: 'none',
@ -127,7 +122,7 @@ const genDescriptionStyles: GenerateStyle<DescriptionsToken> = (token: Descripti
content: '":"', content: '":"',
position: 'relative', position: 'relative',
top: -0.5, // magic for position top: -0.5, // magic for position
marginInline: `${descriptionsItemLabelColonMarginLeft}px ${descriptionsItemLabelColonMarginRight}px`, marginInline: `${colonMarginLeft}px ${colonMarginRight}px`,
}, },
[`&${componentCls}-item-no-colon::after`]: { [`&${componentCls}-item-no-colon::after`]: {
@ -182,28 +177,18 @@ const genDescriptionStyles: GenerateStyle<DescriptionsToken> = (token: Descripti
}; };
}; };
// ============================== Export ============================== // ============================== Export ==============================
export default genComponentStyleHook('Descriptions', (token) => { export default genComponentStyleHook(
const descriptionsBg = token.colorFillAlter; 'Descriptions',
const descriptionsTitleMarginBottom = token.fontSizeSM * token.lineHeightSM; (token) => {
const descriptionsExtraColor = token.colorText; const descriptionToken = mergeToken<DescriptionsToken>(token, {});
const descriptionsSmallPadding = `${token.paddingXS}px ${token.padding}px`; return [genDescriptionStyles(descriptionToken)];
const descriptionsDefaultPadding = `${token.padding}px ${token.paddingLG}px`; },
const descriptionsMiddlePadding = `${token.paddingSM}px ${token.paddingLG}px`; (token) => ({
const descriptionItemPaddingBottom = token.padding; labelBg: token.colorFillAlter,
const descriptionsItemLabelColonMarginRight = token.marginXS; titleMarginBottom: token.fontSizeSM * token.lineHeightSM,
const descriptionsItemLabelColonMarginLeft = token.marginXXS / 2; itemPaddingBottom: token.padding,
colonMarginRight: token.marginXS,
const descriptionToken = mergeToken<DescriptionsToken>(token, { colonMarginLeft: token.marginXXS / 2,
descriptionsBg, extraColor: token.colorText,
descriptionsTitleMarginBottom, }),
descriptionsExtraColor, );
descriptionItemPaddingBottom,
descriptionsSmallPadding,
descriptionsDefaultPadding,
descriptionsMiddlePadding,
descriptionsItemLabelColonMarginRight,
descriptionsItemLabelColonMarginLeft,
});
return [genDescriptionStyles(descriptionToken)];
});

View File

@ -1,7 +1,7 @@
import classNames from 'classnames'; import classNames from 'classnames';
import * as React from 'react'; import * as React from 'react';
import { ConfigContext } from '../config-provider';
import warning from '../_util/warning'; import warning from '../_util/warning';
import { ConfigContext } from '../config-provider';
import useStyle from './style'; import useStyle from './style';

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