Browse Source

Merge branch 'feature' into affix-r

affix-r
lijianan 1 year ago
committed by GitHub
parent
commit
5bfc7297d0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      .dumi/theme/builtins/ColorChunk/index.tsx
  2. 70
      .dumi/theme/builtins/ComponentTokenTable/index.tsx
  3. 5
      .dumi/theme/builtins/Previewer/index.tsx
  4. 2
      .dumi/theme/builtins/TokenTable/index.tsx
  5. 11
      .dumi/theme/common/Link.tsx
  6. 2
      .dumi/theme/common/ThemeSwitch/index.tsx
  7. 46
      CHANGELOG.en-US.md
  8. 46
      CHANGELOG.zh-CN.md
  9. 10
      components/__tests__/blog.test.ts
  10. 12
      components/_util/ActionButton.tsx
  11. 3
      components/_util/reactNode.ts
  12. 11
      components/_util/styleChecker.ts
  13. 2
      components/_util/type.ts
  14. 2
      components/affix/index.tsx
  15. 67
      components/alert/Alert.tsx
  16. 4
      components/alert/__tests__/__snapshots__/demo-extend.test.ts.snap
  17. 4
      components/alert/__tests__/__snapshots__/demo.test.ts.snap
  18. 4
      components/alert/demo/action.tsx
  19. 4
      components/alert/demo/closable.tsx
  20. 2
      components/alert/index.ts
  21. 2
      components/anchor/Anchor.tsx
  22. 2
      components/anchor/index.ts
  23. 8
      components/anchor/style/index.ts
  24. 2
      components/app/index.tsx
  25. 8
      components/auto-complete/index.tsx
  26. 2
      components/avatar/index.ts
  27. 36
      components/avatar/style/index.ts
  28. 6
      components/back-top/index.tsx
  29. 23
      components/badge/Ribbon.tsx
  30. 124
      components/badge/ScrollNumber.tsx
  31. 2
      components/badge/index.tsx
  32. 2
      components/breadcrumb/index.ts
  33. 28
      components/breadcrumb/style/index.ts
  34. 11
      components/button/LoadingIcon.tsx
  35. 4
      components/button/button-group.tsx
  36. 4
      components/button/demo/chinese-chars-loading.tsx
  37. 2
      components/button/demo/loading.md
  38. 2
      components/button/index.en-US.md
  39. 2
      components/button/index.ts
  40. 2
      components/button/index.zh-CN.md
  41. 7
      components/button/style/index.ts
  42. 5
      components/calendar/generateCalendar.tsx
  43. 2
      components/calendar/index.ts
  44. 24
      components/calendar/style/index.ts
  45. 19
      components/card/Card.tsx
  46. 11
      components/card/__tests__/index.test.tsx
  47. 2
      components/card/index.ts
  48. 36
      components/card/style/index.ts
  49. 172
      components/carousel/index.tsx
  50. 12
      components/carousel/style/index.ts
  51. 2
      components/cascader/index.tsx
  52. 16
      components/cascader/style/index.ts
  53. 14
      components/checkbox/Checkbox.tsx
  54. 74
      components/checkbox/Group.tsx
  55. 4
      components/checkbox/index.ts
  56. 2
      components/col/index.ts
  57. 10
      components/collapse/Collapse.tsx
  58. 2
      components/collapse/CollapsePanel.tsx
  59. 2
      components/collapse/index.ts
  60. 75
      components/color-picker/ColorPicker.tsx
  61. 13
      components/color-picker/ColorPickerPanel.tsx
  62. 693
      components/color-picker/__tests__/__snapshots__/demo-extend.test.ts.snap
  63. 34
      components/color-picker/__tests__/__snapshots__/demo.test.ts.snap
  64. 116
      components/color-picker/__tests__/index.test.tsx
  65. 3
      components/color-picker/color.ts
  66. 8
      components/color-picker/components/ColorInput.tsx
  67. 16
      components/color-picker/components/PanelPicker.tsx
  68. 8
      components/color-picker/components/PanelPresets.tsx
  69. 7
      components/color-picker/demo/change-completed.md
  70. 21
      components/color-picker/demo/change-completed.tsx
  71. 7
      components/color-picker/demo/disabled-alpha.md
  72. 6
      components/color-picker/demo/disabled-alpha.tsx
  73. 9
      components/color-picker/hooks/useColorState.ts
  74. 4
      components/color-picker/index.en-US.md
  75. 4
      components/color-picker/index.ts
  76. 4
      components/color-picker/index.zh-CN.md
  77. 4
      components/color-picker/interface.ts
  78. 40
      components/color-picker/style/index.ts
  79. 11
      components/color-picker/style/picker.ts
  80. 6
      components/color-picker/util.ts
  81. 36
      components/config-provider/__tests__/form.test.tsx
  82. 483
      components/config-provider/__tests__/style.test.tsx
  83. 16
      components/config-provider/context.ts
  84. 14
      components/config-provider/index.en-US.md
  85. 45
      components/config-provider/index.tsx
  86. 14
      components/config-provider/index.zh-CN.md
  87. 2
      components/date-picker/__tests__/DatePicker.test.tsx
  88. 30
      components/date-picker/__tests__/__snapshots__/demo-extend.test.ts.snap
  89. 15
      components/date-picker/generatePicker/generateSinglePicker.tsx
  90. 2
      components/date-picker/index.ts
  91. 12
      components/date-picker/style/index.ts
  92. 27
      components/descriptions/Cell.tsx
  93. 2
      components/descriptions/index.en-US.md
  94. 43
      components/descriptions/index.tsx
  95. 2
      components/descriptions/index.zh-CN.md
  96. 24
      components/descriptions/style/index.ts
  97. 2
      components/divider/index.tsx
  98. 18
      components/drawer/index.tsx
  99. 12
      components/drawer/style/index.ts
  100. 2
      components/dropdown/__tests__/index.test.tsx

4
.dumi/theme/builtins/ColorChunk/index.tsx

@ -1,6 +1,6 @@
import * as React from 'react';
import { TinyColor, type ColorInput } from '@ctrl/tinycolor';
import { css } from '@emotion/react';
import * as React from 'react';
import useSiteToken from '../../../hooks/useSiteToken';
interface ColorChunkProps {
@ -23,7 +23,7 @@ const useStyle = () => {
display: inline-block;
width: 6px;
height: 6px;
border-radius: ${token.borderRadiusSM}px;
border-radius: 50%;
margin-inline-end: 4px;
border: 1px solid ${token.colorSplit};
`,

70
.dumi/theme/builtins/ComponentTokenTable/index.tsx

@ -17,12 +17,16 @@ const locales = {
description: '描述',
type: '类型',
value: '默认值',
componentToken: '组件 Token',
globalToken: '全局 Token',
},
en: {
token: 'Token Name',
description: 'Description',
type: 'Type',
value: 'Default Value',
componentToken: 'Component Token',
globalToken: 'Global Token',
},
};
@ -48,9 +52,10 @@ interface SubTokenTableProps {
defaultOpen?: boolean;
title: string;
tokens: string[];
component?: string;
}
const SubTokenTable: React.FC<SubTokenTableProps> = ({ defaultOpen, tokens, title }) => {
const SubTokenTable: React.FC<SubTokenTableProps> = ({ defaultOpen, tokens, title, component }) => {
const [, lang] = useLocale(locales);
const { token } = useSiteToken();
const columns = useColumns();
@ -64,22 +69,28 @@ const SubTokenTable: React.FC<SubTokenTableProps> = ({ defaultOpen, tokens, titl
}
const data = tokens
.sort((token1, token2) => {
const hasColor1 = token1.toLowerCase().includes('color');
const hasColor2 = token2.toLowerCase().includes('color');
if (hasColor1 && !hasColor2) {
return -1;
}
if (!hasColor1 && hasColor2) {
return 1;
}
return token1 < token2 ? -1 : 1;
})
.sort(
component
? undefined
: (token1, token2) => {
const hasColor1 = token1.toLowerCase().includes('color');
const hasColor2 = token2.toLowerCase().includes('color');
if (hasColor1 && !hasColor2) {
return -1;
}
if (!hasColor1 && hasColor2) {
return 1;
}
return token1 < token2 ? -1 : 1;
},
)
.map((name) => {
const meta = tokenMeta[name];
const meta = component
? tokenMeta.components[component].find((item) => item.token === name)
: tokenMeta.global[name];
if (!meta) {
return null;
@ -89,7 +100,7 @@ const SubTokenTable: React.FC<SubTokenTableProps> = ({ defaultOpen, tokens, titl
name,
desc: lang === 'cn' ? meta.desc : meta.descEn,
type: meta.type,
value: defaultToken[name],
value: component ? tokenData[component].component[name] : defaultToken[name],
};
})
.filter(Boolean);
@ -122,28 +133,33 @@ export interface ComponentTokenTableProps {
}
const ComponentTokenTable: React.FC<ComponentTokenTableProps> = ({ component }) => {
const [locale] = useLocale(locales);
const [mergedGlobalTokens] = useMemo(() => {
const globalTokenSet = new Set<string>();
let componentTokens: Record<string, string> = {};
component.split(',').forEach((comp) => {
const { global: globalTokens = [], component: singleComponentTokens = [] } =
tokenData[comp] || {};
const { global: globalTokens = [] } = tokenData[comp] || {};
globalTokens.forEach((token: string) => {
globalTokenSet.add(token);
});
componentTokens = {
...componentTokens,
...singleComponentTokens,
};
});
return [Array.from(globalTokenSet), componentTokens] as const;
return [Array.from(globalTokenSet)] as const;
}, [component]);
return <SubTokenTable title="Global Token" tokens={mergedGlobalTokens} />;
return (
<>
{tokenMeta.components[component] && (
<SubTokenTable
title={locale.componentToken}
tokens={tokenMeta.components[component].map((item) => item.token)}
component={component}
/>
)}
<SubTokenTable title={locale.globalToken} tokens={mergedGlobalTokens} />
</>
);
};
export default React.memo(ComponentTokenTable);

5
.dumi/theme/builtins/Previewer/index.tsx

@ -1,7 +1,6 @@
import type { FC } from 'react';
import React from 'react';
import type { IPreviewerProps } from 'dumi';
import { useTabMeta } from 'dumi';
import React from 'react';
import CodePreviewer from './CodePreviewer';
import DesignPreviewer from './DesignPreviewer';
@ -9,7 +8,7 @@ export interface AntdPreviewerProps extends IPreviewerProps {
originDebug?: IPreviewerProps['debug'];
}
const Previewer: FC<AntdPreviewerProps> = ({ ...props }) => {
const Previewer: React.FC<AntdPreviewerProps> = (props) => {
const tab = useTabMeta();
if (tab?.frontmatter.title === 'Design') {

2
.dumi/theme/builtins/TokenTable/index.tsx

@ -98,7 +98,7 @@ const TokenTable: FC<TokenTableProps> = ({ type }) => {
const data = React.useMemo<TokenData[]>(
() =>
Object.entries(tokenMeta)
Object.entries(tokenMeta.global)
.filter(([, meta]) => meta.source === type)
.map(([token, meta]) => ({
name: token,

11
.dumi/theme/common/Link.tsx

@ -14,10 +14,13 @@ const Link = forwardRef<HTMLAnchorElement, LinkProps>((props, ref) => {
const handleClick = (e: MouseEvent<HTMLAnchorElement>) => {
if (!to.startsWith('http')) {
e.preventDefault();
startTransition(() => {
navigate(to);
});
// Should support open in new tab
if (!e.metaKey && !e.ctrlKey && !e.shiftKey) {
e.preventDefault();
startTransition(() => {
navigate(to);
});
}
}
};

2
.dumi/theme/common/ThemeSwitch/index.tsx

@ -14,7 +14,7 @@ export type ThemeSwitchProps = {
onChange: (value: ThemeName[]) => void;
};
const ThemeSwitch: React.FC<ThemeSwitchProps> = (props: ThemeSwitchProps) => {
const ThemeSwitch: React.FC<ThemeSwitchProps> = (props) => {
const { value = ['light'], onChange } = props;
const { token } = useSiteToken();
const { pathname, search } = useLocation();

46
CHANGELOG.en-US.md

@ -15,6 +15,50 @@ timeline: true
---
## 5.7.0
`2023-07-11`
- 🆕 ConfigProvider now supports `className` and `style` properties for all components. Thanks to [@Yuiai01](https://github.com/Yuiai01), [@li-jia-nan](https://github.com/li-jia-nan), [@MuxinFeng](https://github.com/MuxinFeng) for their contributions.
- 🆕 Badge now supports `classNames` and `styles` properties. [#43245](https://github.com/ant-design/ant-design/pull/43245) [@li-jia-nan](https://github.com/li-jia-nan)
- 🆕 ColorPicker now supports new features such as `showText`, `destroyTooltipOnHide`, `onChangeComplete`, `panelRender` and `size`.
- [#42865](https://github.com/ant-design/ant-design/pull/42865) [@RedJue](https://github.com/RedJue)
- [#42645](https://github.com/ant-design/ant-design/pull/42645) [@linxianxi](https://github.com/linxianxi)
- [#43370](https://github.com/ant-design/ant-design/pull/43370) [@RedJue](https://github.com/RedJue)
- [#43134](https://github.com/ant-design/ant-design/pull/43134) [@RedJue](https://github.com/RedJue)
- [#43116](https://github.com/ant-design/ant-design/pull/43116) [@RedJue](https://github.com/RedJue)
- 🆕 Alert, Drawer, Modal, Notifaction, Tag, Tabs now support hiding the close button by setting `closeIcon` to null or false. [#42828](https://github.com/ant-design/ant-design/discussions/42828) [@kiner-tang](https://github.com/kiner-tang)
- 🆕 Image supports `imageRender`, `toolbarRender` attributes to support custom rendering of preview images and toolbars, also supports new props such as `onTransform`, `minScale`, `maxScale`. Image.PreviewGroup supports `items` attribute to pass in list data, and fixes that the native attributes of the img tag are not passed to preview images The problem. [#43075](https://github.com/ant-design/ant-design/pull/43075) [@linxianxi](https://github.com/linxianxi)
- 🆕 Modify the layout style of the Image preview, the `preview` attribute supports `closeIcon`, Image.PreviewGroup supports the `fallback` attribute, and fixes the problem of loading preview resources in advance. [#43167](https://github.com/ant-design/ant-design/pull/43167) [@linxianxi](https://github.com/linxianxi)
- 🆕 Changed the layout style, Preview now supports `closeIcon`, PreviewGroup now supports `fallback`, and fixed an issue where preview resources would be loaded at the beginning.[#43167](https://github.com/ant-design/ant-design/pull/43167) [@linxianxi](https://github.com/linxianxi)
- 🛠 InputNumber was refactored to use rc-input. (#43000)。[#42762](https://github.com/ant-design/ant-design/pull/43000) [@MuxinFeng](https://github.com/MuxinFeng)
- 🛠 Resolved Circular dependency issue in vite, rollup, meteor and microbundle. [#42750](https://github.com/ant-design/ant-design/pull/42750). Thanks to [@jrr997](https://github.com/jrr997), [@kiner-tang](https://github.com/kiner-tang) and [@MuxinFeng](https://github.com/MuxinFeng) for their contributions.
- 🐞 Remove default values (empty string) of `className` prop in Anchor, CollapsePanel, and Input.Group. [#43481](https://github.com/ant-design/ant-design/pull/43481) [@thinkasany](https://github.com/thinkasany)
- 🐞 Fix Upload progress bar missing fade motion. [#43471](https://github.com/ant-design/ant-design/pull/43471)
- 🐞 Added warning for deprecated Token `colorItemBgSelected` in Menu.[#43461](https://github.com/ant-design/ant-design/pull/43461) [@MadCcc](https://github.com/MadCcc)
- 🐞 Fixed an issue where some browsers had scroll bars that were not redrawn when style feature support was detected.[#43358](https://github.com/ant-design/ant-design/pull/43358) [@LeeeeeeM](https://github.com/LeeeeeeM)
- 🐞 Fixed an issue where the Tab component of Card would not be displayed at all when tabList is empty.[#43416](https://github.com/ant-design/ant-design/pull/43416) [@linxianxi](https://github.com/linxianxi)
- 🐞 Fixed an issue where the `form.validateMessages`` configuration would be lost when using ConfigProvider nestedly.[#43239](https://github.com/ant-design/ant-design/pull/43239) [@Wxh16144](https://github.com/Wxh16144)
- 🐞 Fixed an issue where the ripple effect of Tag click would sometimes be offset from the Tag element.[#43402](https://github.com/ant-design/ant-design/pull/43402)
- 🐞 Fixed an issue where clicking "now" in DatePicker when switching to the year-month panel would not work.[#43367](https://github.com/ant-design/ant-design/pull/43367) [@Yuiai01](https://github.com/Yuiai01)
- 🐞 Fixed an issue where the height set for the TextArea component would become invalid when the screen size changed.[#43169](https://github.com/ant-design/ant-design/pull/43169) [@MadCcc](https://github.com/MadCcc)
- 💄 In Slider, the `tooltip` should be centered when there is little content. [#43430](https://github.com/ant-design/ant-design/pull/43430) [@Jomorx](https://github.com/Jomorx)
- 💄 Added `colorLink` to the seed token, and `colorLinkHover` and `colorLinkActive` will be calculated from colorLink.[#43183](https://github.com/ant-design/ant-design/pull/43183) [@MadCcc](https://github.com/MadCcc)
- 💄 Adjusted some tokens in Slider to component tokens. [#42428](https://github.com/ant-design/ant-design/pull/42428) [@heiyu4585](https://github.com/heiyu4585) RTL[#42428](https://github.com/ant-design/ant-design/pull/42428) [@heiyu4585](https://github.com/heiyu4585)
- RTL
- 🤖 Progress now supports animations in rtl direction.[#43316](https://github.com/ant-design/ant-design/pull/43316) [@Yuiai01](https://github.com/Yuiai01)
- TypeScript
- 🤖 Added `RawPurePanelProps` interface description for Popover.[#43453](https://github.com/ant-design/ant-design/pull/43453) [@thinkasany](https://github.com/thinkasany)
- 🤖 Replaced `ref` type with `TooltipRef` instead of `unknown` for `Popconfirm`.[#43452](https://github.com/ant-design/ant-design/pull/43452) [@thinkasany](https://github.com/thinkasany)
- 🤖 Replaced `ref` type with `TooltipRef` instead of `unknown` for Popover.[#43450](https://github.com/ant-design/ant-design/pull/43450) [@Negentropy247](https://github.com/Negentropy247)
- 🤖 Improved type declaration of `GroupSizeContext` in ButtonGroup.[#43439](https://github.com/ant-design/ant-design/pull/43439) [@thinkasany](https://github.com/thinkasany)
- 🤖 Improved type declaration of `mode` property in Select.[#43413](https://github.com/ant-design/ant-design/pull/43413) [@thinkasany](https://github.com/thinkasany)
- 🤖 Replaced `ref` type with `CheckboxRef` instead of `unknown` for Checkbox.[#43424](https://github.com/ant-design/ant-design/pull/43424) [@li-jia-nan](https://github.com/li-jia-nan)
- 🤖 Improved internal type implementation for Table/Tag/Notification.
- [#43366](https://github.com/ant-design/ant-design/pull/43366) [@li-jia-nan](https://github.com/li-jia-nan)
- [#43357](https://github.com/ant-design/ant-design/pull/43357) [@thinkasany](https://github.com/thinkasany)
- [#43351](https://github.com/ant-design/ant-design/pull/43351) [@thinkasany](https://github.com/thinkasany)
## 5.6.4
`2023-07-03`
@ -794,7 +838,7 @@ timeline: true
- 🐞 Fix Tree config `checkable` and `blockNode` not makes `title` stretch issue. [#39209](https://github.com/ant-design/ant-design/pull/39209) [@Wxh16144](https://github.com/Wxh16144)
- 🐞 Fix Dropdown sub menu missing motion. [#39235](https://github.com/ant-design/ant-design/pull/39235)
- 💄 Fix RangePicker time panel padding style. [#39228](https://github.com/ant-design/ant-design/pull/39228)
- 🐞 Fix Card action button round style. [#39210](https://github.com/ant-design/ant-design/pull/39210) [@muxin](https://github.com/muxin)
- 🐞 Fix Card action button round style. [#39210](https://github.com/ant-design/ant-design/pull/39210) [@MuxinFeng](https://github.com/MuxinFeng)
- 🐞 Fix Badge wave effect color not follow `color`. [#39182](https://github.com/ant-design/ant-design/pull/39182) [@li-jia-nan](https://github.com/li-jia-nan)
- 🐞 Fix Radio disabled status check style. [#39165](https://github.com/ant-design/ant-design/pull/39165) [@Wxh16144](https://github.com/Wxh16144)
- 🐞 Fixed TextArea count style when `resize` is not `none`. [#39121](https://github.com/ant-design/ant-design/pull/39121) [@51wangping](https://github.com/51wangping)

46
CHANGELOG.zh-CN.md

@ -15,6 +15,50 @@ timeline: true
---
## 5.7.0
`2023-07-11`
- 🆕 ConfigProvider 支持所有组件的 `className``style` 属性控制。感谢 [@Yuiai01](https://github.com/Yuiai01)、[@li-jia-nan](https://github.com/li-jia-nan) 和 [@MuxinFeng](https://github.com/MuxinFeng) 的贡献。
- 🆕 Badge 支持 `classNames` 属性和 `styles` 属性。[#43245](https://github.com/ant-design/ant-design/pull/43245) [@li-jia-nan](https://github.com/li-jia-nan)
- 🆕 ColorPicker 支持 `showText`、`destroyTooltipOnHide`、`onChangeComplete`、`panelRender`、`size` 等新特性。
- [#42865](https://github.com/ant-design/ant-design/pull/42865) [@RedJue](https://github.com/RedJue)
- [#42645](https://github.com/ant-design/ant-design/pull/42645) [@linxianxi](https://github.com/linxianxi)
- [#43370](https://github.com/ant-design/ant-design/pull/43370) [@RedJue](https://github.com/RedJue)
- [#43134](https://github.com/ant-design/ant-design/pull/43134) [@RedJue](https://github.com/RedJue)
- [#43116](https://github.com/ant-design/ant-design/pull/43116) [@RedJue](https://github.com/RedJue)
- 🆕 Alert、Drawer、Modal、Notifaction、Tag、Tabs 均已支持通过设置 `closeIcon` 为 null 或 false 隐藏关闭按钮。 [#42828](https://github.com/ant-design/ant-design/discussions/42828) [@kiner-tang](https://github.com/kiner-tang)
- 🆕 Anchor 添加 `replace` 属性。[#43006](https://github.com/ant-design/ant-design/pull/43006) [@ds1371dani](https://github.com/ds1371dani)
- 🆕 Image 支持 `imageRender`、`toolbarRender` 属性以支持预览图和工具栏的自定义渲染,还支持了 `onTransform`、`minScale`、`maxScale` 等新属性,Image.PreviewGroup 支持 `items` 属性传入列表数据,并修复了 img 标签的原生属性没有传递给预览图的问题。[#43075](https://github.com/ant-design/ant-design/pull/43075) [@linxianxi](https://github.com/linxianxi)
- 🆕 修改 Image 预览图的布局风格,`preview` 属性支持 `closeIcon`,Image.PreviewGroup 支持 `fallback` 属性,修复加载预览资源提前加载的问题。[#43167](https://github.com/ant-design/ant-design/pull/43167) [@linxianxi](https://github.com/linxianxi)
- 🛠 InputNumber 使用 rc-input 进行重构。[#43000](https://github.com/ant-design/ant-design/pull/43000) [@MuxinFeng](https://github.com/MuxinFeng)
- 🛠 解决 vite、rollup、meteor、microbundle 等构建工具中遇到的循环依赖问题,并增加相关的检测。[#42750](https://github.com/ant-design/ant-design/pull/42750),感谢 [@jrr997](https://github.com/jrr997)、[@kiner-tang](https://github.com/kiner-tang) 和 [@MuxinFeng](https://github.com/MuxinFeng) 的贡献。
- 🐞 移除 Anchor/CollapsePanel/Input.Group 组件中 `className` 属性的默认值(空字符串)。 [#43481](https://github.com/ant-design/ant-design/pull/43481) [@thinkasany](https://github.com/thinkasany)
- 🐞 修复 Upload 上传进度条延迟消失且丢失动画效果的问题。[#43471](https://github.com/ant-design/ant-design/pull/43471)
- 🐞 为 Menu 中组件 Token `colorItemBgSelected` 添加废弃警告。[#43461](https://github.com/ant-design/ant-design/pull/43461) [@MadCcc](https://github.com/MadCcc)
- 🐞 修复样式特性支持检测时部分浏览器因为未重绘导致出现滚动条的问题。[#43358](https://github.com/ant-design/ant-design/pull/43358) [@LeeeeeeM](https://github.com/LeeeeeeM)
- 🐞 修复 Card `tabList` 为空时 Tab 完全不展示的问题。[#43416](https://github.com/ant-design/ant-design/pull/43416) [@linxianxi](https://github.com/linxianxi)
- 🐞 修复 ConfigProvider 嵌套使用时,`form.validateMessages` 配置会丢失的问题。[#43239](https://github.com/ant-design/ant-design/pull/43239) [@Wxh16144](https://github.com/Wxh16144)
- 🐞 修复 Tag 点击的水波纹效果有时候会和 Tag 元素产生偏移的问题。[#43402](https://github.com/ant-design/ant-design/pull/43402)
- 🐞 修复 DatePicker 切换到年月面板时,`此刻` 点击无效的问题。[#43367](https://github.com/ant-design/ant-design/pull/43367) [@Yuiai01](https://github.com/Yuiai01)
- 🐞 修复 TextArea 组件在屏幕大小变化时设置的高度失效的问题。[#43169](https://github.com/ant-design/ant-design/pull/43169) [@MadCcc](https://github.com/MadCcc)
- 💄 Slider 中 `tooltip` 在内容很少时应该居中。[#43430](https://github.com/ant-design/ant-design/pull/43430) [@Jomorx](https://github.com/Jomorx)
- 💄 将 `colorLink` 添加至 seed token 中, `colorLinkHover``colorLinkActive` 将会由 `colorLink` 计算得出。[#43183](https://github.com/ant-design/ant-design/pull/43183) [@MadCcc](https://github.com/MadCcc)
- 💄 调整 Slider 中部分 token 为 component token。[#42428](https://github.com/ant-design/ant-design/pull/42428) [@heiyu4585](https://github.com/heiyu4585)
- RTL
- 🤖 Progress 支持 rtl 方向的动画。[#43316](https://github.com/ant-design/ant-design/pull/43316) [@Yuiai01](https://github.com/Yuiai01)
- TypeScript
- 🤖 Popover 增加 `RawPurePanelProps` 接口描述。[#43453](https://github.com/ant-design/ant-design/pull/43453) [@thinkasany](https://github.com/thinkasany)
- 🤖 Popconfirm 替换 `ref` 类型 `unknown``TooltipRef`。[#43452](https://github.com/ant-design/ant-design/pull/43452) [@thinkasany](https://github.com/thinkasany)
- 🤖 Popover 替换 `ref` 类型 `unknown``TooltipRef`。[#43450](https://github.com/ant-design/ant-design/pull/43450) [@Negentropy247](https://github.com/Negentropy247)
- 🤖 改进 ButtonGroup 中 `GroupSizeContext` 的类型声明。[#43439](https://github.com/ant-design/ant-design/pull/43439) [@thinkasany](https://github.com/thinkasany)
- 🤖 改进 Select 的 `mode` 属性的类型声明。[#43413](https://github.com/ant-design/ant-design/pull/43413) [@thinkasany](https://github.com/thinkasany)
- 🤖 Checkbox 替换 `ref` 类型 `unknown``CheckboxRef`。[#43424](https://github.com/ant-design/ant-design/pull/43424) [@li-jia-nan](https://github.com/li-jia-nan)
- 🤖 改进 Table/Tag/Notification 内部类型实现。
- [#43366](https://github.com/ant-design/ant-design/pull/43366) [@li-jia-nan](https://github.com/li-jia-nan)
- [#43357](https://github.com/ant-design/ant-design/pull/43357) [@thinkasany](https://github.com/thinkasany)
- [#43351](https://github.com/ant-design/ant-design/pull/43351) [@thinkasany](https://github.com/thinkasany)
## 5.6.4
`2023-07-03`
@ -795,7 +839,7 @@ timeline: true
- 🐞 修复 Tree `checkable``blockNode` 配合时,`title` 元素不拉伸的问题。[#39209](https://github.com/ant-design/ant-design/pull/39209) [@Wxh16144](https://github.com/Wxh16144)
- 🐞 修复 Dropdown 二级菜单丢失动画的问题。[#39235](https://github.com/ant-design/ant-design/pull/39235)
- 💄 修复 RangePicker 内时间面板的 padding 样式。[#39228](https://github.com/ant-design/ant-design/pull/39228)
- 🐞 修复 Card 的按钮组圆角样式。[#39210](https://github.com/ant-design/ant-design/pull/39210) [@muxin](https://github.com/muxin)
- 🐞 修复 Card 的按钮组圆角样式。[#39210](https://github.com/ant-design/ant-design/pull/39210) [@MuxinFeng](https://github.com/MuxinFeng)
- 🐞 修复了 Badge 自定义颜色的时候,波纹的颜色不会跟着小圆点颜色发生变化的问题。[#39182](https://github.com/ant-design/ant-design/pull/39182) [@li-jia-nan](https://github.com/li-jia-nan)
- 🐞 修复 Radio 禁用状态选中样式。[#39165](https://github.com/ant-design/ant-design/pull/39165) [@Wxh16144](https://github.com/Wxh16144)
- 🐞 修复 TextArea `resize` 不是 `none` 时计数文字的样式问题。[#39121](https://github.com/ant-design/ant-design/pull/39121) [@51wangping](https://github.com/51wangping)

10
components/__tests__/blog.test.ts

@ -4,12 +4,20 @@ const path = require('path');
const blogList = [
'check-conduct',
'contributor-development-maintenance-guide',
'css-in-js',
'extract-ssr',
'getContainer',
'github-actions-workflow',
'issue-helper',
'mock-project-build',
'modal-hook-order',
'render-times',
'testing-migrate',
'render-times',
'to-be-collaborator',
'tooltip-align',
'tree-shaking',
'why-not-static',
].map((blogName) => path.join(__dirname, `../../docs/blog/${blogName}.en-US.md`));
describe('blog', () => {

12
components/_util/ActionButton.tsx

@ -14,6 +14,11 @@ export interface ActionButtonProps {
emitEvent?: boolean;
quitOnNullishReturnValue?: boolean;
children?: React.ReactNode;
/**
* Do not throw if is await mode
*/
isSilent?: () => boolean;
}
function isThenable<T extends any>(thing?: PromiseLike<T>): boolean {
@ -29,6 +34,7 @@ const ActionButton: React.FC<ActionButtonProps> = (props) => {
close,
autoFocus,
emitEvent,
isSilent,
quitOnNullishReturnValue,
actionFn,
} = props;
@ -70,6 +76,12 @@ const ActionButton: React.FC<ActionButtonProps> = (props) => {
// See: https://github.com/ant-design/ant-design/issues/6183
setLoading(false, true);
clickedRef.current = false;
// Do not throw if is `await` mode
if (isSilent?.()) {
return;
}
return Promise.reject(e);
},
);

3
components/_util/reactNode.ts

@ -1,4 +1,5 @@
import * as React from 'react';
import type { AnyObject } from './type';
export const { isValidElement } = React;
@ -6,8 +7,6 @@ export function isFragment(child: any): boolean {
return child && isValidElement(child) && child.type === React.Fragment;
}
type AnyObject = Record<PropertyKey, any>;
type RenderProps = AnyObject | ((originProps: AnyObject) => AnyObject | void);
export function replaceElement(

11
components/_util/styleChecker.ts

@ -25,10 +25,17 @@ export const detectFlexGapSupported = () => {
flex.appendChild(document.createElement('div'));
flex.appendChild(document.createElement('div'));
// some browser may not repaint when remove nodes, so we need create a new layer to detect.
const container = document.createElement('div');
container.style.position = 'absolute';
container.style.zIndex = '-9999';
container.appendChild(flex);
// append to the DOM (needed to obtain scrollHeight)
document.body.appendChild(flex);
document.body.appendChild(container);
flexGapSupported = flex.scrollHeight === 1; // flex container should be 1px high from the row-gap
document.body.removeChild(flex);
document.body.removeChild(container);
return flexGapSupported;
};

2
components/_util/type.ts

@ -1,2 +1,4 @@
/** https://github.com/Microsoft/TypeScript/issues/29729 */
export type LiteralUnion<T extends string> = T | (string & {});
export type AnyObject = Record<PropertyKey, any>;

2
components/affix/index.tsx

@ -1,3 +1,5 @@
'use client';
import classNames from 'classnames';
import ResizeObserver from 'rc-resize-observer';
import omit from 'rc-util/lib/omit';

67
components/alert/Alert.tsx

@ -96,31 +96,33 @@ const CloseIcon: React.FC<CloseIconProps> = (props) => {
) : null;
};
const Alert: React.FC<AlertProps> = ({
description,
prefixCls: customizePrefixCls,
message,
banner,
className,
rootClassName,
style,
onMouseEnter,
onMouseLeave,
onClick,
afterClose,
showIcon,
closable,
closeText,
closeIcon,
action,
...props
}) => {
const Alert: React.FC<AlertProps> = (props) => {
const {
description,
prefixCls: customizePrefixCls,
message,
banner,
className,
rootClassName,
style,
onMouseEnter,
onMouseLeave,
onClick,
afterClose,
showIcon,
closable,
closeText,
closeIcon,
action,
...otherProps
} = props;
const [closed, setClosed] = React.useState(false);
if (process.env.NODE_ENV !== 'production') {
warning(!closeText, 'Alert', '`closeText` is deprecated. Please use `closeIcon` instead.');
}
const ref = React.useRef<HTMLDivElement>(null);
const { getPrefixCls, direction } = React.useContext(ConfigContext);
const { getPrefixCls, direction, alert } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('alert', customizePrefixCls);
const [wrapSSR, hashId] = useStyle(prefixCls);
@ -129,14 +131,13 @@ const Alert: React.FC<AlertProps> = ({
props.onClose?.(e);
};
const getType = () => {
const { type } = props;
if (type !== undefined) {
return type;
const type = React.useMemo<AlertProps['type']>(() => {
if (props.type !== undefined) {
return props.type;
}
// banner mode defaults to 'warning'
return banner ? 'warning' : 'info';
};
}, [props.type, banner]);
// closeable when closeText or closeIcon is assigned
const isClosable = React.useMemo(() => {
@ -150,8 +151,6 @@ const Alert: React.FC<AlertProps> = ({
return closeIcon !== false && closeIcon !== null && closeIcon !== undefined;
}, [closeText, closeIcon, closable]);
const type = getType();
// banner mode defaults to Icon
const isShowIcon = banner && showIcon === undefined ? true : showIcon;
@ -164,15 +163,13 @@ const Alert: React.FC<AlertProps> = ({
[`${prefixCls}-banner`]: !!banner,
[`${prefixCls}-rtl`]: direction === 'rtl',
},
alert?.className,
className,
rootClassName,
hashId,
);
const dataOrAriaProps = pickAttrs(props, {
aria: true,
data: true,
});
const restProps = pickAttrs(otherProps, { aria: true, data: true });
return wrapSSR(
<CSSMotion
@ -180,9 +177,7 @@ const Alert: React.FC<AlertProps> = ({
motionName={`${prefixCls}-motion`}
motionAppear={false}
motionEnter={false}
onLeaveStart={(node) => ({
maxHeight: node.offsetHeight,
})}
onLeaveStart={(node) => ({ maxHeight: node.offsetHeight })}
onLeaveEnd={afterClose}
>
{({ className: motionClassName, style: motionStyle }) => (
@ -190,12 +185,12 @@ const Alert: React.FC<AlertProps> = ({
ref={ref}
data-show={!closed}
className={classNames(alertCls, motionClassName)}
style={{ ...style, ...motionStyle }}
style={{ ...alert?.style, ...style, ...motionStyle }}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onClick={onClick}
role="alert"
{...dataOrAriaProps}
{...restProps}
>
{isShowIcon ? (
<IconNode

4
components/alert/__tests__/__snapshots__/demo-extend.test.ts.snap

@ -165,7 +165,7 @@ exports[`renders components/alert/demo/action.tsx extend context correctly 1`] =
class="ant-space-item"
>
<button
class="ant-btn ant-btn-ghost ant-btn-sm"
class="ant-btn ant-btn-text ant-btn-sm"
type="button"
>
<span>
@ -247,7 +247,7 @@ exports[`renders components/alert/demo/action.tsx extend context correctly 1`] =
class="ant-space-item"
>
<button
class="ant-btn ant-btn-ghost ant-btn-sm ant-btn-dangerous"
class="ant-btn ant-btn-default ant-btn-sm ant-btn-background-ghost ant-btn-dangerous"
type="button"
>
<span>

4
components/alert/__tests__/__snapshots__/demo.test.ts.snap

@ -165,7 +165,7 @@ exports[`renders components/alert/demo/action.tsx correctly 1`] = `
class="ant-space-item"
>
<button
class="ant-btn ant-btn-ghost ant-btn-sm"
class="ant-btn ant-btn-text ant-btn-sm"
type="button"
>
<span>
@ -247,7 +247,7 @@ exports[`renders components/alert/demo/action.tsx correctly 1`] = `
class="ant-space-item"
>
<button
class="ant-btn ant-btn-ghost ant-btn-sm ant-btn-dangerous"
class="ant-btn ant-btn-default ant-btn-sm ant-btn-background-ghost ant-btn-dangerous"
type="button"
>
<span>

4
components/alert/demo/action.tsx

@ -30,7 +30,7 @@ const App: React.FC = () => (
type="warning"
action={
<Space>
<Button size="small" type="ghost">
<Button type="text" size="small" ghost>
Done
</Button>
</Space>
@ -46,7 +46,7 @@ const App: React.FC = () => (
<Button size="small" type="primary">
Accept
</Button>
<Button size="small" danger type="ghost">
<Button size="small" danger ghost>
Decline
</Button>
</Space>

4
components/alert/demo/closable.tsx

@ -10,14 +10,14 @@ const App: React.FC = () => (
<Alert
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
type="warning"
closeIcon
closable
onClose={onClose}
/>
<Alert
message="Error Text"
description="Error Description Error Description Error Description Error Description Error Description Error Description"
type="error"
closeIcon
closable
onClose={onClose}
/>
</Space>

2
components/alert/index.ts

@ -1,3 +1,5 @@
'use client';
import type { AlertProps } from './Alert';
import InternalAlert from './Alert';
import ErrorBoundary from './ErrorBoundary';

2
components/anchor/Anchor.tsx

@ -114,7 +114,7 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
const {
rootClassName,
anchorPrefixCls: prefixCls,
className = '',
className,
style,
offsetTop,
affix = true,

2
components/anchor/index.ts

@ -1,3 +1,5 @@
'use client';
import InternalAnchor from './Anchor';
import AnchorLink from './AnchorLink';

8
components/anchor/style/index.ts

@ -4,7 +4,15 @@ import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
export interface ComponentToken {
/**
* @desc
* @descEN Link horizontal padding
*/
linkPaddingBlock: number;
/**
* @desc
* @descEN Link vertical padding
*/
linkPaddingInlineStart: number;
}

2
components/app/index.tsx

@ -1,3 +1,5 @@
'use client';
import classNames from 'classnames';
import type { ReactNode } from 'react';
import React, { useContext } from 'react';

8
components/auto-complete/index.tsx

@ -1,10 +1,4 @@
/**
* TODO: 4.0
*
* - Remove `dataSource`
* - `size` not work with customizeInput
* - CustomizeInput not feedback `ENTER` key since accessibility enhancement
*/
'use client';
import classNames from 'classnames';
import type { BaseSelectRef } from 'rc-select';

2
components/avatar/index.ts

@ -1,3 +1,5 @@
'use client';
import type { ForwardRefExoticComponent, RefAttributes } from 'react';
import type { AvatarProps } from './avatar';
import InternalAvatar from './avatar';

36
components/avatar/style/index.ts

@ -4,14 +4,50 @@ import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
export interface ComponentToken {
/**
* @desc
* @descEN Background color of Avatar
*/
containerSize: number;
/**
* @desc
* @descEN Size of large Avatar
*/
containerSizeLG: number;
/**
* @desc
* @descEN Size of small Avatar
*/
containerSizeSM: number;
/**
* @desc
* @descEN Font size of Avatar
*/
textFontSize: number;
/**
* @desc
* @descEN Font size of large Avatar
*/
textFontSizeLG: number;
/**
* @desc
* @descEN Font size of small Avatar
*/
textFontSizeSM: number;
/**
* @desc
* @descEN Spacing between avatars in a group
*/
groupSpace: number;
/**
* @desc
* @descEN Overlapping of avatars in a group
*/
groupOverlapping: number;
/**
* @desc
* @descEN Border color of avatars in a group
*/
groupBorderColor: string;
}

6
components/back-top/index.tsx

@ -1,15 +1,17 @@
'use client';
import VerticalAlignTopOutlined from '@ant-design/icons/VerticalAlignTopOutlined';
import classNames from 'classnames';
import CSSMotion from 'rc-motion';
import omit from 'rc-util/lib/omit';
import * as React from 'react';
import type { ConfigConsumerProps } from '../config-provider';
import { ConfigContext } from '../config-provider';
import getScroll from '../_util/getScroll';
import { cloneElement } from '../_util/reactNode';
import scrollTo from '../_util/scrollTo';
import throttleByAnimationFrame from '../_util/throttleByAnimationFrame';
import warning from '../_util/warning';
import type { ConfigConsumerProps } from '../config-provider';
import { ConfigContext } from '../config-provider';
import useStyle from './style';
export interface BackTopProps {

23
components/badge/Ribbon.tsx

@ -1,10 +1,10 @@
import classNames from 'classnames';
import * as React from 'react';
import { ConfigContext } from '../config-provider';
import type { PresetColorType } from '../_util/colors';
import { isPresetColor } from '../_util/colors';
import type { LiteralUnion } from '../_util/type';
import { ConfigContext } from '../config-provider';
import useStyle from './style';
import { isPresetColor } from '../_util/colors';
type RibbonPlacement = 'start' | 'end';
@ -18,15 +18,16 @@ export interface RibbonProps {
placement?: RibbonPlacement;
}
const Ribbon: React.FC<RibbonProps> = ({
className,
prefixCls: customizePrefixCls,
style,
color,
children,
text,
placement = 'end',
}) => {
const Ribbon: React.FC<RibbonProps> = (props) => {
const {
className,
prefixCls: customizePrefixCls,
style,
color,
children,
text,
placement = 'end',
} = props;
const { getPrefixCls, direction } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('ribbon', customizePrefixCls);
const colorInPreset = isPresetColor(color, false);

124
components/badge/ScrollNumber.tsx

@ -1,7 +1,7 @@
import classNames from 'classnames';
import * as React from 'react';
import { ConfigContext } from '../config-provider';
import { cloneElement } from '../_util/reactNode';
import { ConfigContext } from '../config-provider';
import SingleNumber from './SingleNumber';
export interface ScrollNumberProps {
@ -21,75 +21,67 @@ export interface ScrollNumberState {
count?: string | number | null;
}
const ScrollNumber = React.forwardRef<HTMLElement, ScrollNumberProps>(
(
{
prefixCls: customizePrefixCls,
count,
className,
motionClassName,
style,
title,
show,
component: Component = 'sup',
children,
...restProps
},
ref,
) => {
const { getPrefixCls } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('scroll-number', customizePrefixCls);
const ScrollNumber = React.forwardRef<HTMLElement, ScrollNumberProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
count,
className,
motionClassName,
style,
title,
show,
component: Component = 'sup',
children,
...restProps
} = props;
const { getPrefixCls } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('scroll-number', customizePrefixCls);
// ============================ Render ============================
const newProps = {
...restProps,
'data-show': show,
style,
className: classNames(prefixCls, className, motionClassName),
title: title as string,
};
// ============================ Render ============================
const newProps = {
...restProps,
'data-show': show,
style,
className: classNames(prefixCls, className, motionClassName),
title: title as string,
};
// Only integer need motion
let numberNodes: React.ReactNode = count;
if (count && Number(count) % 1 === 0) {
const numberList = String(count).split('');
// Only integer need motion
let numberNodes: React.ReactNode = count;
if (count && Number(count) % 1 === 0) {
const numberList = String(count).split('');
numberNodes = numberList.map((num, i) => (
<SingleNumber
prefixCls={prefixCls}
count={Number(count)}
value={num}
// eslint-disable-next-line react/no-array-index-key
key={numberList.length - i}
/>
));
}
numberNodes = numberList.map((num, i) => (
<SingleNumber
prefixCls={prefixCls}
count={Number(count)}
value={num}
// eslint-disable-next-line react/no-array-index-key
key={numberList.length - i}
/>
));
}
// allow specify the border
// mock border-color by box-shadow for compatible with old usage:
// <Badge count={4} style={{ backgroundColor: '#fff', color: '#999', borderColor: '#d9d9d9' }} />
if (style && style.borderColor) {
newProps.style = {
...style,
boxShadow: `0 0 0 1px ${style.borderColor} inset`,
};
}
if (children) {
return cloneElement(children, (oriProps) => ({
className: classNames(
`${prefixCls}-custom-component`,
oriProps?.className,
motionClassName,
),
}));
}
// allow specify the border
// mock border-color by box-shadow for compatible with old usage:
// <Badge count={4} style={{ backgroundColor: '#fff', color: '#999', borderColor: '#d9d9d9' }} />
if (style && style.borderColor) {
newProps.style = {
...style,
boxShadow: `0 0 0 1px ${style.borderColor} inset`,
};
}
if (children) {
return cloneElement(children, (oriProps) => ({
className: classNames(`${prefixCls}-custom-component`, oriProps?.className, motionClassName),
}));
}
return (
<Component {...newProps} ref={ref}>
{numberNodes}
</Component>
);
},
);
return (
<Component {...newProps} ref={ref}>
{numberNodes}
</Component>
);
});
export default ScrollNumber;

2
components/badge/index.tsx

@ -1,3 +1,5 @@
'use client';
import classnames from 'classnames';
import CSSMotion from 'rc-motion';
import * as React from 'react';

2
components/breadcrumb/index.ts

@ -1,3 +1,5 @@
'use client';
import Breadcrumb from './Breadcrumb';
export type { BreadcrumbProps } from './Breadcrumb';

28
components/breadcrumb/style/index.ts

@ -4,12 +4,40 @@ import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
export interface ComponentToken {
/**
* @desc
* @descEN Text color of Breadcrumb item
*/
itemColor: string;
/**
* @desc
* @descEN Icon size
*/
iconFontSize: number;
/**
* @desc
* @descEN Text color of link
*/
linkColor: string;
/**
* @desc
* @descEN Color of hovered link
*/
linkHoverColor: string;
/**
* @desc
* @descEN Text color of the last item
*/
lastItemColor: string;
/**
* @desc
* @descEN Margin of separator
*/
separatorMargin: number;
/**
* @desc
* @descEN Color of separator
*/
separatorColor: string;
}

11
components/button/LoadingIcon.tsx

@ -1,7 +1,7 @@
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
import classNames from 'classnames';
import CSSMotion from 'rc-motion';
import React, { forwardRef } from 'react';
import classNames from 'classnames';
import IconWrapper from './IconWrapper';
type InnerLoadingIconProps = {
@ -43,13 +43,8 @@ const getRealWidth = (node: HTMLElement): React.CSSProperties => ({
transform: 'scale(1)',
});
const LoadingIcon: React.FC<LoadingIconProps> = ({
prefixCls,
loading,
existIcon,
className,
style,
}) => {
const LoadingIcon: React.FC<LoadingIconProps> = (props) => {
const { prefixCls, loading, existIcon, className, style } = props;
const visible = !!loading;
if (existIcon) {

4
components/button/button-group.tsx

@ -1,9 +1,9 @@
import classNames from 'classnames';
import * as React from 'react';
import warning from '../_util/warning';
import { ConfigContext } from '../config-provider';
import type { SizeType } from '../config-provider/SizeContext';
import { useToken } from '../theme/internal';
import warning from '../_util/warning';
export interface ButtonGroupProps {
size?: SizeType;
@ -13,7 +13,7 @@ export interface ButtonGroupProps {
children?: React.ReactNode;
}
export const GroupSizeContext = React.createContext<SizeType | undefined>(undefined);
export const GroupSizeContext = React.createContext<SizeType>(undefined);
const ButtonGroup: React.FC<ButtonGroupProps> = (props) => {
const { getPrefixCls, direction } = React.useContext(ConfigContext);

4
components/button/demo/chinese-chars-loading.tsx

@ -1,13 +1,13 @@
// @ts-nocheck
import React from 'react';
import { PoweroffOutlined } from '@ant-design/icons';
import { Button, Space } from 'antd';
import React from 'react';
const Text1 = () => '部署';
const Text2 = () => <span></span>;
const Text3 = () => 'Submit';
const App = () => (
const App: React.FC = () => (
<Space wrap>
<Button loading></Button>
<Button loading>

2
components/button/demo/loading.md

@ -1,6 +1,6 @@
## zh-CN
添加 `loading` 属性即可让按钮处于加载状态,最后个按钮演示点击后进入加载状态。
添加 `loading` 属性即可让按钮处于加载状态,最后个按钮演示点击后进入加载状态。
## en-US

2
components/button/index.en-US.md

@ -67,7 +67,7 @@ Different button styles can be generated by setting Button properties. The recom
| size | Set the size of button | `large` \| `middle` \| `small` | `middle` | |
| styles | Semantic DOM style | Record<SemanticDOM, CSSProperties> | - | 5.4.0 |
| target | Same as target attribute of a, works when href is specified | string | - | |
| type | Can be set to `primary` `ghost` `dashed` `link` `text` `default` | string | `default` | |
| type | Set button type | `primary` \| `dashed` \| `link` \| `text` \| `default` | `default` | |
| onClick | Set the handler to handle `click` event | (event: MouseEvent) => void | - | |
It accepts all props which native buttons support.

2
components/button/index.ts

@ -1,3 +1,5 @@
'use client';
import Button from './button';
export type { SizeType as ButtonSize } from '../config-provider/SizeContext';

2
components/button/index.zh-CN.md

@ -72,7 +72,7 @@ group:
| size | 设置按钮大小 | `large` \| `middle` \| `small` | `middle` | |
| styles | 语义化结构 style | Record<SemanticDOM, CSSProperties> | - | 5.4.0 |
| target | 相当于 a 链接的 target 属性,href 存在时生效 | string | - | |
| type | 设置按钮类型 | `primary` \| `ghost` \| `dashed` \| `link` \| `text` \| `default` | `default` | |
| type | 设置按钮类型 | `primary` \| `dashed` \| `link` \| `text` \| `default` | `default` | |
| onClick | 点击按钮时的回调 | (event: MouseEvent) => void | - | |
支持原生 button 的其他所有属性。

7
components/button/style/index.ts

@ -409,6 +409,13 @@ const genTypeButtonStyle: GenerateStyle<ButtonToken> = (token) => {
[`${componentCls}-dashed`]: genDashedButtonStyle(token),
[`${componentCls}-link`]: genLinkButtonStyle(token),
[`${componentCls}-text`]: genTextButtonStyle(token),
[`${componentCls}-ghost`]: genGhostButtonStyle(
token.componentCls,
token.colorBgContainer,
token.colorBgContainer,
token.colorTextDisabled,
token.colorBorder,
),
};
};

5
components/calendar/generateCalendar.tsx

@ -115,7 +115,7 @@ function generateCalendar<DateType>(generateConfig: GenerateConfig<DateType>) {
onPanelChange,
onSelect,
} = props;
const { getPrefixCls, direction } = React.useContext(ConfigContext);
const { getPrefixCls, direction, calendar } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('picker', customizePrefixCls);
const calendarPrefixCls = `${prefixCls}-calendar`;
@ -304,11 +304,12 @@ function generateCalendar<DateType>(generateConfig: GenerateConfig<DateType>) {
[`${calendarPrefixCls}-mini`]: !fullscreen,
[`${calendarPrefixCls}-rtl`]: direction === 'rtl',
},
calendar?.className,
className,
rootClassName,
hashId,
)}
style={style}
style={{ ...calendar?.style, ...style }}
>
{headerRender ? (
headerRender({

2
components/calendar/index.ts

@ -1,3 +1,5 @@
'use client';
import type { Dayjs } from 'dayjs';
import dayjsGenerateConfig from 'rc-picker/lib/generate/dayjs';
import type { CalendarProps } from './generateCalendar';

24
components/calendar/style/index.ts

@ -8,11 +8,35 @@ import type { FullToken } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
export interface ComponentToken {
/**
* @desc
* @descEN Width of year select
*/
yearControlWidth: number;
/**
* @desc
* @descEN Width of month select
*/
monthControlWidth: number;
/**
* @desc
* @descEN Height of mini calendar content
*/
miniContentHeight: number;
/**
* @desc
* @descEN Background color of full calendar
*/
fullBg: string;
/**
* @desc
* @descEN Background color of full calendar panel
*/
fullPanelBg: string;
/**
* @desc
* @descEN Background color of selected date item
*/
itemActiveBg: string;
}

19
components/card/Card.tsx

@ -118,16 +118,15 @@ const Card = React.forwardRef<HTMLDivElement, CardProps>((props, ref) => {
let head: React.ReactNode;
const mergedSize = useSize(customizeSize);
const tabSize = !mergedSize || mergedSize === 'default' ? 'large' : mergedSize;
const tabs =
tabList && tabList.length ? (
<Tabs
size={tabSize}
{...extraProps}
className={`${prefixCls}-head-tabs`}
onChange={onTabChange}
items={tabList.map(({ tab, ...item }) => ({ label: tab, ...item }))}
/>
) : null;
const tabs = tabList ? (
<Tabs
size={tabSize}
{...extraProps}
className={`${prefixCls}-head-tabs`}
onChange={onTabChange}
items={tabList.map(({ tab, ...item }) => ({ label: tab, ...item }))}
/>
) : null;
if (title || extra || tabs) {
head = (
<div className={`${prefixCls}-head`} style={headStyle}>

11
components/card/__tests__/index.test.tsx

@ -132,6 +132,17 @@ describe('Card', () => {
expect(cardRef.current).toHaveClass('ant-card');
});
it('should show tab when tabList is empty', () => {
const { container } = render(
<Card title="Card title" tabList={[]} tabProps={{ type: 'editable-card' }}>
<p>Card content</p>
</Card>,
);
expect(container.querySelector('.ant-tabs')).toBeTruthy();
expect(container.querySelector('.ant-tabs-nav-add')).toBeTruthy();
});
it('correct pass tabList props', () => {
const { container } = render(
<Card

2
components/card/index.ts

@ -1,3 +1,5 @@
'use client';
import InternalCard from './Card';
import Grid from './Grid';
import Meta from './Meta';

36
components/card/style/index.ts

@ -5,14 +5,50 @@ import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
export interface ComponentToken {
/**
* @desc
* @descEN Background color of card header
*/
headerBg: string;
/**
* @desc
* @descEN Font size of card header
*/
headerFontSize: number;
/**
* @desc
* @descEN Font size of small card header
*/
headerFontSizeSM: number;
/**
* @desc
* @descEN Height of card header
*/
headerHeight: number;
/**
* @desc
* @descEN Height of small card header
*/
headerHeightSM: number;
/**
* @desc
* @descEN Background color of card actions
*/
actionsBg: string;
/**
* @desc
* @descEN Margin of each item in card actions
*/
actionsLiMargin: string;
/**
* @desc
* @descEN Margin bottom of tabs component
*/
tabsMarginBottom: number;
/**
* @desc
* @descEN Text color of extra area
*/
extraColor: string;
}

172
components/carousel/index.tsx

@ -1,3 +1,5 @@
'use client';
import type { Settings } from '@ant-design/react-slick';
import SlickCarousel from '@ant-design/react-slick';
import classNames from 'classnames';
@ -29,94 +31,94 @@ export interface CarouselRef {
innerSlider: any;
}
const Carousel = React.forwardRef<CarouselRef, CarouselProps>(
(
{
dots = true,
arrows = false,
draggable = false,
waitForAnimate = false,
dotPosition = 'bottom',
vertical = dotPosition === 'left' || dotPosition === 'right',
rootClassName,
...props
},
const Carousel = React.forwardRef<CarouselRef, CarouselProps>((props, ref) => {
const {
dots = true,
arrows = false,
draggable = false,
waitForAnimate = false,
dotPosition = 'bottom',
vertical = dotPosition === 'left' || dotPosition === 'right',
rootClassName,
className: customClassName,
style,
...otherProps
} = props;
const { getPrefixCls, direction, carousel } = React.useContext(ConfigContext);
const slickRef = React.useRef<any>();
const goTo = (slide: number, dontAnimate = false) => {
slickRef.current.slickGoTo(slide, dontAnimate);
};
React.useImperativeHandle(
ref,
) => {
const { getPrefixCls, direction } = React.useContext(ConfigContext);
const slickRef = React.useRef<any>();
const goTo = (slide: number, dontAnimate = false) => {
slickRef.current.slickGoTo(slide, dontAnimate);
};
React.useImperativeHandle(
ref,
() => ({
goTo,
autoPlay: slickRef.current.innerSlider.autoPlay,
innerSlider: slickRef.current.innerSlider,
prev: slickRef.current.slickPrev,
next: slickRef.current.slickNext,
}),
[slickRef.current],
);
const prevCount = React.useRef(React.Children.count(props.children));
React.useEffect(() => {
if (prevCount.current !== React.Children.count(props.children)) {
goTo(props.initialSlide || 0, false);
prevCount.current = React.Children.count(props.children);
}
}, [props.children]);
const newProps = {
vertical,
...props,
};
if (newProps.effect === 'fade') {
newProps.fade = true;
() => ({
goTo,
autoPlay: slickRef.current.innerSlider.autoPlay,
innerSlider: slickRef.current.innerSlider,
prev: slickRef.current.slickPrev,
next: slickRef.current.slickNext,
}),
[slickRef.current],
);
const prevCount = React.useRef(React.Children.count(props.children));
React.useEffect(() => {
if (prevCount.current !== React.Children.count(props.children)) {
goTo(props.initialSlide || 0, false);
prevCount.current = React.Children.count(props.children);
}
}, [props.children]);
const newProps = {
vertical,
className: classNames(customClassName, carousel?.className),
style: { ...carousel?.style, ...style },
...otherProps,
};
if (newProps.effect === 'fade') {
newProps.fade = true;
}
const prefixCls = getPrefixCls('carousel', newProps.prefixCls);
const dotsClass = 'slick-dots';
const enableDots = !!dots;
const dsClass = classNames(
dotsClass,
`${dotsClass}-${dotPosition}`,
typeof dots === 'boolean' ? false : dots?.className,
);
const [wrapSSR, hashId] = useStyle(prefixCls);
const className = classNames(
prefixCls,
{
[`${prefixCls}-rtl`]: direction === 'rtl',
[`${prefixCls}-vertical`]: newProps.vertical,
},
hashId,
rootClassName,
);
return wrapSSR(
<div className={className}>
<SlickCarousel
ref={slickRef}
{...newProps}
dots={enableDots}
dotsClass={dsClass}
arrows={arrows}
draggable={draggable}
waitForAnimate={waitForAnimate}
/>
</div>,
);
},
);
const prefixCls = getPrefixCls('carousel', newProps.prefixCls);
const dotsClass = 'slick-dots';
const enableDots = !!dots;
const dsClass = classNames(
dotsClass,
`${dotsClass}-${dotPosition}`,
typeof dots === 'boolean' ? false : dots?.className,
);
const [wrapSSR, hashId] = useStyle(prefixCls);
const className = classNames(
prefixCls,
{
[`${prefixCls}-rtl`]: direction === 'rtl',
[`${prefixCls}-vertical`]: newProps.vertical,
},
hashId,
rootClassName,
);
return wrapSSR(
<div className={className}>
<SlickCarousel
ref={slickRef}
{...newProps}
dots={enableDots}
dotsClass={dsClass}
arrows={arrows}
draggable={draggable}
waitForAnimate={waitForAnimate}
/>
</div>,
);
});
if (process.env.NODE_ENV !== 'production') {
Carousel.displayName = 'Carousel';

12
components/carousel/style/index.ts

@ -3,10 +3,22 @@ import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
export interface ComponentToken {
/**
* @desc
* @descEN Width of indicator
*/
dotWidth: number;
/**
* @desc
* @descEN Height of indicator
*/
dotHeight: number;
/** @deprecated Use `dotActiveWidth` instead. */
dotWidthActive: number;
/**
* @desc
* @descEN Width of active indicator
*/
dotActiveWidth: number;
}

2
components/cascader/index.tsx

@ -1,3 +1,5 @@
'use client';
import LeftOutlined from '@ant-design/icons/LeftOutlined';
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
import RightOutlined from '@ant-design/icons/RightOutlined';

16
components/cascader/style/index.ts

@ -1,12 +1,24 @@
import { getStyle as getCheckboxStyle } from '../../checkbox/style';
import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook } from '../../theme/internal';
import { textEllipsis } from '../../style';
import { genCompactItemStyle } from '../../style/compact-item';
import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook } from '../../theme/internal';
export interface ComponentToken {
/**
* @desc
* @descEN Width of Cascader
*/
controlWidth: number;
/**
* @desc
* @descEN Width of item
*/
controlItemWidth: number;
/**
* @desc
* @descEN Height of dropdown
*/
dropdownHeight: number;
}

14
components/checkbox/Checkbox.tsx

@ -51,7 +51,10 @@ export interface CheckboxProps extends AbstractCheckboxProps<CheckboxChangeEvent
}
const InternalCheckbox: React.ForwardRefRenderFunction<CheckboxRef, CheckboxProps> = (
{
props,
ref,
) => {
const {
prefixCls: customizePrefixCls,
className,
rootClassName,
@ -63,9 +66,7 @@ const InternalCheckbox: React.ForwardRefRenderFunction<CheckboxRef, CheckboxProp
skipGroup = false,
disabled,
...restProps
},
ref,
) => {
} = props;
const { getPrefixCls, direction, checkbox } = React.useContext(ConfigContext);
const checkboxGroup = React.useContext(GroupContext);
const { isFormItemInput } = React.useContext(FormItemInputContext);
@ -112,8 +113,8 @@ const InternalCheckbox: React.ForwardRefRenderFunction<CheckboxRef, CheckboxProp
checkboxProps.checked = checkboxGroup.value.includes(restProps.value);
}
const classString = classNames(
`${prefixCls}-wrapper`,
{
[`${prefixCls}-wrapper`]: true,
[`${prefixCls}-rtl`]: direction === 'rtl',
[`${prefixCls}-wrapper-checked`]: checkboxProps.checked,
[`${prefixCls}-wrapper-disabled`]: mergedDisabled,
@ -152,7 +153,8 @@ const InternalCheckbox: React.ForwardRefRenderFunction<CheckboxRef, CheckboxProp
);
};
const Checkbox = React.forwardRef<unknown, CheckboxProps>(InternalCheckbox);
const Checkbox = React.forwardRef<CheckboxRef, CheckboxProps>(InternalCheckbox);
if (process.env.NODE_ENV !== 'production') {
Checkbox.displayName = 'Checkbox';
}

74
components/checkbox/Group.tsx

@ -36,8 +36,11 @@ export interface CheckboxGroupProps extends AbstractCheckboxGroupProps {
children?: React.ReactNode;
}
const InternalCheckboxGroup: React.ForwardRefRenderFunction<HTMLDivElement, CheckboxGroupProps> = (
{
const InternalGroup: React.ForwardRefRenderFunction<HTMLDivElement, CheckboxGroupProps> = (
props,
ref,
) => {
const {
defaultValue,
children,
options = [],
@ -47,9 +50,7 @@ const InternalCheckboxGroup: React.ForwardRefRenderFunction<HTMLDivElement, Chec
style,
onChange,
...restProps
},
ref,
) => {
} = props;
const { getPrefixCls, direction } = React.useContext(ConfigContext);
const [value, setValue] = React.useState<CheckboxValueType[]>(
@ -63,16 +64,16 @@ const InternalCheckboxGroup: React.ForwardRefRenderFunction<HTMLDivElement, Chec
}
}, [restProps.value]);
const getOptions = () =>
options.map((option) => {
if (typeof option === 'string' || typeof option === 'number') {
return {
label: option,
value: option,
};
}
return option;
});
const memoOptions = React.useMemo(
() =>
options.map<CheckboxOptionType>((option) => {
if (typeof option === 'string' || typeof option === 'number') {
return { label: option, value: option };
}
return option;
}),
[options],
);
const cancelValue = (val: string) => {
setRegisteredValues((prevValues) => prevValues.filter((v) => v !== val));
@ -93,13 +94,12 @@ const InternalCheckboxGroup: React.ForwardRefRenderFunction<HTMLDivElement, Chec
if (!('value' in restProps)) {
setValue(newValue);
}
const opts = getOptions();
onChange?.(
newValue
.filter((val) => registeredValues.includes(val))
.sort((a, b) => {
const indexA = opts.findIndex((opt) => opt.value === a);
const indexB = opts.findIndex((opt) => opt.value === b);
const indexA = memoOptions.findIndex((opt) => opt.value === a);
const indexB = memoOptions.findIndex((opt) => opt.value === b);
return indexA - indexB;
}),
);
@ -112,23 +112,23 @@ const InternalCheckboxGroup: React.ForwardRefRenderFunction<HTMLDivElement, Chec
const domProps = omit(restProps, ['value', 'disabled']);
if (options && options.length > 0) {
children = getOptions().map((option) => (
<Checkbox
prefixCls={prefixCls}
key={option.value.toString()}
disabled={'disabled' in option ? option.disabled : restProps.disabled}
value={option.value}
checked={value.includes(option.value)}
onChange={option.onChange}
className={`${groupPrefixCls}-item`}
style={option.style}
title={option.title}
>
{option.label}
</Checkbox>
));
}
const childrenNode = options.length
? memoOptions.map<React.ReactNode>((option) => (
<Checkbox
prefixCls={prefixCls}
key={option.value.toString()}
disabled={'disabled' in option ? option.disabled : restProps.disabled}
value={option.value}
checked={value.includes(option.value)}
onChange={option.onChange}
className={`${groupPrefixCls}-item`}
style={option.style}
title={option.title}
>
{option.label}
</Checkbox>
))
: children;
// eslint-disable-next-line react/jsx-no-constructed-context-values
const context = {
@ -151,7 +151,7 @@ const InternalCheckboxGroup: React.ForwardRefRenderFunction<HTMLDivElement, Chec
);
return wrapSSR(
<div className={classString} style={style} {...domProps} ref={ref}>
<GroupContext.Provider value={context}>{children}</GroupContext.Provider>
<GroupContext.Provider value={context}>{childrenNode}</GroupContext.Provider>
</div>,
);
};
@ -159,6 +159,6 @@ const InternalCheckboxGroup: React.ForwardRefRenderFunction<HTMLDivElement, Chec
export type { CheckboxGroupContext } from './GroupContext';
export { GroupContext };
const CheckboxGroup = React.forwardRef<HTMLDivElement, CheckboxGroupProps>(InternalCheckboxGroup);
const CheckboxGroup = React.forwardRef<HTMLDivElement, CheckboxGroupProps>(InternalGroup);
export default React.memo(CheckboxGroup);

4
components/checkbox/index.ts

@ -1,11 +1,13 @@
'use client';
import type * as React from 'react';
import type { CheckboxProps } from './Checkbox';
import InternalCheckbox from './Checkbox';
import Group from './Group';
export type { CheckboxRef } from 'rc-checkbox';
export type { CheckboxChangeEvent, CheckboxProps } from './Checkbox';
export type { CheckboxGroupProps, CheckboxOptionType } from './Group';
export type { CheckboxRef } from 'rc-checkbox';
type CompoundedComponent = React.ForwardRefExoticComponent<
CheckboxProps & React.RefAttributes<HTMLInputElement>

2
components/col/index.ts

@ -1,3 +1,5 @@
'use client';
import { Col, type ColProps, type ColSize } from '../grid';
export type { ColProps, ColSize };

10
components/collapse/Collapse.tsx

@ -57,12 +57,13 @@ interface PanelProps {
}
const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) => {
const { getPrefixCls, direction } = React.useContext(ConfigContext);
const { getPrefixCls, direction, collapse } = React.useContext(ConfigContext);
const {
prefixCls: customizePrefixCls,
className,
rootClassName,
style,
bordered = true,
ghost,
size: customizeSize,
@ -101,7 +102,10 @@ const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) =>
) as React.ReactNode;
return cloneElement(icon, () => ({
className: classNames((icon as any).props.className, `${prefixCls}-arrow`),
className: classNames(
(icon as React.ReactElement<any>).props.className,
`${prefixCls}-arrow`,
),
}));
};
@ -113,6 +117,7 @@ const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) =>
[`${prefixCls}-ghost`]: !!ghost,
[`${prefixCls}-${mergedSize}`]: mergedSize !== 'middle',
},
collapse?.className,
className,
rootClassName,
hashId,
@ -151,6 +156,7 @@ const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) =>
expandIcon={renderExpandIcon}
prefixCls={prefixCls}
className={collapseClassName}
style={{ ...collapse?.style, ...style }}
>
{items}
</RcCollapse>,

2
components/collapse/CollapsePanel.tsx

@ -30,7 +30,7 @@ const CollapsePanel = React.forwardRef<HTMLDivElement, CollapsePanelProps>((prop
);
const { getPrefixCls } = React.useContext(ConfigContext);
const { prefixCls: customizePrefixCls, className = '', showArrow = true } = props;
const { prefixCls: customizePrefixCls, className, showArrow = true } = props;
const prefixCls = getPrefixCls('collapse', customizePrefixCls);
const collapsePanelClassName = classNames(
{

2
components/collapse/index.ts

@ -1,3 +1,5 @@
'use client';
import Collapse from './Collapse';
export type { CollapseProps } from './Collapse';

75
components/color-picker/ColorPicker.tsx

@ -5,12 +5,15 @@ import type {
import classNames from 'classnames';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import type { CSSProperties, FC } from 'react';
import React, { useContext, useRef, useState } from 'react';
import React, { useContext, useMemo, useRef, useState } from 'react';
import genPurePanel from '../_util/PurePanel';
import { getStatusClassNames } from '../_util/statusUtils';
import warning from '../_util/warning';
import type { SizeType } from '../config-provider/SizeContext';
import type { ConfigConsumerProps } from '../config-provider/context';
import { ConfigContext } from '../config-provider/context';
import useSize from '../config-provider/hooks/useSize';
import { FormItemInputContext, NoFormStyle } from '../form/context';
import type { PopoverProps } from '../popover';
import Popover from '../popover';
import theme from '../theme';
@ -21,19 +24,20 @@ import useColorState from './hooks/useColorState';
import type {
ColorFormat,
ColorPickerBaseProps,
ColorValueType,
PresetsItem,
TriggerPlacement,
TriggerType,
} from './interface';
import useStyle from './style/index';
import { customizePrefixCls, generateColor } from './util';
import { customizePrefixCls, genAlphaColor, generateColor, getAlphaColor } from './util';
export type ColorPickerProps = Omit<
RcColorPickerProps,
'onChange' | 'value' | 'defaultValue' | 'panelRender'
'onChange' | 'value' | 'defaultValue' | 'panelRender' | 'disabledAlpha' | 'onChangeComplete'
> & {
value?: Color | string;
defaultValue?: Color | string;
value?: ColorValueType;
defaultValue?: ColorValueType;
children?: React.ReactNode;
open?: boolean;
disabled?: boolean;
@ -51,10 +55,12 @@ export type ColorPickerProps = Omit<
size?: SizeType;
styles?: { popup?: CSSProperties; popupOverlayInner?: CSSProperties };
rootClassName?: string;
disabledAlpha?: boolean;
onOpenChange?: (open: boolean) => void;
onFormatChange?: (format: ColorFormat) => void;
onChange?: (value: Color, hex: string) => void;
onClear?: () => void;
onChangeComplete?: (value: Color) => void;
} & Pick<PopoverProps, 'getPopupContainer' | 'autoAdjustOverflow' | 'destroyTooltipOnHide'>;
type CompoundedComponent = React.FC<ColorPickerProps> & {
@ -81,10 +87,12 @@ const ColorPicker: CompoundedComponent = (props) => {
size: customizeSize,
rootClassName,
styles,
disabledAlpha = false,
onFormatChange,
onChange,
onClear,
onOpenChange,
onChangeComplete,
getPopupContainer,
autoAdjustOverflow = true,
destroyTooltipOnHide,
@ -112,12 +120,18 @@ const ColorPicker: CompoundedComponent = (props) => {
const prefixCls = getPrefixCls('color-picker', customizePrefixCls);
const isAlphaColor = useMemo(() => getAlphaColor(colorValue) < 100, [colorValue]);
// ===================== Form Status =====================
const { status: contextStatus } = React.useContext(FormItemInputContext);
// ===================== Style =====================
const mergedSize = useSize(customizeSize);
const [wrapSSR, hashId] = useStyle(prefixCls);
const rtlCls = { [`${prefixCls}-rtl`]: direction };
const mergeRootCls = classNames(rootClassName, rtlCls);
const mergeCls = classNames(
getStatusClassNames(prefixCls, contextStatus),
{
[`${prefixCls}-sm`]: mergedSize === 'small',
[`${prefixCls}-lg`]: mergedSize === 'large',
@ -131,24 +145,35 @@ const ColorPicker: CompoundedComponent = (props) => {
const popupAllowCloseRef = useRef(true);
// ===================== Warning ======================
if (process.env.NODE_ENV !== 'production') {
warning(
!(disabledAlpha && isAlphaColor),
'ColorPicker',
'`disabledAlpha` will make the alpha to be 100% when use alpha color.',
);
}
const handleChange = (data: Color, type?: HsbaColorType, pickColor?: boolean) => {
let color: Color = generateColor(data);
if (colorCleared) {
const isNull = value === null || (!value && defaultValue === null);
if (colorCleared || isNull) {
setColorCleared(false);
const hsba = color.toHsb();
// ignore alpha slider
if (colorValue.toHsb().a === 0 && type !== 'alpha') {
hsba.a = 1;
color = generateColor(hsba);
if (getAlphaColor(colorValue) === 0 && type !== 'alpha') {
color = genAlphaColor(color);
}
}
if (!value) {
setColorValue(color);
// ignore alpha color
if (disabledAlpha && isAlphaColor) {
color = genAlphaColor(color);
}
// Only for drag-and-drop color picking
if (pickColor) {
popupAllowCloseRef.current = false;
}
setColorValue(color);
onChange?.(color, color.toHexString());
};
@ -157,8 +182,14 @@ const ColorPicker: CompoundedComponent = (props) => {
onClear?.();
};
const handleChangeComplete = () => {
const handleChangeComplete: ColorPickerProps['onChangeComplete'] = (color) => {
popupAllowCloseRef.current = true;
let changeColor = generateColor(color);
// ignore alpha color
if (disabledAlpha && isAlphaColor) {
changeColor = genAlphaColor(color);
}
onChangeComplete?.(changeColor);
};
const popoverProps: PopoverProps = {
@ -178,10 +209,12 @@ const ColorPicker: CompoundedComponent = (props) => {
allowClear,
colorCleared,
disabled,
disabledAlpha,
presets,
panelRender,
format: formatValue,
onFormatChange: setFormatValue,
onChangeComplete: handleChangeComplete,
};
const mergedStyle: React.CSSProperties = { ...colorPicker?.style, ...style };
@ -196,12 +229,14 @@ const ColorPicker: CompoundedComponent = (props) => {
}
}}
content={
<ColorPickerPanel
{...colorBaseProps}
onChange={handleChange}
onChangeComplete={handleChangeComplete}
onClear={handleClear}
/>
<NoFormStyle override status>
<ColorPickerPanel
{...colorBaseProps}
onChange={handleChange}
onChangeComplete={handleChangeComplete}
onClear={handleClear}
/>
</NoFormStyle>
}
overlayClassName={mergePopupCls}
{...popoverProps}
@ -211,7 +246,7 @@ const ColorPicker: CompoundedComponent = (props) => {
open={popupOpen}
className={mergeCls}
style={mergedStyle}
color={colorValue}
color={value ? generateColor(value) : colorValue}
prefixCls={prefixCls}
disabled={disabled}
colorCleared={colorCleared}

13
components/color-picker/ColorPickerPanel.tsx

@ -10,21 +10,11 @@ import type { ColorPickerBaseProps } from './interface';
interface ColorPickerPanelProps extends ColorPickerBaseProps {
onChange?: (value?: Color, type?: HsbaColorType, pickColor?: boolean) => void;
onChangeComplete?: (type?: HsbaColorType) => void;
onClear?: () => void;
}
const ColorPickerPanel: FC<ColorPickerPanelProps> = (props) => {
const {
prefixCls,
presets,
panelRender,
color,
onChange,
onClear,
onChangeComplete,
...injectProps
} = props;
const { prefixCls, presets, panelRender, color, onChange, onClear, ...injectProps } = props;
const colorPickerPanelPrefixCls = `${prefixCls}-inner-content`;
// ==== Inject props ===
@ -33,7 +23,6 @@ const ColorPickerPanel: FC<ColorPickerPanelProps> = (props) => {
value: color,
onChange,
onClear,
onChangeComplete,
...injectProps,
};

693
components/color-picker/__tests__/__snapshots__/demo-extend.test.ts.snap

@ -767,8 +767,10 @@ Array [
]
`;
exports[`renders components/color-picker/demo/controlled.tsx extend context correctly 1`] = `
Array [
exports[`renders components/color-picker/demo/change-completed.tsx extend context correctly 1`] = `
<div
class="ant-app"
>
<div
class="ant-color-picker-trigger"
>
@ -780,7 +782,7 @@ Array [
style="background: rgb(22, 119, 255);"
/>
</div>
</div>,
</div>
<div
class="ant-popover ant-zoom-big-appear ant-zoom-big-appear-prepare ant-zoom-big ant-color-picker ant-popover-placement-bottomLeft"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
@ -1145,14 +1147,14 @@ Array [
</div>
</div>
</div>
</div>,
]
</div>
</div>
`;
exports[`renders components/color-picker/demo/disabled.tsx extend context correctly 1`] = `
exports[`renders components/color-picker/demo/controlled.tsx extend context correctly 1`] = `
Array [
<div
class="ant-color-picker-trigger ant-color-picker-trigger-disabled"
class="ant-color-picker-trigger"
>
<div
class="ant-color-picker-color-block"
@ -1162,11 +1164,6 @@ Array [
style="background: rgb(22, 119, 255);"
/>
</div>
<div
class="ant-color-picker-trigger-text"
>
#1677FF
</div>
</div>,
<div
class="ant-popover ant-zoom-big-appear ant-zoom-big-appear-prepare ant-zoom-big ant-color-picker ant-popover-placement-bottomLeft"
@ -1536,6 +1533,678 @@ Array [
]
`;
exports[`renders components/color-picker/demo/disabled.tsx extend context correctly 1`] = `
Array [
<div
class="ant-color-picker-trigger ant-color-picker-trigger-disabled"
>
<div
class="ant-color-picker-color-block"
>
<div
class="ant-color-picker-color-block-inner"
style="background: rgb(22, 119, 255);"
/>
</div>
<div
class="ant-color-picker-trigger-text"
>
#1677FF
</div>
</div>,
<div
class="ant-popover ant-zoom-big-appear ant-zoom-big-appear-prepare ant-zoom-big ant-color-picker ant-popover-placement-bottomLeft"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-popover-arrow"
style="position: absolute;"
/>
<div
class="ant-popover-content"
>
<div
class="ant-popover-inner"
role="tooltip"
>
<div
class="ant-popover-inner-content"
>
<div
class="ant-color-picker-inner-content"
>
<div
class="ant-color-picker-panel"
>
<div
class="ant-color-picker-select"
>
<div
class="ant-color-picker-palette"
style="position: relative;"
>
<div
style="position: absolute; left: 0px; top: 0px; z-index: 1;"
>
<div
class="ant-color-picker-handler"
style="background-color: rgb(22, 119, 255);"
/>
</div>
<div
class="ant-color-picker-saturation"
style="background-color: rgb(0, 0, 0);"
/>
</div>
</div>
<div
class="ant-color-picker-slider-container"
>
<div
class="ant-color-picker-slider-group"
>
<div
class="ant-color-picker-slider ant-color-picker-slider-hue"
>
<div
class="ant-color-picker-palette"
style="position: relative;"
>
<div
style="position: absolute; left: 0px; top: 0px; z-index: 1;"
>
<div
class="ant-color-picker-handler ant-color-picker-handler-sm"
style="background-color: rgb(0, 0, 0);"
/>
</div>
<div
class="ant-color-picker-gradient"
style="position: absolute; inset: 0;"
/>
</div>
</div>
<div
class="ant-color-picker-slider ant-color-picker-slider-alpha"
>
<div
class="ant-color-picker-palette"
style="position: relative;"
>
<div
style="position: absolute; left: 0px; top: 0px; z-index: 1;"
>
<div
class="ant-color-picker-handler ant-color-picker-handler-sm"
style="background-color: rgb(22, 119, 255);"
/>
</div>
<div
class="ant-color-picker-gradient"
style="position: absolute; inset: 0;"
/>
</div>
</div>
</div>
<div
class="ant-color-picker-color-block"
>
<div
class="ant-color-picker-color-block-inner"
style="background: rgb(22, 119, 255);"
/>
</div>
</div>
</div>
<div
class="ant-color-picker-input-container"
>
<div
class="ant-select ant-select-sm ant-select-borderless ant-color-picker-format-select ant-select-single ant-select-show-arrow"
>
<div
class="ant-select-selector"
>
<span
class="ant-select-selection-search"
>
<input
aria-activedescendant="rc_select_TEST_OR_SSR_list_0"
aria-autocomplete="list"
aria-controls="rc_select_TEST_OR_SSR_list"
aria-expanded="false"
aria-haspopup="listbox"
aria-owns="rc_select_TEST_OR_SSR_list"
autocomplete="off"
class="ant-select-selection-search-input"
id="rc_select_TEST_OR_SSR"
readonly=""
role="combobox"
style="opacity: 0;"
type="search"
unselectable="on"
value=""
/>
</span>
<span
class="ant-select-selection-item"
title="HEX"
>
HEX
</span>
</div>
<div
class="ant-select-dropdown ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up ant-select-dropdown-placement-bottomRight"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box; width: 68px;"
>
<div>
<div
id="rc_select_TEST_OR_SSR_list"
role="listbox"
style="height: 0px; width: 0px; overflow: hidden;"
>
<div
aria-label="HEX"
aria-selected="true"
id="rc_select_TEST_OR_SSR_list_0"
role="option"
>
hex
</div>
<div
aria-label="HSB"
aria-selected="false"
id="rc_select_TEST_OR_SSR_list_1"
role="option"
>
hsb
</div>
</div>
<div
class="rc-virtual-list"
style="position: relative;"
>
<div
class="rc-virtual-list-holder"
style="max-height: 256px; overflow-y: auto;"
>
<div>
<div
class="rc-virtual-list-holder-inner"
style="display: flex; flex-direction: column;"
>
<div
aria-selected="true"
class="ant-select-item ant-select-item-option ant-select-item-option-active ant-select-item-option-selected"
title="HEX"
>
<div
class="ant-select-item-option-content"
>
HEX
</div>
<span
aria-hidden="true"
class="ant-select-item-option-state"
style="user-select: none;"
unselectable="on"
/>
</div>
<div
aria-selected="false"
class="ant-select-item ant-select-item-option"
title="HSB"
>
<div
class="ant-select-item-option-content"
>
HSB
</div>
<span
aria-hidden="true"
class="ant-select-item-option-state"
style="user-select: none;"
unselectable="on"
/>
</div>
<div
aria-selected="false"
class="ant-select-item ant-select-item-option"
title="RGB"
>
<div
class="ant-select-item-option-content"
>
RGB
</div>
<span
aria-hidden="true"
class="ant-select-item-option-state"
style="user-select: none;"
unselectable="on"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<span
aria-hidden="true"
class="ant-select-arrow"
style="user-select: none;"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-select-suffix"
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>
<div
class="ant-color-picker-input"
>
<span
class="ant-input-affix-wrapper ant-color-picker-hex-input ant-input-affix-wrapper-sm"
>
<span
class="ant-input-prefix"
>
#
</span>
<input
class="ant-input ant-input-sm"
type="text"
value="1677FF"
/>
</span>
</div>
<div
class="ant-input-number ant-input-number-sm ant-color-picker-steppers ant-color-picker-alpha-input"
>
<div
class="ant-input-number-handler-wrap"
>
<span
aria-disabled="true"
aria-label="Increase Value"
class="ant-input-number-handler ant-input-number-handler-up ant-input-number-handler-up-disabled"
role="button"
unselectable="on"
>
<span
aria-label="up"
class="anticon anticon-up ant-input-number-handler-up-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
/>
</svg>
</span>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="ant-input-number-handler ant-input-number-handler-down"
role="button"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-input-number-handler-down-inner"
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>
<div
class="ant-input-number-input-wrap"
>
<input
aria-valuemax="100"
aria-valuemin="0"
aria-valuenow="100"
autocomplete="off"
class="ant-input-number-input"
role="spinbutton"
step="1"
value="100%"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>,
]
`;
exports[`renders components/color-picker/demo/disabled-alpha.tsx extend context correctly 1`] = `
Array [
<div
class="ant-color-picker-trigger"
>
<div
class="ant-color-picker-color-block"
>
<div
class="ant-color-picker-color-block-inner"
style="background: rgb(22, 119, 255);"
/>
</div>
</div>,
<div
class="ant-popover ant-zoom-big-appear ant-zoom-big-appear-prepare ant-zoom-big ant-color-picker ant-popover-placement-bottomLeft"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-popover-arrow"
style="position: absolute;"
/>
<div
class="ant-popover-content"
>
<div
class="ant-popover-inner"
role="tooltip"
>
<div
class="ant-popover-inner-content"
>
<div
class="ant-color-picker-inner-content"
>
<div
class="ant-color-picker-panel"
>
<div
class="ant-color-picker-select"
>
<div
class="ant-color-picker-palette"
style="position: relative;"
>
<div
style="position: absolute; left: 0px; top: 0px; z-index: 1;"
>
<div
class="ant-color-picker-handler"
style="background-color: rgb(22, 119, 255);"
/>
</div>
<div
class="ant-color-picker-saturation"
style="background-color: rgb(0, 0, 0);"
/>
</div>
</div>
<div
class="ant-color-picker-slider-container"
>
<div
class="ant-color-picker-slider-group ant-color-picker-slider-group-disabled-alpha"
>
<div
class="ant-color-picker-slider ant-color-picker-slider-hue"
>
<div
class="ant-color-picker-palette"
style="position: relative;"
>
<div
style="position: absolute; left: 0px; top: 0px; z-index: 1;"
>
<div
class="ant-color-picker-handler ant-color-picker-handler-sm"
style="background-color: rgb(0, 0, 0);"
/>
</div>
<div
class="ant-color-picker-gradient"
style="position: absolute; inset: 0;"
/>
</div>
</div>
</div>
<div
class="ant-color-picker-color-block"
>
<div
class="ant-color-picker-color-block-inner"
style="background: rgb(22, 119, 255);"
/>
</div>
</div>
</div>
<div
class="ant-color-picker-input-container"
>
<div
class="ant-select ant-select-sm ant-select-borderless ant-color-picker-format-select ant-select-single ant-select-show-arrow"
>
<div
class="ant-select-selector"
>
<span
class="ant-select-selection-search"
>
<input
aria-activedescendant="rc_select_TEST_OR_SSR_list_0"
aria-autocomplete="list"
aria-controls="rc_select_TEST_OR_SSR_list"
aria-expanded="false"
aria-haspopup="listbox"
aria-owns="rc_select_TEST_OR_SSR_list"
autocomplete="off"
class="ant-select-selection-search-input"
id="rc_select_TEST_OR_SSR"
readonly=""
role="combobox"
style="opacity: 0;"
type="search"
unselectable="on"
value=""
/>
</span>
<span
class="ant-select-selection-item"
title="HEX"
>
HEX
</span>
</div>
<div
class="ant-select-dropdown ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up ant-select-dropdown-placement-bottomRight"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box; width: 68px;"
>
<div>
<div
id="rc_select_TEST_OR_SSR_list"
role="listbox"
style="height: 0px; width: 0px; overflow: hidden;"
>
<div
aria-label="HEX"
aria-selected="true"
id="rc_select_TEST_OR_SSR_list_0"
role="option"
>
hex
</div>
<div
aria-label="HSB"
aria-selected="false"
id="rc_select_TEST_OR_SSR_list_1"
role="option"
>
hsb
</div>
</div>
<div
class="rc-virtual-list"
style="position: relative;"
>
<div
class="rc-virtual-list-holder"
style="max-height: 256px; overflow-y: auto;"
>
<div>
<div
class="rc-virtual-list-holder-inner"
style="display: flex; flex-direction: column;"
>
<div
aria-selected="true"
class="ant-select-item ant-select-item-option ant-select-item-option-active ant-select-item-option-selected"
title="HEX"
>
<div
class="ant-select-item-option-content"
>
HEX
</div>
<span
aria-hidden="true"
class="ant-select-item-option-state"
style="user-select: none;"
unselectable="on"
/>
</div>
<div
aria-selected="false"
class="ant-select-item ant-select-item-option"
title="HSB"
>
<div
class="ant-select-item-option-content"
>
HSB
</div>
<span
aria-hidden="true"
class="ant-select-item-option-state"
style="user-select: none;"
unselectable="on"
/>
</div>
<div
aria-selected="false"
class="ant-select-item ant-select-item-option"
title="RGB"
>
<div
class="ant-select-item-option-content"
>
RGB
</div>
<span
aria-hidden="true"
class="ant-select-item-option-state"
style="user-select: none;"
unselectable="on"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<span
aria-hidden="true"
class="ant-select-arrow"
style="user-select: none;"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-select-suffix"
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>
<div
class="ant-color-picker-input"
>
<span
class="ant-input-affix-wrapper ant-color-picker-hex-input ant-input-affix-wrapper-sm"
>
<span
class="ant-input-prefix"
>
#
</span>
<input
class="ant-input ant-input-sm"
type="text"
value="1677FF"
/>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>,
]
`;
exports[`renders components/color-picker/demo/format.tsx extend context correctly 1`] = `
<div
class="ant-space ant-space-vertical"

34
components/color-picker/__tests__/__snapshots__/demo.test.ts.snap

@ -30,6 +30,25 @@ exports[`renders components/color-picker/demo/base.tsx correctly 1`] = `
</div>
`;
exports[`renders components/color-picker/demo/change-completed.tsx correctly 1`] = `
<div
class="ant-app"
>
<div
class="ant-color-picker-trigger"
>
<div
class="ant-color-picker-color-block"
>
<div
class="ant-color-picker-color-block-inner"
style="background:rgb(22, 119, 255)"
/>
</div>
</div>
</div>
`;
exports[`renders components/color-picker/demo/controlled.tsx correctly 1`] = `
<div
class="ant-color-picker-trigger"
@ -65,6 +84,21 @@ exports[`renders components/color-picker/demo/disabled.tsx correctly 1`] = `
</div>
`;
exports[`renders components/color-picker/demo/disabled-alpha.tsx correctly 1`] = `
<div
class="ant-color-picker-trigger"
>
<div
class="ant-color-picker-color-block"
>
<div
class="ant-color-picker-color-block-inner"
style="background:rgb(22, 119, 255)"
/>
</div>
</div>
`;
exports[`renders components/color-picker/demo/format.tsx correctly 1`] = `
<div
class="ant-space ant-space-vertical"

116
components/color-picker/__tests__/index.test.tsx

@ -4,8 +4,11 @@ import React, { useMemo, useState } from 'react';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { waitFakeTimer } from '../../../tests/utils';
import { resetWarned } from '../../_util/warning';
import ConfigProvider from '../../config-provider';
import Form from '../../form';
import theme from '../../theme';
import type { ColorPickerProps } from '../ColorPicker';
import ColorPicker from '../ColorPicker';
import type { Color } from '../color';
@ -34,11 +37,14 @@ function doMouseMove(
describe('ColorPicker', () => {
mountTest(ColorPicker);
rtlTest(ColorPicker);
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
beforeEach(() => {
resetWarned();
jest.useFakeTimers();
});
afterEach(() => {
errorSpy.mockReset();
jest.useRealTimers();
});
@ -55,7 +61,7 @@ describe('ColorPicker', () => {
});
it('Should component custom trigger work', async () => {
const App = () => {
const App: React.FC = () => {
const [color, setColor] = useState<Color | string>('hsb(215, 91%, 100%)');
const colorString = useMemo(
() => (typeof color === 'string' ? color : color.toHsbString()),
@ -397,4 +403,112 @@ describe('ColorPicker', () => {
expect(componentContainer.querySelector('.ant-color-picker-inner-content')).toBeTruthy();
expect(componentContainer).toMatchSnapshot();
});
it('Should null work as expect', async () => {
spyElementPrototypes(HTMLElement, {
getBoundingClientRect: () => ({
x: 0,
y: 100,
width: 100,
height: 100,
}),
});
const { container } = render(<ColorPicker value={null} open />);
expect(
container.querySelector('.ant-color-picker-alpha-input input')?.getAttribute('value'),
).toEqual('0%');
expect(
container.querySelector('.ant-color-picker-hex-input input')?.getAttribute('value'),
).toEqual('000000');
doMouseMove(container, 0, 999);
expect(
container.querySelector('.ant-color-picker-alpha-input input')?.getAttribute('value'),
).toEqual('100%');
});
it('should support valid in form', async () => {
const Demo = () => {
const [form] = Form.useForm();
const submit = () => {
form.validateFields();
};
return (
<Form form={form} initialValues={{ 'color-picker': null }}>
<Form.Item
name="color-picker"
label="ColorPicker"
rules={[{ required: true, message: 'color is required!' }]}
>
<ColorPicker />
</Form.Item>
<button type="button" onClick={submit}>
submit
</button>
</Form>
);
};
const { container } = render(<Demo />);
expect(container.querySelector('.ant-color-picker-status-error')).toBeFalsy();
fireEvent.click(container.querySelector('button')!);
await waitFakeTimer();
expect(container.querySelector('.ant-color-picker-status-error')).toBeTruthy();
expect(container.querySelector('.ant-form-item-explain-error')?.innerHTML).toEqual(
'color is required!',
);
});
it('Should onChangeComplete work', async () => {
const handleChangeComplete = jest.fn();
const { container } = render(<ColorPicker open onChangeComplete={handleChangeComplete} />);
doMouseMove(container, 0, 999);
expect(handleChangeComplete).toHaveBeenCalledTimes(1);
});
it('Should disabledAlpha work', async () => {
const { container } = render(<ColorPicker open disabledAlpha />);
expect(container.querySelector('.ant-color-picker-slider-group-disabled-alpha')).toBeTruthy();
expect(container.querySelector('.ant-color-picker-slider-alpha')).toBeFalsy();
expect(container.querySelector('.ant-color-picker-alpha-input')).toBeFalsy();
});
it('Should disabledAlpha work with value', async () => {
spyElementPrototypes(HTMLElement, {
getBoundingClientRect: () => ({
x: 0,
y: 100,
width: 100,
height: 100,
}),
});
const Demo = () => {
const [value, setValue] = useState<ColorPickerProps['value']>('#1677ff86');
const [changedValue, setChangedValue] = useState<ColorPickerProps['value']>('#1677ff86');
return (
<ColorPicker
open
disabledAlpha
value={value}
onChange={setValue}
onChangeComplete={setChangedValue}
>
<div className="color-value">
{typeof value === 'string' ? value : value?.toHexString()}
</div>
<div className="color-value-changed">
{typeof changedValue === 'string' ? changedValue : changedValue?.toHexString()}
</div>
</ColorPicker>
);
};
const { container } = render(<Demo />);
expect(container.querySelector('.color-value')?.innerHTML).toEqual('#1677ff86');
doMouseMove(container, 0, 999);
expect(container.querySelector('.color-value')?.innerHTML).toEqual('#000000');
expect(container.querySelector('.color-value-changed')?.innerHTML).toEqual('#000000');
});
it('Should warning work when set disabledAlpha true and color is alpha color', () => {
render(<ColorPicker disabledAlpha value="#1677ff" />);
expect(errorSpy).not.toHaveBeenCalled();
});
});

3
components/color-picker/color.ts

@ -19,6 +19,9 @@ export class ColorFactory {
constructor(color: ColorGenInput<Color>) {
this.metaColor = new RcColor(color as ColorGenInput);
if (!color) {
this.metaColor.setAlpha(0);
}
}
toHsb() {

8
components/color-picker/components/ColorInput.tsx

@ -11,7 +11,7 @@ import ColorHsbInput from './ColorHsbInput';
import ColorRgbInput from './ColorRgbInput';
interface ColorInputProps
extends Pick<ColorPickerBaseProps, 'prefixCls' | 'format' | 'onFormatChange'> {
extends Pick<ColorPickerBaseProps, 'prefixCls' | 'format' | 'onFormatChange' | 'disabledAlpha'> {
value?: Color;
onChange?: (value: Color) => void;
}
@ -22,7 +22,7 @@ const selectOptions = [ColorFormat.hex, ColorFormat.hsb, ColorFormat.rgb].map((f
}));
const ColorInput: FC<ColorInputProps> = (props) => {
const { prefixCls, format, value, onFormatChange, onChange } = props;
const { prefixCls, format, value, disabledAlpha, onFormatChange, onChange } = props;
const [colorFormat, setColorFormat] = useMergedState(ColorFormat.hex, {
value: format,
onChange: onFormatChange,
@ -61,7 +61,9 @@ const ColorInput: FC<ColorInputProps> = (props) => {
options={selectOptions}
/>
<div className={colorInputPrefixCls}>{steppersNode}</div>
<ColorAlphaInput prefixCls={prefixCls} value={value} onChange={onChange} />
{!disabledAlpha && (
<ColorAlphaInput prefixCls={prefixCls} value={value} onChange={onChange} />
)}
</div>
);
};

16
components/color-picker/components/PanelPicker.tsx

@ -9,10 +9,12 @@ import ColorClear from './ColorClear';
import ColorInput from './ColorInput';
export interface PanelPickerProps
extends Pick<ColorPickerBaseProps, 'prefixCls' | 'colorCleared' | 'allowClear'> {
extends Pick<
ColorPickerBaseProps,
'prefixCls' | 'colorCleared' | 'allowClear' | 'disabledAlpha' | 'onChangeComplete'
> {
value?: Color;
onChange?: (value?: Color, type?: HsbaColorType, pickColor?: boolean) => void;
onChangeComplete?: (type?: HsbaColorType) => void;
onClear?: () => void;
}
@ -22,6 +24,7 @@ const PanelPicker: FC = () => {
colorCleared,
allowClear,
value,
disabledAlpha,
onChange,
onClear,
onChangeComplete,
@ -44,10 +47,17 @@ const PanelPicker: FC = () => {
<RcColorPicker
prefixCls={prefixCls}
value={value?.toHsb()}
disabledAlpha={disabledAlpha}
onChange={(colorValue, type) => onChange?.(colorValue, type, true)}
onChangeComplete={onChangeComplete}
/>
<ColorInput value={value} onChange={onChange} prefixCls={prefixCls} {...injectProps} />
<ColorInput
value={value}
onChange={onChange}
prefixCls={prefixCls}
disabledAlpha={disabledAlpha}
{...injectProps}
/>
</>
);
};

8
components/color-picker/components/PanelPresets.tsx

@ -12,11 +12,9 @@ export interface PanelPresetsProps extends Pick<ColorPickerBaseProps, 'prefixCls
const PanelPresets: FC = () => {
const { prefixCls, value, presets, onChange } = useContext(PanelPresetsContext);
return (
Array.isArray(presets) && (
<ColorPresets value={value} presets={presets} prefixCls={prefixCls} onChange={onChange} />
)
);
return Array.isArray(presets) ? (
<ColorPresets value={value} presets={presets} prefixCls={prefixCls} onChange={onChange} />
) : null;
};
export default PanelPresets;

7
components/color-picker/demo/change-completed.md

@ -0,0 +1,7 @@
## zh-CN
颜色选择完成才会触发回调
## en-US
The callback will be called only when the color selection is completed.

21
components/color-picker/demo/change-completed.tsx

@ -0,0 +1,21 @@
import { App, ColorPicker } from 'antd';
import type { ColorPickerProps } from 'antd/es/color-picker';
import React, { useState } from 'react';
const Demo = () => {
const { message } = App.useApp();
const [value, setValue] = useState<ColorPickerProps['value']>('#1677ff');
return (
<App>
<ColorPicker
value={value}
onChangeComplete={(color) => {
setValue(color);
message.success(`The selected color is ${color.toHexString()}`);
}}
/>
</App>
);
};
export default Demo;

7
components/color-picker/demo/disabled-alpha.md

@ -0,0 +1,7 @@
## zh-CN
禁用颜色透明度。
## en-US
Disabled color alpha.

6
components/color-picker/demo/disabled-alpha.tsx

@ -0,0 +1,6 @@
import { ColorPicker } from 'antd';
import React from 'react';
const Demo = () => <ColorPicker disabledAlpha />;
export default Demo;

9
components/color-picker/hooks/useColorState.ts

@ -1,18 +1,19 @@
import { useEffect, useState } from 'react';
import type { Color } from '../color';
import type { ColorValueType } from '../interface';
import { generateColor } from '../util';
function hasValue(value?: Color | string) {
function hasValue(value?: ColorValueType) {
return value !== undefined;
}
const useColorState = (
defaultStateValue: Color | string,
option: { defaultValue?: Color | string; value?: Color | string },
defaultStateValue: ColorValueType,
option: { defaultValue?: ColorValueType; value?: ColorValueType },
): readonly [Color, React.Dispatch<React.SetStateAction<Color>>] => {
const { defaultValue, value } = option;
const [colorValue, setColorValue] = useState<Color>(() => {
let mergeState: string | Color | undefined;
let mergeState: ColorValueType | undefined;
if (hasValue(value)) {
mergeState = value;
} else if (hasValue(defaultValue)) {

4
components/color-picker/index.en-US.md

@ -22,8 +22,10 @@ Used when the user needs to customize the color selection.
<code src="./demo/base.tsx">Basic Usage</code>
<code src="./demo/size.tsx">Trigger size</code>
<code src="./demo/controlled.tsx">controlled mode</code>
<code src="./demo/change-completed.tsx">Color change completed</code>
<code src="./demo/text-render.tsx">Rendering Trigger Text</code>
<code src="./demo/disabled.tsx">Disable</code>
<code src="./demo/disabled-alpha.tsx">Disabled Alpha</code>
<code src="./demo/allowClear.tsx">Clear Color</code>
<code src="./demo/trigger.tsx">Custom Trigger</code>
<code src="./demo/trigger-event.tsx">Custom Trigger Event</code>
@ -44,6 +46,7 @@ Used when the user needs to customize the color selection.
| children | Trigger of ColorPicker | React.ReactNode | - | |
| defaultValue | Default value of color | string \| `Color` | - | |
| disabled | Disable ColorPicker | boolean | - | |
| disabledAlpha | Disable Alpha | boolean | - | 5.8.0 |
| destroyTooltipOnHide | Whether destroy popover when hidden | `boolean` | false | 5.7.0 |
| format | Format of color | `rgb` \| `hex` \| `hsb` | `hex` | |
| open | Whether to show popup | boolean | - | |
@ -55,6 +58,7 @@ Used when the user needs to customize the color selection.
| trigger | ColorPicker trigger mode | `hover` \| `click` | `click` | |
| value | Value of color | string \| `Color` | - | |
| onChange | Callback when `value` is changed | `(value: Color, hex: string) => void` | - | |
| onChangeComplete | Called when color pick ends | `(value: Color) => void` | - | 5.7.0 |
| 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` | - | 5.6.0 |

4
components/color-picker/index.ts

@ -1,6 +1,8 @@
'use client';
import ColorPicker from './ColorPicker';
export type { Color } from './color';
export type { ColorPickerProps } from './ColorPicker';
export type { Color } from './color';
export default ColorPicker;

4
components/color-picker/index.zh-CN.md

@ -23,8 +23,10 @@ group:
<code src="./demo/base.tsx">基本使用</code>
<code src="./demo/size.tsx">触发器尺寸大小</code>
<code src="./demo/controlled.tsx">受控模式</code>
<code src="./demo/change-completed.tsx">颜色完成选择</code>
<code src="./demo/text-render.tsx">渲染触发器文本</code>
<code src="./demo/disabled.tsx">禁用</code>
<code src="./demo/disabled-alpha.tsx">禁用透明度</code>
<code src="./demo/allowClear.tsx">清除颜色</code>
<code src="./demo/trigger.tsx">自定义触发器</code>
<code src="./demo/trigger-event.tsx">自定义触发事件</code>
@ -45,6 +47,7 @@ group:
| children | 颜色选择器的触发器 | React.ReactNode | - | |
| defaultValue | 颜色默认的值 | string \| `Color` | - | |
| disabled | 禁用颜色选择器 | boolean | - | |
| disabledAlpha | 禁用透明度 | boolean | - | 5.8.0 |
| destroyTooltipOnHide | 关闭后是否销毁弹窗 | `boolean` | false | 5.7.0 |
| format | 颜色格式 | `rgb` \| `hex` \| `hsb` | `hex` | |
| open | 是否显示弹出窗口 | boolean | - | |
@ -56,6 +59,7 @@ group:
| trigger | 颜色选择器的触发模式 | `hover` \| `click` | `click` | |
| value | 颜色的值 | string \| `Color` | - | |
| onChange | 颜色变化的回调 | `(value: Color, hex: string) => void` | - | |
| onChangeComplete | 颜色选择完成的回调 | `(value: Color) => void` | - | 5.7.0 |
| onFormatChange | 颜色格式变化的回调 | `(format: 'hex' \| 'rgb' \| 'hsb') => void` | - | |
| onOpenChange | 当 `open` 被改变时的回调 | `(open: boolean) => void` | - | |
| onClear | 清除的回调 | `() => void` | - | 5.6.0 |

4
components/color-picker/interface.ts

@ -28,7 +28,11 @@ export interface ColorPickerBaseProps {
allowClear?: boolean;
colorCleared?: boolean;
disabled?: boolean;
disabledAlpha?: boolean;
presets?: PresetsItem[];
panelRender?: ColorPickerProps['panelRender'];
onFormatChange?: ColorPickerProps['onFormatChange'];
onChangeComplete?: ColorPickerProps['onChangeComplete'];
}
export type ColorValueType = Color | string | null;

40
components/color-picker/style/index.ts

@ -77,6 +77,45 @@ const genClearStyle = (
};
};
const genStatusStyle = (token: ColorPickerToken): CSSObject => {
const {
componentCls,
colorError,
colorWarning,
colorErrorBorderHover,
colorWarningBorderHover,
colorErrorOutline,
colorWarningOutline,
} = token;
return {
[`&${componentCls}-status-error`]: {
borderColor: colorError,
'&:hover': {
borderColor: colorErrorBorderHover,
},
[`&${componentCls}-trigger-active`]: {
...genActiveStyle(
mergeToken<ColorPickerToken>(token, {
controlOutline: colorErrorOutline,
}),
),
},
},
[`&${componentCls}-status-warning`]: {
borderColor: colorWarning,
'&:hover': {
borderColor: colorWarningBorderHover,
},
[`&${componentCls}-trigger-active`]: {
...genActiveStyle(
mergeToken<ColorPickerToken>(token, {
controlOutline: colorWarningOutline,
}),
),
},
},
};
};
const genSizeStyle = (token: ColorPickerToken): CSSObject => {
const {
componentCls,
@ -202,6 +241,7 @@ const genColorPickerStyle: GenerateStyle<ColorPickerToken> = (token) => {
},
...genClearStyle(token, controlHeightSM),
...genColorBlockStyle(token, controlHeightSM),
...genStatusStyle(token),
...genSizeStyle(token),
},
...genRtlStyle(token),

11
components/color-picker/style/picker.ts

@ -58,14 +58,23 @@ const genPickerStyle: GenerateStyle<ColorPickerToken, CSSObject> = (token) => {
boxShadow: colorPickerInsetShadow,
},
'&-alpha': getTransBg(`${colorPickerSliderHeight}px`, token.colorFillSecondary),
marginBottom: marginSM,
'&-hue': { marginBottom: marginSM },
},
[`${componentCls}-slider-container`]: {
display: 'flex',
gap: marginSM,
marginBottom: marginSM,
[`${componentCls}-slider-group`]: {
flex: 1,
'&-disabled-alpha': {
display: 'flex',
alignItems: 'center',
[`${componentCls}-slider`]: {
flex: 1,
marginBottom: 0,
},
},
},
},
};

6
components/color-picker/util.ts

@ -14,3 +14,9 @@ export const generateColor = (color: ColorGenInput<Color>): Color => {
export const getRoundNumber = (value: number) => Math.round(Number(value || 0));
export const getAlphaColor = (color: Color) => getRoundNumber(color.toHsb().a * 100);
export const genAlphaColor = (color: Color, alpha?: number) => {
const hsba = color.toHsb();
hsba.a = alpha || 1;
return generateColor(hsba);
};

36
components/config-provider/__tests__/form.test.tsx

@ -148,6 +148,42 @@ describe('ConfigProvider.Form', () => {
'age must be between 18-99',
);
});
// https://github.com/ant-design/ant-design/issues/43210
it('should merge parent ConfigProvider validateMessages', async () => {
const MyForm = () => (
<Form>
<Form.Item name="name" label="Name" rules={[{ required: true }]}>
<Input />
</Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form>
);
const { container, getAllByRole, getAllByText } = render(
<ConfigProvider>
<MyForm />
<ConfigProvider form={{ validateMessages: { required: 'Required' } }}>
<MyForm />
<ConfigProvider>
<MyForm />
</ConfigProvider>
</ConfigProvider>
</ConfigProvider>,
);
const submitButtons = getAllByRole('button');
expect(submitButtons).toHaveLength(3);
submitButtons.forEach(fireEvent.click);
await waitFakeTimer();
expect(container.querySelectorAll('.ant-form-item-explain-error')).toHaveLength(3);
expect(getAllByText('Please enter Name')).toHaveLength(1);
expect(getAllByText('Required')).toHaveLength(2);
});
});
describe('form requiredMark', () => {

483
components/config-provider/__tests__/style.test.tsx

@ -1,39 +1,53 @@
import React from 'react';
import ConfigProvider from '..';
import { fireEvent, render } from '../../../tests/utils';
import Alert from '../../alert';
import Anchor from '../../anchor';
import Avatar from '../../avatar';
import Badge from '../../badge';
import Breadcrumb from '../../breadcrumb';
import Calendar from '../../calendar';
import Card from '../../card';
import Carousel from '../../carousel';
import Cascader from '../../cascader';
import Checkbox from '../../checkbox';
import Collapse from '../../collapse';
import ColorPicker from '../../color-picker';
import DatePicker from '../../date-picker';
import Descriptions from '../../descriptions';
import Divider from '../../divider';
import Drawer from '../../drawer';
import Empty from '../../empty';
import Form from '../../form';
import Image from '../../image';
import Input from '../../input';
import Layout from '../../layout';
import List from '../../list';
import Mentions from '../../mentions';
import Menu from '../../menu';
import message from '../../message';
import Modal from '../../modal';
import notification from '../../notification';
import Pagination from '../../pagination';
import Progress from '../../progress';
import Radio from '../../radio';
import Rate from '../../rate';
import Result from '../../result';
import Segmented from '../../segmented';
import Select from '../../select';
import Skeleton from '../../skeleton';
import Slider from '../../slider';
import Space from '../../space';
import Spin from '../../spin';
import Statistic from '../../statistic';
import Steps from '../../steps';
import Switch from '../../switch';
import Table from '../../table';
import Tabs from '../../tabs';
import Tag from '../../tag';
import TimePicker from '../../time-picker';
import Timeline from '../../timeline';
import Transfer from '../../transfer';
import Tree from '../../tree';
import Typography from '../../typography';
import Upload from '../../upload';
@ -142,6 +156,71 @@ describe('ConfigProvider support style and className props', () => {
expect(container.querySelector('.ant-divider'))?.toHaveStyle({ color: 'red', height: '80px' });
});
it('Should Drawer className works', () => {
render(
<ConfigProvider
drawer={{
className: 'test-class',
}}
>
<Drawer title="Test Drawer" open />
</ConfigProvider>,
);
expect(document.querySelector('.ant-drawer-content')).toHaveClass('test-class');
});
it('Should Drawer style works', () => {
render(
<ConfigProvider
drawer={{
style: { color: 'red' },
}}
>
<Drawer title="Test Drawer" style={{ fontSize: '16px' }} open />
</ConfigProvider>,
);
expect(document.querySelector('.ant-drawer-content')).toHaveStyle(
'color: red; font-size: 16px;',
);
});
it('Should Carousel className works', () => {
const { container } = render(
<ConfigProvider
carousel={{
className: 'test-class',
}}
>
<Carousel>
<div>
<h3>test item</h3>
</div>
</Carousel>
</ConfigProvider>,
);
expect(container.querySelector('.slick-slider')).toHaveClass('test-class');
});
it('Should Carousel style works', () => {
const { container } = render(
<ConfigProvider carousel={{ style: { color: 'red' } }}>
<Carousel style={{ fontSize: '16px' }}>
<div>
<h3>test item 1</h3>
</div>
<div>
<h3>test item 2</h3>
</div>
</Carousel>
</ConfigProvider>,
);
expect(container.querySelector('.slick-slider')).toHaveStyle('color: red; font-size: 16px;');
});
it('Should Cascader className & style works', () => {
const options = [
{
@ -189,6 +268,48 @@ describe('ConfigProvider support style and className props', () => {
expect(element).toHaveStyle({ backgroundColor: 'red' });
});
it('Should Collapse className works', () => {
const items = [
{
key: '1',
label: 'test label',
children: <p>item</p>,
},
];
const { container } = render(
<ConfigProvider
collapse={{
className: 'test-class',
}}
>
<Collapse items={items} />
</ConfigProvider>,
);
expect(container.querySelector('.ant-collapse')).toHaveClass('test-class');
});
it('Should Collapse style works', () => {
const items = [
{
key: '1',
label: 'test label',
children: <p>item</p>,
},
];
const { container } = render(
<ConfigProvider
collapse={{
style: { color: 'red' },
}}
>
<Collapse items={items} style={{ fontSize: '16px' }} />
</ConfigProvider>,
);
expect(container.querySelector('.ant-collapse')).toHaveStyle('color: red; font-size: 16px;');
});
it('Should Typography className & style works', () => {
const { container } = render(
<ConfigProvider
@ -202,6 +323,34 @@ describe('ConfigProvider support style and className props', () => {
expect(element).toHaveStyle({ backgroundColor: 'red' });
});
it('Should Skeleton className works', () => {
const { container } = render(
<ConfigProvider
skeleton={{
className: 'test-class',
}}
>
<Skeleton />
</ConfigProvider>,
);
expect(container.querySelector('.ant-skeleton')).toHaveClass('test-class');
});
it('Should Skeleton style works', () => {
const { container } = render(
<ConfigProvider
skeleton={{
style: { color: 'red' },
}}
>
<Skeleton style={{ fontSize: '16px' }} />
</ConfigProvider>,
);
expect(container.querySelector('.ant-skeleton')).toHaveStyle('color: red; font-size: 16px;');
});
it('Should Spin className & style works', () => {
const { container } = render(
<ConfigProvider
@ -215,6 +364,34 @@ describe('ConfigProvider support style and className props', () => {
expect(element).toHaveStyle({ backgroundColor: 'red' });
});
it('Should Statistic className works', () => {
const { container } = render(
<ConfigProvider
statistic={{
className: 'test-class',
}}
>
<Statistic title="Test Title" value={100} />
</ConfigProvider>,
);
expect(container.querySelector('.ant-statistic')).toHaveClass('test-class');
});
it('Should Statistic style works', () => {
const { container } = render(
<ConfigProvider
statistic={{
style: { color: 'red' },
}}
>
<Statistic style={{ fontSize: '16px' }} title="Test Title" value={100} />
</ConfigProvider>,
);
expect(container.querySelector('.ant-statistic')).toHaveStyle('color: red; font-size: 16px;');
});
it('Should Segmented className & style works', () => {
const { container } = render(
<ConfigProvider
@ -348,6 +525,84 @@ describe('ConfigProvider support style and className props', () => {
expect(element).toHaveStyle({ background: 'red' });
});
it('Should List className works', () => {
const listData = [
{
title: 'Test Title',
},
];
const { container } = render(
<ConfigProvider
list={{
className: 'test-class',
}}
>
<List dataSource={listData} />
</ConfigProvider>,
);
expect(container.querySelector('.ant-list')).toHaveClass('test-class');
});
it('Should List style works', () => {
const listData = [
{
title: 'Test Title',
},
];
const { container } = render(
<ConfigProvider
list={{
style: { color: 'red' },
}}
>
<List style={{ fontSize: '16px' }} dataSource={listData} />
</ConfigProvider>,
);
expect(container.querySelector('.ant-list')).toHaveStyle('color: red; font-size: 16px;');
});
it('Should Menu className works', () => {
const menuItems = [
{
label: 'Test Label',
key: 'test',
},
];
const { container } = render(
<ConfigProvider
menu={{
className: 'test-class',
}}
>
<Menu items={menuItems} />
</ConfigProvider>,
);
expect(container.querySelector('.ant-menu')).toHaveClass('test-class');
});
it('Should Menu style works', () => {
const menuItems = [
{
label: 'Test Label',
key: 'test',
},
];
const { container } = render(
<ConfigProvider
menu={{
style: { color: 'red' },
}}
>
<Menu items={menuItems} style={{ fontSize: '16px' }} />
</ConfigProvider>,
);
expect(container.querySelector('.ant-menu')).toHaveStyle('color: red; font-size: 16px;');
});
it('Should Mentions className & style works', () => {
const { container } = render(
<ConfigProvider
@ -445,6 +700,34 @@ describe('ConfigProvider support style and className props', () => {
expect(element).toHaveStyle({ backgroundColor: 'red' });
});
it('Should Alert className works', () => {
const { container } = render(
<ConfigProvider
alert={{
className: 'test-class',
}}
>
<Alert message="Test Message" />
</ConfigProvider>,
);
expect(container.querySelector('.ant-alert')).toHaveClass('test-class');
});
it('Should Alert style works', () => {
const { container } = render(
<ConfigProvider
alert={{
style: { color: 'red' },
}}
>
<Alert style={{ fontSize: '16px' }} message="Test Message" />
</ConfigProvider>,
);
expect(container.querySelector('.ant-alert')).toHaveStyle('color: red; font-size: 16px;');
});
it('Should Anchor className & style works', () => {
const { container } = render(
<ConfigProvider
@ -520,6 +803,34 @@ describe('ConfigProvider support style and className props', () => {
expect(element).toHaveStyle({ backgroundColor: 'blue' });
});
it('Should Progress className works', () => {
const { container } = render(
<ConfigProvider
progress={{
className: 'test-class',
}}
>
<Progress />
</ConfigProvider>,
);
expect(container.querySelector('.ant-progress')).toHaveClass('test-class');
});
it('Should Progress style works', () => {
const { container } = render(
<ConfigProvider
progress={{
style: { color: 'red' },
}}
>
<Progress style={{ fontSize: '16px' }} />
</ConfigProvider>,
);
expect(container.querySelector('.ant-progress')).toHaveStyle('color: red; font-size: 16px;');
});
it('Should Descriptions className & style works', () => {
const { container } = render(
<ConfigProvider
@ -649,6 +960,36 @@ describe('ConfigProvider support style and className props', () => {
expect(element).toHaveStyle({ backgroundColor: 'blue' });
});
it('Should Calendar className works', () => {
const { container } = render(
<ConfigProvider
calendar={{
className: 'test-class',
}}
>
<Calendar />
</ConfigProvider>,
);
expect(container.querySelector('.ant-picker-calendar')).toHaveClass('test-class');
});
it('Should Calendar style works', () => {
const { container } = render(
<ConfigProvider
calendar={{
style: { color: 'red' },
}}
>
<Calendar style={{ fontSize: '16px' }} />
</ConfigProvider>,
);
expect(container.querySelector('.ant-picker-calendar')).toHaveStyle(
'color: red; font-size: 16px;',
);
});
it('Should Card className & style works', () => {
const { container } = render(
<ConfigProvider card={{ className: 'cp-card', style: { backgroundColor: 'blue' } }}>
@ -671,6 +1012,34 @@ describe('ConfigProvider support style and className props', () => {
expect(element).toHaveStyle({ backgroundColor: 'red' });
});
it('Should TimePicker className works', () => {
const { container } = render(
<ConfigProvider
timePicker={{
className: 'test-class',
}}
>
<TimePicker />
</ConfigProvider>,
);
expect(container.querySelector('.ant-picker')).toHaveClass('test-class');
});
it('Should TimePicker style works', () => {
const { container } = render(
<ConfigProvider
timePicker={{
style: { color: 'red' },
}}
>
<TimePicker style={{ fontSize: '16px' }} />
</ConfigProvider>,
);
expect(container.querySelector('.ant-picker')).toHaveStyle('color: red; font-size: 16px;');
});
it('Should message className & style works', () => {
const Demo: React.FC = () => {
const [messageApi, contextHolder] = message.useMessage();
@ -724,6 +1093,92 @@ describe('ConfigProvider support style and className props', () => {
expect(element).toHaveStyle({ color: 'blue' });
});
it('Should Timeline className works', () => {
const items = [
{
children: 'test item',
},
];
const { container } = render(
<ConfigProvider
timeline={{
className: 'test-class',
}}
>
<Timeline items={items} />
</ConfigProvider>,
);
expect(container.querySelector('.ant-timeline')).toHaveClass('test-class');
});
it('Should Timeline style works', () => {
const items = [
{
children: 'test item',
},
];
const { container } = render(
<ConfigProvider
timeline={{
style: { color: 'red' },
}}
>
<Timeline items={items} style={{ fontSize: '16px' }} />
</ConfigProvider>,
);
expect(container.querySelector('.ant-timeline')).toHaveStyle('color: red; font-size: 16px;');
});
it('Should Transfer className works', () => {
const mockData = [
{
key: '0-0',
title: `content`,
description: `description of content`,
},
];
const { container } = render(
<ConfigProvider
transfer={{
className: 'test-class',
}}
>
<Transfer dataSource={mockData} />
</ConfigProvider>,
);
expect(container.querySelector('.ant-transfer')).toHaveClass('test-class');
});
it('Should Transfer style works', () => {
const mockData = [
{
key: '0-0',
title: `content`,
description: `description of content`,
},
];
const { container } = render(
<ConfigProvider
transfer={{
style: {
color: 'red',
},
}}
>
<Transfer dataSource={mockData} style={{ fontSize: '16px' }} />
</ConfigProvider>,
);
expect(container.querySelector('.ant-transfer')).toHaveStyle('color: red; font-size: 16px;');
});
it('Should Tree className works', () => {
const treeData = [
{
@ -780,4 +1235,32 @@ describe('ConfigProvider support style and className props', () => {
expect(element).toHaveClass('cp-colorPicker');
expect(element).toHaveStyle({ backgroundColor: 'red' });
});
it('Should DatePicker className works', () => {
const { container } = render(
<ConfigProvider
datePicker={{
className: 'test-class',
}}
>
<DatePicker />
</ConfigProvider>,
);
expect(container.querySelector('.ant-picker')).toHaveClass('test-class');
});
it('Should DatePicker style works', () => {
const { container } = render(
<ConfigProvider
datePicker={{
style: { color: 'red' },
}}
>
<DatePicker style={{ fontSize: '16px' }} />
</ConfigProvider>,
);
expect(container.querySelector('.ant-picker')).toHaveStyle('color: red; font-size: 16px;');
});
});

16
components/config-provider/context.ts

@ -1,4 +1,5 @@
import type { DerivativeFunc } from '@ant-design/cssinjs';
import type { ValidateMessages } from 'rc-field-form/lib/interface';
import * as React from 'react';
import type { Options } from 'scroll-into-view-if-needed';
import type { BadgeProps } from '../badge';
@ -89,26 +90,37 @@ export interface ConfigConsumerProps {
requiredMark?: RequiredMark;
colon?: boolean;
scrollToFirstError?: Options | boolean;
validateMessages?: ValidateMessages;
};
theme?: ThemeConfig;
select?: ComponentStyleConfig & {
showSearch?: boolean;
};
alert?: ComponentStyleConfig;
anchor?: ComponentStyleConfig;
button?: ButtonConfig;
divider?: ComponentStyleConfig;
drawer?: ComponentStyleConfig;
calendar?: ComponentStyleConfig;
carousel?: ComponentStyleConfig;
cascader?: ComponentStyleConfig;
collapse?: ComponentStyleConfig;
typography?: ComponentStyleConfig;
skeleton?: ComponentStyleConfig;
spin?: ComponentStyleConfig;
segmented?: ComponentStyleConfig;
steps?: ComponentStyleConfig;
statistic?: ComponentStyleConfig;
image?: ComponentStyleConfig;
layout?: ComponentStyleConfig;
list?: ComponentStyleConfig;
mentions?: ComponentStyleConfig;
modal?: ComponentStyleConfig;
progress?: ComponentStyleConfig;
result?: ComponentStyleConfig;
slider?: ComponentStyleConfig;
breadcrumb?: ComponentStyleConfig;
menu?: ComponentStyleConfig;
checkbox?: ComponentStyleConfig;
descriptions?: ComponentStyleConfig;
empty?: ComponentStyleConfig;
@ -116,16 +128,20 @@ export interface ConfigConsumerProps {
radio?: ComponentStyleConfig;
rate?: ComponentStyleConfig;
switch?: ComponentStyleConfig;
transfer?: ComponentStyleConfig;
avatar?: ComponentStyleConfig;
message?: ComponentStyleConfig;
tag?: ComponentStyleConfig;
table?: ComponentStyleConfig;
card?: ComponentStyleConfig;
tabs?: ComponentStyleConfig;
timeline?: ComponentStyleConfig;
timePicker?: ComponentStyleConfig;
upload?: ComponentStyleConfig;
notification?: ComponentStyleConfig;
tree?: ComponentStyleConfig;
colorPicker?: ComponentStyleConfig;
datePicker?: ComponentStyleConfig;
}
const defaultGetPrefixCls = (suffixCls?: string, customizePrefixCls?: string) => {

14
components/config-provider/index.en-US.md

@ -101,40 +101,54 @@ const {
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| alert | Set Alert common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| anchor | Set Anchor common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| avatar | Set Avatar common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| badge | Set Badge common props | { className?: string, style?: React.CSSProperties, classNames?: { count?: string, indicator?: string }, styles?: { count?: React.CSSProperties, indicator?: React.CSSProperties } } | - | 5.7.0 |
| breadcrumb | Set Breadcrumb common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| button | Set Button common props | { className?: string, style?: React.CSSProperties, classNames?: { icon: string }, styles?: { icon: React.CSSProperties } } | - | 5.6.0 |
| card | Set Card common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| calendar | Set Calendar common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| carousel | Set Carousel common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| cascader | Set Cascader common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| checkbox | Set Checkbox common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| collapse | Set Collapse common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| colorPicker | Set ColorPicker common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| datePicker | Set datePicker common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| descriptions | Set Descriptions common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| divider | Set Divider common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| drawer | Set Drawer common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| empty | Set Empty common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| form | Set Form common props | { className?: string, style?: React.CSSProperties, validateMessages?: [ValidateMessages](/components/form/#validatemessages), requiredMark?: boolean \| `optional`, scrollToFirstError?: boolean \| [Options](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options) } | - | requiredMark: 4.8.0; colon: 4.18.0; scrollToFirstError: 5.2.0; className: 5.7.0; style: 5.7.0 |
| image | Set Image common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| input | Set Input common props | { autoComplete?: string, className?: string, style?: React.CSSProperties } | - | 4.2.0 |
| layout | Set Layout common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| list | Set List common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| menu | Set Menu common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| mentions | Set Mentions common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| message | Set Message common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| modal | Set Modal common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| notification | Set Notification common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| pagination | Set Pagination common props | { showSizeChanger?: boolean, className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| progress | Set Progress common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| radio | Set Radio common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| rate | Set Rate common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| result | Set Result common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| skeleton | Set Skeleton common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| segmented | Set Segmented common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| select | Set Select common props | { className?: string, showSearch?: boolean, style?: React.CSSProperties } | - | 5.7.0 |
| slider | Set Slider common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| switch | Set Switch common props | { className?: string, style?: React.CSSProperties } | - | 5.7.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 |
| spin | Set Spin common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| statistic | Set Statistic common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| steps | Set Steps common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| table | Set Table common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| tabs | Set Tabs common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| tag | Set Tag common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| timeline | Set Timeline common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| timePicker | Set TimePicker common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| transfer | Set Transfer common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| tree | Set Tree common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| typography | Set Typography common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| upload | Set Upload common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |

45
components/config-provider/index.tsx

@ -1,3 +1,5 @@
'use client';
import { createTheme } from '@ant-design/cssinjs';
import IconContext from '@ant-design/icons/lib/components/Context';
import type { ValidateMessages } from 'rc-field-form/lib/interface';
@ -138,21 +140,31 @@ export interface ConfigProviderProps {
popupMatchSelectWidth?: boolean;
popupOverflow?: PopupOverflow;
theme?: ThemeConfig;
alert?: ComponentStyleConfig;
anchor?: ComponentStyleConfig;
button?: ButtonConfig;
calendar?: ComponentStyleConfig;
carousel?: ComponentStyleConfig;
cascader?: ComponentStyleConfig;
collapse?: ComponentStyleConfig;
divider?: ComponentStyleConfig;
drawer?: ComponentStyleConfig;
typography?: ComponentStyleConfig;
skeleton?: ComponentStyleConfig;
spin?: ComponentStyleConfig;
segmented?: ComponentStyleConfig;
statistic?: ComponentStyleConfig;
steps?: ComponentStyleConfig;
image?: ComponentStyleConfig;
layout?: ComponentStyleConfig;
list?: ComponentStyleConfig;
mentions?: ComponentStyleConfig;
modal?: ComponentStyleConfig;
progress?: ComponentStyleConfig;
result?: ComponentStyleConfig;
slider?: ComponentStyleConfig;
breadcrumb?: ComponentStyleConfig;
menu?: ComponentStyleConfig;
checkbox?: ComponentStyleConfig;
descriptions?: ComponentStyleConfig;
empty?: ComponentStyleConfig;
@ -160,16 +172,20 @@ export interface ConfigProviderProps {
radio?: ComponentStyleConfig;
rate?: ComponentStyleConfig;
switch?: ComponentStyleConfig;
transfer?: ComponentStyleConfig;
avatar?: ComponentStyleConfig;
message?: ComponentStyleConfig;
tag?: ComponentStyleConfig;
table?: ComponentStyleConfig;
card?: ComponentStyleConfig;
tabs?: ComponentStyleConfig;
timeline?: ComponentStyleConfig;
timePicker?: ComponentStyleConfig;
upload?: ComponentStyleConfig;
notification?: ComponentStyleConfig;
tree?: ComponentStyleConfig;
colorPicker?: ComponentStyleConfig;
datePicker?: ComponentStyleConfig;
}
interface ProviderChildrenProps extends ConfigProviderProps {
@ -245,6 +261,7 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
children,
csp: customCsp,
autoInsertSpaceInButton,
alert,
anchor,
form,
locale,
@ -261,20 +278,29 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
theme,
componentDisabled,
segmented,
statistic,
spin,
calendar,
carousel,
cascader,
collapse,
typography,
checkbox,
descriptions,
divider,
drawer,
skeleton,
steps,
image,
layout,
list,
mentions,
modal,
progress,
result,
slider,
breadcrumb,
menu,
pagination,
input,
empty,
@ -282,16 +308,20 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
radio,
rate,
switch: SWITCH,
transfer,
avatar,
message,
tag,
table,
card,
tabs,
timeline,
timePicker,
upload,
notification,
tree,
colorPicker,
datePicker,
} = props;
// =================================== Warning ===================================
@ -334,6 +364,7 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
const baseConfig = {
csp,
autoInsertSpaceInButton,
alert,
anchor,
locale: locale || legacyLocale,
direction,
@ -345,37 +376,50 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
iconPrefixCls,
theme: mergedTheme,
segmented,
statistic,
spin,
calendar,
carousel,
cascader,
collapse,
typography,
checkbox,
descriptions,
divider,
drawer,
skeleton,
steps,
image,
input,
layout,
list,
mentions,
modal,
progress,
result,
slider,
breadcrumb,
menu,
pagination,
empty,
badge,
radio,
rate,
switch: SWITCH,
transfer,
avatar,
message,
tag,
table,
card,
tabs,
timeline,
timePicker,
upload,
notification,
tree,
colorPicker,
datePicker,
};
const config = {
@ -423,6 +467,7 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
merge(
defaultLocale.Form?.defaultValidateMessages || {},
memoedConfig.locale?.Form?.defaultValidateMessages || {},
memoedConfig.form?.validateMessages || {},
form?.validateMessages || {},
),
[memoedConfig, form?.validateMessages],

14
components/config-provider/index.zh-CN.md

@ -103,40 +103,54 @@ const {
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| alert | 设置 Alert 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| anchor | 设置 Anchor 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| avatar | 设置 Avatar 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| badge | 设置 Badge 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: { count?: string, indicator?: string }, styles?: { count?: React.CSSProperties, indicator?: React.CSSProperties } } | - | 5.7.0 |
| breadcrumb | 设置 Breadcrumb 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| button | 设置 Button 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: { icon: string }, styles?: { icon: React.CSSProperties } } | - | 5.6.0 |
| calendar | 设置 Calendar 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| card | 设置 Card 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| carousel | 设置 Carousel 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| cascader | 设置 Cascader 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| checkbox | 设置 Checkbox 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| collapse | 设置 Collapse 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| colorPicker | 设置 ColorPicker 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| datePicker | 设置 DatePicker 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| descriptions | 设置 Descriptions 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| divider | 设置 Divider 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| drawer | 设置 Drawer 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| empty | 设置 Empty 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| form | 设置 Form 组件的通用属性 | { className?: string, style?: React.CSSProperties, validateMessages?: [ValidateMessages](/components/form-cn#validatemessages), requiredMark?: boolean \| `optional`, colon?: boolean, scrollToFirstError?: boolean \| [Options](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options)} | - | requiredMark: 4.8.0; colon: 4.18.0; scrollToFirstError: 5.2.0; className: 5.7.0; style: 5.7.0 |
| image | 设置 Image 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| input | 设置 Input 组件的通用属性 | { autoComplete?: string, className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| layout | 设置 Layout 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| list | 设置 List 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| menu | 设置 Menu 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| mentions | 设置 Mentions 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| message | 设置 Message 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| modal | 设置 Modal 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| notification | 设置 Notification 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| pagination | 设置 Pagination 组件的通用属性 | { showSizeChanger?: boolean, className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| progress | 设置 Progress 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| radio | 设置 Radio 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| rate | 设置 Rate 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| result | 设置 Result 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| skeleton | 设置 Skeleton 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| segmented | 设置 Segmented 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| select | 设置 Select 组件的通用属性 | { className?: string, showSearch?: boolean, style?: React.CSSProperties } | - | 5.7.0 |
| slider | 设置 Slider 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| switch | 设置 Switch 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.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 |
| spin | 设置 Spin 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| statistic | 设置 Statistic 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| steps | 设置 Steps 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| table | 设置 Table 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| tabs | 设置 Tabs 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| tag | 设置 Tag 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| timeline | 设置 Timeline 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| timePicker | 设置 TimePicker 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| transfer | 设置 Transfer 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| tree | 设置 Tree 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| typography | 设置 Typography 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| upload | 设置 Upload 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |

2
components/date-picker/__tests__/DatePicker.test.tsx

@ -21,7 +21,7 @@ jest.mock('@rc-component/trigger', () => {
const h: typeof React = jest.requireActual('react');
return {
default: h.forwardRef<unknown, TriggerProps>((props, ref) => {
default: h.forwardRef<HTMLElement, TriggerProps>((props, ref) => {
triggerProps = props;
return h.createElement(Trigger, { ref, ...props });
}),

30
components/date-picker/__tests__/__snapshots__/demo-extend.test.ts.snap

@ -28480,36 +28480,6 @@ exports[`renders components/date-picker/demo/mode.tsx extend context correctly 1
</ul>
</div>
</div>
<div
class="ant-picker-footer"
>
<ul
class="ant-picker-ranges"
>
<li
class="ant-picker-now"
>
<a
class="ant-picker-now-btn"
>
Now
</a>
</li>
<li
class="ant-picker-ok"
>
<button
class="ant-btn ant-btn-primary ant-btn-sm"
disabled=""
type="button"
>
<span>
OK
</span>
</button>
</li>
</ul>
</div>
</div>
</div>
</div>

15
components/date-picker/generatePicker/generateSinglePicker.tsx

@ -37,11 +37,13 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
picker?: PickerMode,
displayName?: string,
) {
const consumerName = displayName === 'TimePicker' ? 'timePicker' : 'datePicker';
const Picker = forwardRef<DatePickRef<DateType> | CommonPickerMethods, InnerPickerProps>(
(props, ref) => {
const {
prefixCls: customizePrefixCls,
getPopupContainer: customizeGetPopupContainer,
style,
className,
rootClassName,
size: customizeSize,
@ -55,7 +57,14 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
...restProps
} = props;
const { getPrefixCls, direction, getPopupContainer } = useContext(ConfigContext);
const {
getPrefixCls,
direction,
getPopupContainer,
// Consume different styles according to different names
[consumerName]: consumerStyle,
} = useContext(ConfigContext);
const prefixCls = getPrefixCls('picker', customizePrefixCls);
const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction);
const innerRef = React.useRef<RCPicker<DateType>>(null);
@ -147,15 +156,17 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
[`${prefixCls}-borderless`]: !bordered,
},
getStatusClassNames(
prefixCls as string,
prefixCls,
getMergedStatus(contextStatus, customStatus),
hasFeedback,
),
hashId,
compactItemClassnames,
consumerStyle?.className,
className,
rootClassName,
)}
style={{ ...consumerStyle?.style, ...style }}
prefixCls={prefixCls}
getPopupContainer={customizeGetPopupContainer || getPopupContainer}
generateConfig={generateConfig}

2
components/date-picker/index.ts

@ -1,3 +1,5 @@
'use client';
import type { Dayjs } from 'dayjs';
import dayjsGenerateConfig from 'rc-picker/lib/generate/dayjs';
import genPurePanel from '../_util/PurePanel';

12
components/date-picker/style/index.ts

@ -23,8 +23,20 @@ import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import type { TokenWithCommonCls } from '../../theme/util/genComponentStyleHook';
export interface ComponentToken {
/**
* @desc
* @descEN Width of preset area
*/
presetsWidth: number;
/**
* @desc
* @descEN Max width of preset area
*/
presetsMaxWidth: number;
/**
* @desc z-index
* @descEN z-index of popup
*/
zIndexPopup: number;
}

27
components/descriptions/Cell.tsx

@ -19,19 +19,20 @@ export interface CellProps {
colon?: boolean;
}
const Cell: React.FC<CellProps> = ({
itemPrefixCls,
component,
span,
className,
style,
labelStyle,
contentStyle,
bordered,
label,
content,
colon,
}) => {
const Cell: React.FC<CellProps> = (props) => {
const {
itemPrefixCls,
component,
span,
className,
style,
labelStyle,
contentStyle,
bordered,
label,
content,
colon,
} = props;
const Component = component as any;
if (bordered) {

2
components/descriptions/index.en-US.md

@ -33,7 +33,7 @@ Commonly displayed on the details page.
| --- | --- | --- | --- | --- |
| bordered | Whether to display the border | boolean | false | |
| colon | Change default props `colon` value of Descriptions.Item | boolean | true | |
| column | The number of `DescriptionItems` in a row,could be a number or a object like `{ xs: 8, sm: 16, md: 24}`,(Only set `bordered={true}` to take effect) | number | 3 | |
| column | The number of `DescriptionItems` in a row,could be a number or a object like `{ xs: 8, sm: 16, md: 24}`,(Only set `bordered={true}` to take effect) | number \| [Record<Breakpoint, number>](https://github.com/ant-design/ant-design/blob/84ca0d23ae52e4f0940f20b0e22eabe743f90dca/components/descriptions/index.tsx#L111C21-L111C56) | 3 | |
| contentStyle | Customize content style | CSSProperties | - | 4.10.0 |
| extra | The action area of the description list, placed at the top-right | ReactNode | - | 4.5.0 |
| labelStyle | Customize label style | CSSProperties | - | 4.10.0 |

43
components/descriptions/index.tsx

@ -1,3 +1,5 @@
'use client';
/* eslint-disable react/no-array-index-key */
import classNames from 'classnames';
import toArray from 'rc-util/lib/Children/toArray';
@ -92,6 +94,10 @@ function getRows(children: React.ReactNode, column: number) {
return rows;
}
interface CompoundedComponent {
Item: typeof DescriptionsItem;
}
export interface DescriptionsProps {
prefixCls?: string;
className?: string;
@ -109,23 +115,24 @@ export interface DescriptionsProps {
contentStyle?: React.CSSProperties;
}
function Descriptions({
prefixCls: customizePrefixCls,
title,
extra,
column = DEFAULT_COLUMN_MAP,
colon = true,
bordered,
layout,
children,
className,
rootClassName,
style,
size: customizeSize,
labelStyle,
contentStyle,
...restProps
}: DescriptionsProps) {
const Descriptions: React.FC<DescriptionsProps> & CompoundedComponent = (props) => {
const {
prefixCls: customizePrefixCls,
title,
extra,
column = DEFAULT_COLUMN_MAP,
colon = true,
bordered,
layout,
children,
className,
rootClassName,
style,
size: customizeSize,
labelStyle,
contentStyle,
...restProps
} = props;
const { getPrefixCls, direction, descriptions } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('descriptions', customizePrefixCls);
const [screens, setScreens] = React.useState<ScreenMap>({});
@ -202,7 +209,7 @@ function Descriptions({
</div>
</DescriptionsContext.Provider>,
);
}
};
if (process.env.NODE_ENV !== 'production') {
Descriptions.displayName = 'Descriptions';

2
components/descriptions/index.zh-CN.md

@ -34,7 +34,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*d27AQJrowGAAAA
| --- | --- | --- | --- | --- |
| bordered | 是否展示边框 | boolean | false | |
| colon | 配置 `Descriptions.Item``colon` 的默认值 | boolean | true | |
| column | 一行的 `DescriptionItems` 数量,可以写成像素值或支持响应式的对象写法 `{ xs: 8, sm: 16, md: 24}` | number | 3 | |
| column | 一行的 `DescriptionItems` 数量,可以写成像素值或支持响应式的对象写法 `{ xs: 8, sm: 16, md: 24}` | number \| [Record<Breakpoint, number>](https://github.com/ant-design/ant-design/blob/84ca0d23ae52e4f0940f20b0e22eabe743f90dca/components/descriptions/index.tsx#L111C21-L111C56) | 3 | |
| contentStyle | 自定义内容样式 | CSSProperties | - | 4.10.0 |
| extra | 描述列表的操作区域,显示在右上方 | ReactNode | - | 4.5.0 |
| labelStyle | 自定义标签样式 | CSSProperties | - | 4.10.0 |

24
components/descriptions/style/index.ts

@ -6,11 +6,35 @@ import { genComponentStyleHook, mergeToken } from '../../theme/internal';
/** Component only token. Which will handle additional calculation of alias token */
export interface ComponentToken {
// Component token here
/**
* @desc
* @descEN Background color of label
*/
labelBg: string;
/**
* @desc
* @descEN Bottom margin of title
*/
titleMarginBottom: number;
/**
* @desc
* @descEN Bottom padding of item
*/
itemPaddingBottom: number;
/**
* @desc
* @descEN Right margin of colon
*/
colonMarginRight: number;
/**
* @desc
* @descEN Left margin of colon
*/
colonMarginLeft: number;
/**
* @desc
* @descEN Text color of extra area
*/
extraColor: string;
}

2
components/divider/index.tsx

@ -1,3 +1,5 @@
'use client';
import classNames from 'classnames';
import * as React from 'react';
import warning from '../_util/warning';

18
components/drawer/index.tsx

@ -1,18 +1,20 @@
'use client';
import classNames from 'classnames';
import RcDrawer from 'rc-drawer';
import type { DrawerProps as RcDrawerProps } from 'rc-drawer';
import RcDrawer from 'rc-drawer';
import type { CSSMotionProps } from 'rc-motion';
import * as React from 'react';
import { ConfigContext } from '../config-provider';
import { NoFormStyle } from '../form/context';
import { getTransitionName } from '../_util/motion';
import warning from '../_util/warning';
import DrawerPanel from './DrawerPanel';
import { ConfigContext } from '../config-provider';
import { NoFormStyle } from '../form/context';
import type { DrawerPanelProps } from './DrawerPanel';
import DrawerPanel from './DrawerPanel';
// CSSINJS
import useStyle from './style';
import { NoCompactStyle } from '../space/Compact';
import useStyle from './style';
const SizeTypes = ['default', 'large'] as const;
type sizeType = typeof SizeTypes[number];
@ -51,6 +53,8 @@ function Drawer(props: DrawerProps) {
onClose,
prefixCls: customizePrefixCls,
getContainer: customizeGetContainer,
style,
className,
// Deprecated
visible,
@ -59,7 +63,7 @@ function Drawer(props: DrawerProps) {
...rest
} = props;
const { getPopupContainer, getPrefixCls, direction } = React.useContext(ConfigContext);
const { getPopupContainer, getPrefixCls, direction, drawer } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('drawer', customizePrefixCls);
// Style
@ -141,6 +145,8 @@ function Drawer(props: DrawerProps) {
push={push}
width={mergedWidth}
height={mergedHeight}
style={{ ...drawer?.style, ...style }}
className={classNames(drawer?.className, className)}
rootClassName={drawerClassName}
getContainer={getContainer}
afterOpenChange={afterOpenChange ?? afterVisibleChange}

12
components/drawer/style/index.ts

@ -3,8 +3,20 @@ import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import genMotionStyle from './motion';
export interface ComponentToken {
/**
* @desc z-index
* @descEN z-index of drawer
*/
zIndexPopup: number;
/**
* @desc
* @descEN Horizontal padding of footer
*/
footerPaddingBlock: number;
/**
* @desc
* @descEN Vertical padding of footer
*/
footerPaddingInline: number;
}

2
components/dropdown/__tests__/index.test.tsx

@ -15,7 +15,7 @@ jest.mock('@rc-component/trigger', () => {
const h: typeof React = jest.requireActual('react');
return {
default: h.forwardRef<unknown, TriggerProps>((props, ref) => {
default: h.forwardRef<HTMLElement, TriggerProps>((props, ref) => {
triggerProps = props;
return h.createElement(Trigger, { ref, ...props });
}),

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

Loading…
Cancel
Save