Browse Source

chore: merge master into feature

pull/43342/head
栗嘉男 1 year ago
parent
commit
60a2ae6319
  1. 4
      .dumi/pages/theme-editor/index.tsx
  2. 19
      CHANGELOG.en-US.md
  3. 19
      CHANGELOG.zh-CN.md
  4. 4
      components/app/index.en-US.md
  5. 2
      components/app/index.tsx
  6. 1044
      components/badge/__tests__/__snapshots__/demo-extend.test.ts.snap
  7. 1044
      components/badge/__tests__/__snapshots__/demo.test.ts.snap
  8. 47
      components/badge/demo/colorful-with-count-debug.tsx
  9. 9
      components/badge/demo/ribbon.tsx
  10. 5
      components/badge/style/index.ts
  11. 20
      components/breadcrumb/Breadcrumb.tsx
  12. 10
      components/breadcrumb/BreadcrumbItem.tsx
  13. 15
      components/breadcrumb/__tests__/Breadcrumb.test.tsx
  14. 11
      components/button/button.tsx
  15. 32
      components/button/style/index.ts
  16. 47
      components/cascader/index.tsx
  17. 7
      components/date-picker/index.en-US.md
  18. 8
      components/date-picker/index.zh-CN.md
  19. 9
      components/form/FormList.tsx
  20. 8
      components/form/__tests__/__snapshots__/demo-extend.test.ts.snap
  21. 8
      components/form/__tests__/__snapshots__/demo.test.tsx.snap
  22. 66
      components/form/__tests__/list.test.tsx
  23. 6
      components/form/demo/layout-can-wrap.tsx
  24. 7
      components/popconfirm/style/index.tsx
  25. 41
      components/progress/style/index.tsx
  26. 2
      components/select/style/index.tsx
  27. 8
      components/spin/index.tsx
  28. 15
      components/table/demo/drag-sorting.tsx
  29. 16
      components/tag/demo/draggable.tsx
  30. 1
      components/time-picker/index.en-US.md
  31. 1
      components/time-picker/index.zh-CN.md
  32. 61
      components/transfer/ListBody.tsx
  33. 39
      components/transfer/__tests__/index.test.tsx
  34. 1
      components/transfer/list.tsx
  35. 8
      components/transfer/style/index.ts
  36. 12
      components/tree/demo/drag-debug.tsx
  37. 8
      components/tree/demo/draggable.tsx
  38. 16
      components/typography/Base/index.tsx
  39. 31
      docs/react/faq.en-US.md
  40. 31
      docs/react/faq.zh-CN.md
  41. 2
      docs/spec/data-format.zh-CN.md
  42. 14
      docs/spec/transition.en-US.md
  43. 14
      docs/spec/transition.zh-CN.md
  44. 7
      package.json

4
.dumi/pages/theme-editor/index.tsx

@ -183,6 +183,10 @@ const CustomTheme = () => {
style={{ height: 'calc(100vh - 64px - 56px)' }} style={{ height: 'calc(100vh - 64px - 56px)' }}
onThemeChange={(newTheme) => { onThemeChange={(newTheme) => {
setTheme(newTheme.config); setTheme(newTheme.config);
setThemeConfigContent({
json: newTheme.config,
text: undefined,
});
}} }}
locale={lang === 'cn' ? zhCN : enUS} locale={lang === 'cn' ? zhCN : enUS}
/> />

19
CHANGELOG.en-US.md

@ -15,6 +15,25 @@ timeline: true
--- ---
## 5.6.4
`2023-07-03`
- Form
- 🐞 Fix `onFieldsChange` event will still be triggered incorrectly when the field is not configured with `rules` when the Form is submitted. [#43290](https://github.com/ant-design/ant-design/pull/43290)
- 🐞 Fix the problem that the warning message that `name` is empty is falsely reported when the `name` of Form.List is 0. [#43199](https://github.com/ant-design/ant-design/pull/43199) [@Yuiai01](https://github.com/Yuiai01)
- 🐞 Fix the Badge `color` attribute does not take effect. [#43304](https://github.com/ant-design/ant-design/pull/43304)
- 🐞 Fix the position of Select clear icon when FormItem sets `hasFeedback`. [#43302](https://github.com/ant-design/ant-design/pull/43302) [@tinyfind](https://github.com/tinyfind)
- 🐞 Fix Transfer paging drop-down button is hidden and `showSizeChanger` method is invalid. [#41906](https://github.com/ant-design/ant-design/pull/41906) [@Yuiai01](https://github.com/Yuiai01)
- 🐞 Fix the invalid modification of `colorText` and `fontSize` of Popconfirm component. [#43212](https://github.com/ant-design/ant-design/pull/43212) [@MadCcc](https://github.com/MadCcc)
- 🐞 Fix the problem that deleting files after Upload configures `maxCount` will not trigger `onChange`. [#43193](https://github.com/ant-design/ant-design/pull/43193)
- 💄 Fix Button disabled style error when it has `link` or `href` attribute. [#43091](https://github.com/ant-design/ant-design/pull/43091) [@BoyYangzai](https://github.com/BoyYangzai)
- TypeScript
- 🤖 Optimize Breadcrumb `params` type and support generics. [#43211](https://github.com/ant-design/ant-design/pull/43211)
- 🤖 Optimize Breadcrumb `params` type and support generics. [#43257](https://github.com/ant-design/ant-design/pull/43257) [@thinkasany](https://github.com/thinkasany)
- 🤖 Remove redundant number type from Button `loading`. [#43256](https://github.com/ant-design/ant-design/pull/43256) [@thinkasany](https://github.com/thinkasany)
- 🤖 Transparently pass Cascader `optionType` generic. [#43231](https://github.com/ant-design/ant-design/pull/43231) [@ZWkang](https://github.com/ZWkang)
## 5.6.3 ## 5.6.3
`2023-06-25` `2023-06-25`

19
CHANGELOG.zh-CN.md

@ -15,6 +15,25 @@ timeline: true
--- ---
## 5.6.4
`2023-07-03`
- Form
- 🐞 修复 Form 在提交时,字段没有配置 `rules` 时仍会错误触发 `onFieldsChange` 事件的问题。[#43290](https://github.com/ant-design/ant-design/pull/43290)
- 🐞 修复 Form.List 的 `name` 为 0 时误报 `name` 为空的警告信息的问题。[#43199](https://github.com/ant-design/ant-design/pull/43199) [@Yuiai01](https://github.com/Yuiai01)
- 🐞 修复 Badge `color` 属性不生效的问题。[#43304](https://github.com/ant-design/ant-design/pull/43304)
- 🐞 修复 Select 组件的消除图标在 FormItem 设置 `hasFeedback` 时的位置问题。[#43302](https://github.com/ant-design/ant-design/pull/43302) [@tinyfind](https://github.com/tinyfind)
- 🐞 修复 Transfer 分页下拉按钮被隐藏以及 `showSizeChanger` 方法无效。[#41906](https://github.com/ant-design/ant-design/pull/41906) [@Yuiai01](https://github.com/Yuiai01)
- 🐞 修复 Popconfirm 组件 `colorText``fontSize` 修改无效的问题。[#43212](https://github.com/ant-design/ant-design/pull/43212) [@MadCcc](https://github.com/MadCcc)
- 🐞 修复 Upload 配置 `maxCount` 后删除文件不会触发 `onChange` 的问题。[#43193](https://github.com/ant-design/ant-design/pull/43193)
- 💄 修复 Button 在有 `link``href` 属性时禁用样式错误。[#43091](https://github.com/ant-design/ant-design/pull/43091) [@BoyYangzai](https://github.com/BoyYangzai)
- TypeScript
- 🤖 优化 Breadcrumb `params` 类型,支持泛型。[#43211](https://github.com/ant-design/ant-design/pull/43211)
- 🤖 优化 Typography `copyIdRef` 类型。[#43257](https://github.com/ant-design/ant-design/pull/43257) [@thinkasany](https://github.com/thinkasany)
- 🤖 移除 Button `loading` 多余 number 类型。[#43256](https://github.com/ant-design/ant-design/pull/43256) [@thinkasany](https://github.com/thinkasany)
- 🤖 透传 Cascader `optionType` 泛型。[#43231](https://github.com/ant-design/ant-design/pull/43231) [@ZWkang](https://github.com/ZWkang)
## 5.6.3 ## 5.6.3
`2023-06-25` `2023-06-25`

4
components/app/index.en-US.md

@ -13,7 +13,7 @@ Application wrapper for some global usages.
## When To Use ## When To Use
- Provide reset styles based on `.ant-app` element. - Provide reset styles based on `.ant-app` element.
- You could use static methods of `message/notification/Modal` form `useApp` without put `contextHolder` mannully. - You could use static methods of `message/notification/Modal` form `useApp` without writing `contextHolder` manually.
## Examples ## Examples
@ -95,7 +95,7 @@ export default () => {
return null; return null;
}; };
export { message, notification, modal }; export { message, modal, notification };
``` ```
```tsx ```tsx

2
components/app/index.tsx

@ -42,7 +42,7 @@ const App: React.FC<AppProps> & { useApp: typeof useApp } = (props) => {
message: { ...appConfig.message, ...message }, message: { ...appConfig.message, ...message },
notification: { ...appConfig.notification, ...notification }, notification: { ...appConfig.notification, ...notification },
}), }),
[message, notification, appConfig.message, appConfig.message], [message, notification, appConfig.message, appConfig.notification],
); );
const [messageApi, messageContextHolder] = useMessage(mergedAppConfig.message); const [messageApi, messageContextHolder] = useMessage(mergedAppConfig.message);

1044
components/badge/__tests__/__snapshots__/demo-extend.test.ts.snap

File diff suppressed because it is too large

1044
components/badge/__tests__/__snapshots__/demo.test.ts.snap

File diff suppressed because it is too large

47
components/badge/demo/colorful-with-count-debug.tsx

@ -1,5 +1,5 @@
import React from 'react';
import { Badge, Space } from 'antd'; import { Badge, Space } from 'antd';
import React from 'react';
const colors = [ const colors = [
'pink', 'pink',
@ -17,24 +17,35 @@ const colors = [
'lime', 'lime',
]; ];
const AvatarItem = ({ color }: { color: string }) => (
<div
style={{
width: 90,
height: 90,
lineHeight: '90px',
background: '#ccc',
textAlign: 'center',
}}
>
{color}
</div>
);
const App: React.FC = () => ( const App: React.FC = () => (
<Space wrap size={['large', 'middle']}> <>
{colors.map((color) => ( <Space wrap size={['large', 'middle']}>
<Badge color={color} count={44} key={color}> {colors.map((color) => (
<div <Badge color={color} count={44} key={color}>
style={{ <AvatarItem color={color} />
width: 90, </Badge>
height: 90, ))}
lineHeight: '90px', </Space>
background: '#ccc', <Space wrap size={['large', 'middle']}>
textAlign: 'center', {colors.map((color) => (
}} <Badge status="processing" color={color} text="loading" key={color} />
> ))}
{color} </Space>
</div> </>
</Badge>
))}
</Space>
); );
export default App; export default App;

9
components/badge/demo/ribbon.tsx

@ -1,5 +1,5 @@
import React from 'react';
import { Badge, Card, Space } from 'antd'; import { Badge, Card, Space } from 'antd';
import React from 'react';
const App: React.FC = () => ( const App: React.FC = () => (
<Space direction="vertical" size="middle" style={{ width: '100%' }}> <Space direction="vertical" size="middle" style={{ width: '100%' }}>
@ -8,12 +8,7 @@ const App: React.FC = () => (
and raises the spyglass. and raises the spyglass.
</Card> </Card>
</Badge.Ribbon> </Badge.Ribbon>
<Badge.Ribbon text={ <Badge.Ribbon text="Hippies" color="pink">
<div>
Hippies <br />
Happy
</div>
} color="pink">
<Card title="Pushes open the window" size="small"> <Card title="Pushes open the window" size="small">
and raises the spyglass. and raises the spyglass.
</Card> </Card>

5
components/badge/style/index.ts

@ -74,8 +74,11 @@ const genSharedBadgeStyle: GenerateStyle<BadgeToken> = (token: BadgeToken): CSSO
const ribbonWrapperPrefixCls = `${antCls}-ribbon-wrapper`; const ribbonWrapperPrefixCls = `${antCls}-ribbon-wrapper`;
const colorPreset = genPresetColor(token, (colorKey, { darkColor }) => ({ const colorPreset = genPresetColor(token, (colorKey, { darkColor }) => ({
[`${componentCls}-color-${colorKey}`]: { [`&${componentCls} ${componentCls}-color-${colorKey}`]: {
background: darkColor, background: darkColor,
[`&:not(${componentCls}-count)`]: {
color: darkColor,
},
}, },
})); }));

20
components/breadcrumb/Breadcrumb.tsx

@ -46,9 +46,9 @@ export type ItemType = Partial<BreadcrumbItemType & BreadcrumbSeparatorType>;
export type InternalRouteType = Partial<BreadcrumbItemType & BreadcrumbSeparatorType>; export type InternalRouteType = Partial<BreadcrumbItemType & BreadcrumbSeparatorType>;
export interface BreadcrumbProps { export interface BreadcrumbProps<T extends Record<PropertyKey, any> = any> {
prefixCls?: string; prefixCls?: string;
params?: any; params?: T;
separator?: React.ReactNode; separator?: React.ReactNode;
style?: React.CSSProperties; style?: React.CSSProperties;
className?: string; className?: string;
@ -60,19 +60,13 @@ export interface BreadcrumbProps {
items?: ItemType[]; items?: ItemType[];
itemRender?: ( itemRender?: (route: ItemType, params: T, routes: ItemType[], paths: string[]) => React.ReactNode;
route: ItemType,
params: any,
routes: ItemType[],
paths: string[],
) => React.ReactNode;
} }
const getPath = (params: any, path?: string) => { const getPath = <T extends Record<PropertyKey, any> = any>(params: T, path?: string) => {
if (path === undefined) { if (path === undefined) {
return path; return path;
} }
let mergedPath = (path || '').replace(/^\//, ''); let mergedPath = (path || '').replace(/^\//, '');
Object.keys(params).forEach((key) => { Object.keys(params).forEach((key) => {
mergedPath = mergedPath.replace(`:${key}`, params[key]!); mergedPath = mergedPath.replace(`:${key}`, params[key]!);
@ -80,7 +74,7 @@ const getPath = (params: any, path?: string) => {
return mergedPath; return mergedPath;
}; };
const Breadcrumb = (props: BreadcrumbProps) => { const Breadcrumb = <T extends Record<PropertyKey, any> = any>(props: BreadcrumbProps<T>) => {
const { const {
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
separator = '/', separator = '/',
@ -113,7 +107,7 @@ const Breadcrumb = (props: BreadcrumbProps) => {
// generated by route // generated by route
const paths: string[] = []; const paths: string[] = [];
const itemRenderRoutes: any = items || legacyRoutes; const itemRenderRoutes = items || legacyRoutes;
crumbs = mergedItems.map((item, index) => { crumbs = mergedItems.map((item, index) => {
const { const {
@ -168,7 +162,7 @@ const Breadcrumb = (props: BreadcrumbProps) => {
onClick={onClick} onClick={onClick}
prefixCls={prefixCls} prefixCls={prefixCls}
> >
{mergedItemRender(item as BreadcrumbItemType, params, itemRenderRoutes, paths, href)} {mergedItemRender(item, params, itemRenderRoutes!, paths, href)}
</InternalBreadcrumbItem> </InternalBreadcrumbItem>
); );
}); });

10
components/breadcrumb/BreadcrumbItem.tsx

@ -37,7 +37,7 @@ export interface BreadcrumbItemProps extends SeparatorType {
overlay?: DropdownProps['overlay']; overlay?: DropdownProps['overlay'];
} }
export const InternalBreadcrumbItem = (props: BreadcrumbItemProps) => { export const InternalBreadcrumbItem: React.FC<BreadcrumbItemProps> = (props) => {
const { prefixCls, separator = '/', children, menu, overlay, dropdownProps, href } = props; const { prefixCls, separator = '/', children, menu, overlay, dropdownProps, href } = props;
// Warning for deprecated usage // Warning for deprecated usage
@ -103,11 +103,15 @@ export const InternalBreadcrumbItem = (props: BreadcrumbItemProps) => {
return null; return null;
}; };
const BreadcrumbItem = (props: BreadcrumbItemProps) => { type CompoundedComponent = React.FC<BreadcrumbItemProps> & {
/** @internal */
__ANT_BREADCRUMB_ITEM: boolean;
};
const BreadcrumbItem: CompoundedComponent = (props) => {
const { prefixCls: customizePrefixCls, children, href, ...restProps } = props; const { prefixCls: customizePrefixCls, children, href, ...restProps } = props;
const { getPrefixCls } = React.useContext(ConfigContext); const { getPrefixCls } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('breadcrumb', customizePrefixCls); const prefixCls = getPrefixCls('breadcrumb', customizePrefixCls);
return ( return (
<InternalBreadcrumbItem {...restProps} prefixCls={prefixCls}> <InternalBreadcrumbItem {...restProps} prefixCls={prefixCls}>
{renderItem(prefixCls, restProps as ItemType, children, href)} {renderItem(prefixCls, restProps as ItemType, children, href)}

15
components/breadcrumb/__tests__/Breadcrumb.test.tsx

@ -384,4 +384,19 @@ describe('Breadcrumb', () => {
); );
expect(document.querySelector('.ant-dropdown')).toBeTruthy(); expect(document.querySelector('.ant-dropdown')).toBeTruthy();
}); });
it('Breadcrumb params type test', () => {
interface Params {
key1?: number;
key2?: string;
}
expect(
<Breadcrumb<Params>
params={{
key1: 1,
key2: 'test',
}}
/>,
).toBeTruthy();
});
}); });

11
components/button/button.tsx

@ -76,8 +76,6 @@ type CompoundedComponent = React.ForwardRefExoticComponent<
__ANT_BUTTON: boolean; __ANT_BUTTON: boolean;
}; };
type Loading = number | boolean;
type LoadingConfigType = { type LoadingConfigType = {
loading: boolean; loading: boolean;
delay: number; delay: number;
@ -137,7 +135,7 @@ const InternalButton: React.ForwardRefRenderFunction<
const loadingOrDelay = useMemo<LoadingConfigType>(() => getLoadingConfig(loading), [loading]); const loadingOrDelay = useMemo<LoadingConfigType>(() => getLoadingConfig(loading), [loading]);
const [innerLoading, setLoading] = useState<Loading>(loadingOrDelay.loading); const [innerLoading, setLoading] = useState<boolean>(loadingOrDelay.loading);
const [hasTwoCNChar, setHasTwoCNChar] = useState<boolean>(false); const [hasTwoCNChar, setHasTwoCNChar] = useState<boolean>(false);
@ -218,8 +216,6 @@ const InternalButton: React.ForwardRefRenderFunction<
const linkButtonRestProps = omit(rest as ButtonProps & { navigate: any }, ['navigate']); const linkButtonRestProps = omit(rest as ButtonProps & { navigate: any }, ['navigate']);
const hrefAndDisabled = linkButtonRestProps.href !== undefined && mergedDisabled;
const classes = classNames( const classes = classNames(
prefixCls, prefixCls,
hashId, hashId,
@ -234,7 +230,6 @@ const InternalButton: React.ForwardRefRenderFunction<
[`${prefixCls}-block`]: block, [`${prefixCls}-block`]: block,
[`${prefixCls}-dangerous`]: !!danger, [`${prefixCls}-dangerous`]: !!danger,
[`${prefixCls}-rtl`]: direction === 'rtl', [`${prefixCls}-rtl`]: direction === 'rtl',
[`${prefixCls}-disabled`]: hrefAndDisabled,
}, },
compactItemClassnames, compactItemClassnames,
className, className,
@ -263,7 +258,9 @@ const InternalButton: React.ForwardRefRenderFunction<
return wrapSSR( return wrapSSR(
<a <a
{...linkButtonRestProps} {...linkButtonRestProps}
className={classes} className={classNames(classes, {
[`${prefixCls}-disabled`]: mergedDisabled,
})}
style={fullStyle} style={fullStyle}
onClick={handleClick} onClick={handleClick}
ref={buttonRef as React.Ref<HTMLAnchorElement>} ref={buttonRef as React.Ref<HTMLAnchorElement>}

32
components/button/style/index.ts

@ -115,8 +115,12 @@ const genSharedButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token): CSS
}; };
}; };
const genHoverActiveButtonStyle = (hoverStyle: CSSObject, activeStyle: CSSObject): CSSObject => ({ const genHoverActiveButtonStyle = (
'&:not(:disabled)': { btnCls: string,
hoverStyle: CSSObject,
activeStyle: CSSObject,
): CSSObject => ({
[`&:not(:disabled):not(${btnCls}-disabled)`]: {
'&:hover': hoverStyle, '&:hover': hoverStyle,
'&:active': activeStyle, '&:active': activeStyle,
}, },
@ -161,6 +165,7 @@ const genGhostButtonStyle = (
boxShadow: 'none', boxShadow: 'none',
...genHoverActiveButtonStyle( ...genHoverActiveButtonStyle(
btnCls,
{ {
backgroundColor: 'transparent', backgroundColor: 'transparent',
...hoverStyle, ...hoverStyle,
@ -180,7 +185,7 @@ const genGhostButtonStyle = (
}); });
const genSolidDisabledButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token) => ({ const genSolidDisabledButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token) => ({
'&:disabled': { [`&:disabled, &${token.componentCls}-disabled`]: {
...genDisabledStyle(token), ...genDisabledStyle(token),
}, },
}); });
@ -190,7 +195,7 @@ const genSolidButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token) => ({
}); });
const genPureDisabledButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token) => ({ const genPureDisabledButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token) => ({
'&:disabled': { [`&:disabled, &${token.componentCls}-disabled`]: {
cursor: 'not-allowed', cursor: 'not-allowed',
color: token.colorTextDisabled, color: token.colorTextDisabled,
}, },
@ -206,6 +211,7 @@ const genDefaultButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token) =>
boxShadow: `0 ${token.controlOutlineWidth}px 0 ${token.controlTmpOutline}`, boxShadow: `0 ${token.controlOutlineWidth}px 0 ${token.controlTmpOutline}`,
...genHoverActiveButtonStyle( ...genHoverActiveButtonStyle(
token.componentCls,
{ {
color: token.colorPrimaryHover, color: token.colorPrimaryHover,
borderColor: token.colorPrimaryHover, borderColor: token.colorPrimaryHover,
@ -229,6 +235,7 @@ const genDefaultButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token) =>
borderColor: token.colorError, borderColor: token.colorError,
...genHoverActiveButtonStyle( ...genHoverActiveButtonStyle(
token.componentCls,
{ {
color: token.colorErrorHover, color: token.colorErrorHover,
borderColor: token.colorErrorBorderHover, borderColor: token.colorErrorBorderHover,
@ -260,6 +267,7 @@ const genPrimaryButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token) =>
boxShadow: `0 ${token.controlOutlineWidth}px 0 ${token.controlOutline}`, boxShadow: `0 ${token.controlOutlineWidth}px 0 ${token.controlOutline}`,
...genHoverActiveButtonStyle( ...genHoverActiveButtonStyle(
token.componentCls,
{ {
color: token.colorTextLightSolid, color: token.colorTextLightSolid,
backgroundColor: token.colorPrimaryHover, backgroundColor: token.colorPrimaryHover,
@ -291,6 +299,7 @@ const genPrimaryButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token) =>
boxShadow: `0 ${token.controlOutlineWidth}px 0 ${token.colorErrorOutline}`, boxShadow: `0 ${token.controlOutlineWidth}px 0 ${token.colorErrorOutline}`,
...genHoverActiveButtonStyle( ...genHoverActiveButtonStyle(
token.componentCls,
{ {
backgroundColor: token.colorErrorHover, backgroundColor: token.colorErrorHover,
}, },
@ -329,6 +338,7 @@ const genLinkButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token) => ({
color: token.colorLink, color: token.colorLink,
...genHoverActiveButtonStyle( ...genHoverActiveButtonStyle(
token.componentCls,
{ {
color: token.colorLinkHover, color: token.colorLinkHover,
}, },
@ -343,6 +353,7 @@ const genLinkButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token) => ({
color: token.colorError, color: token.colorError,
...genHoverActiveButtonStyle( ...genHoverActiveButtonStyle(
token.componentCls,
{ {
color: token.colorErrorHover, color: token.colorErrorHover,
}, },
@ -358,6 +369,7 @@ const genLinkButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token) => ({
// Type: Text // Type: Text
const genTextButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token) => ({ const genTextButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token) => ({
...genHoverActiveButtonStyle( ...genHoverActiveButtonStyle(
token.componentCls,
{ {
color: token.colorText, color: token.colorText,
backgroundColor: token.colorBgTextHover, backgroundColor: token.colorBgTextHover,
@ -375,6 +387,7 @@ const genTextButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token) => ({
...genPureDisabledButtonStyle(token), ...genPureDisabledButtonStyle(token),
...genHoverActiveButtonStyle( ...genHoverActiveButtonStyle(
token.componentCls,
{ {
color: token.colorErrorHover, color: token.colorErrorHover,
backgroundColor: token.colorErrorBg, backgroundColor: token.colorErrorBg,
@ -387,14 +400,6 @@ const genTextButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token) => ({
}, },
}); });
// Href and Disabled
const genDisabledButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token) => ({
...genDisabledStyle(token),
[`&${token.componentCls}:hover`]: {
...genDisabledStyle(token),
},
});
const genTypeButtonStyle: GenerateStyle<ButtonToken> = (token) => { const genTypeButtonStyle: GenerateStyle<ButtonToken> = (token) => {
const { componentCls } = token; const { componentCls } = token;
@ -404,7 +409,6 @@ const genTypeButtonStyle: GenerateStyle<ButtonToken> = (token) => {
[`${componentCls}-dashed`]: genDashedButtonStyle(token), [`${componentCls}-dashed`]: genDashedButtonStyle(token),
[`${componentCls}-link`]: genLinkButtonStyle(token), [`${componentCls}-link`]: genLinkButtonStyle(token),
[`${componentCls}-text`]: genTextButtonStyle(token), [`${componentCls}-text`]: genTextButtonStyle(token),
[`${componentCls}-disabled`]: genDisabledButtonStyle(token),
}; };
}; };
@ -528,7 +532,7 @@ export default genComponentStyleHook('Button', (token) => {
// Block // Block
genBlockButtonStyle(buttonToken), genBlockButtonStyle(buttonToken),
// Group (type, ghost, danger, disabled, loading) // Group (type, ghost, danger, loading)
genTypeButtonStyle(buttonToken), genTypeButtonStyle(buttonToken),
// Button Group // Button Group

47
components/cascader/index.tsx

@ -96,30 +96,39 @@ const defaultSearchRender: ShowSearchType['render'] = (inputValue, path, prefixC
return optionList; return optionList;
}; };
type SingleCascaderProps = Omit<RcSingleCascaderProps, 'checkable' | 'options'> & { type SingleCascaderProps<OptionType extends BaseOptionType> = Omit<
RcSingleCascaderProps<OptionType>,
'checkable' | 'options'
> & {
multiple?: false; multiple?: false;
}; };
type MultipleCascaderProps = Omit<RcMultipleCascaderProps, 'checkable' | 'options'> & { type MultipleCascaderProps<OptionType extends BaseOptionType> = Omit<
RcMultipleCascaderProps<OptionType>,
'checkable' | 'options'
> & {
multiple: true; multiple: true;
}; };
type UnionCascaderProps = SingleCascaderProps | MultipleCascaderProps; type UnionCascaderProps<OptionType extends BaseOptionType> =
| SingleCascaderProps<OptionType>
export type CascaderProps<DataNodeType = any> = UnionCascaderProps & { | MultipleCascaderProps<OptionType>;
multiple?: boolean;
size?: SizeType; export type CascaderProps<DataNodeType extends BaseOptionType = any> =
disabled?: boolean; UnionCascaderProps<DataNodeType> & {
bordered?: boolean; multiple?: boolean;
placement?: SelectCommonPlacement; size?: SizeType;
suffixIcon?: React.ReactNode; disabled?: boolean;
options?: DataNodeType[]; bordered?: boolean;
status?: InputStatus; placement?: SelectCommonPlacement;
suffixIcon?: React.ReactNode;
rootClassName?: string; options?: DataNodeType[];
popupClassName?: string; status?: InputStatus;
/** @deprecated Please use `popupClassName` instead */
dropdownClassName?: string; rootClassName?: string;
}; popupClassName?: string;
/** @deprecated Please use `popupClassName` instead */
dropdownClassName?: string;
};
export interface CascaderRef { export interface CascaderRef {
focus: () => void; focus: () => void;

7
components/date-picker/index.en-US.md

@ -83,7 +83,7 @@ The following APIs are shared by DatePicker, RangePicker.
| className | The picker className | string | - | | | className | The picker className | string | - | |
| dateRender | Custom rendering function for date cells, >= 5.4.0 use `cellRender` instead. | function(currentDate: dayjs, today: dayjs) => React.ReactNode | - | < 5.4.0 | | dateRender | Custom rendering function for date cells, >= 5.4.0 use `cellRender` instead. | function(currentDate: dayjs, today: dayjs) => React.ReactNode | - | < 5.4.0 |
| changeOnBlur | Trigger `change` when blur. e.g. datetime picker no need click confirm button | boolean | false | 5.5.0 | | changeOnBlur | Trigger `change` when blur. e.g. datetime picker no need click confirm button | boolean | false | 5.5.0 |
| cellRender | Custom rendering function for picker cells | function(current: dayjs, today: dayjs, info: { originNode: React.ReactElement,today: DateType, range?: 'start' \| 'end', type: PanelMode, locale?: Locale, subType?: 'hour' \| 'minute' \| 'second' \| 'meridiem' }) => React.ReactNode | - | 5.4.0 | | cellRender | Custom rendering function for picker cells | (current: dayjs, info: { originNode: React.ReactElement,today: DateType, range?: 'start' \| 'end', type: PanelMode, locale?: Locale, subType?: 'hour' \| 'minute' \| 'second' \| 'meridiem' }) => React.ReactNode | - | 5.4.0 |
| disabled | Determine whether the DatePicker is disabled | boolean | false | | | disabled | Determine whether the DatePicker is disabled | boolean | false | |
| disabledDate | Specify the date that cannot be selected | (currentDate: dayjs) => boolean | - | | | disabledDate | Specify the date that cannot be selected | (currentDate: dayjs) => boolean | - | |
| format | To set the date format, support multi-format matching when it is an array, display the first one shall prevail. refer to [dayjs#format](https://day.js.org/docs/en/display/format). for example: [Custom Format](#components-date-picker-demo-format) | [formatType](#formattype) | [rc-picker](https://github.com/react-component/picker/blob/f512f18ed59d6791280d1c3d7d37abbb9867eb0b/src/utils/uiUtil.ts#L155-L177) | | | format | To set the date format, support multi-format matching when it is an array, display the first one shall prevail. refer to [dayjs#format](https://day.js.org/docs/en/display/format). for example: [Custom Format](#components-date-picker-demo-format) | [formatType](#formattype) | [rc-picker](https://github.com/react-component/picker/blob/f512f18ed59d6791280d1c3d7d37abbb9867eb0b/src/utils/uiUtil.ts#L155-L177) | |
@ -187,7 +187,7 @@ Added in `4.1.0`.
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| allowEmpty | Allow start or end input leave empty | \[boolean, boolean] | \[false, false] | | | allowEmpty | Allow start or end input leave empty | \[boolean, boolean] | \[false, false] | |
| dateRender | Custom rendering function for date cells, >= 5.4.0 use `cellRender` instead. | function(currentDate: dayjs, today: dayjs) => React.ReactNode | - | < 5.4.0 | | dateRender | Custom rendering function for date cells, >= 5.4.0 use `cellRender` instead. | function(currentDate: dayjs, today: dayjs) => React.ReactNode | - | < 5.4.0 |
| cellRender | Custom rendering function for picker cells | function(current: dayjs, today: dayjs, info: { originNode: React.ReactElement,today: DateType, range?: 'start' \| 'end', type: PanelMode, locale?: Locale, subType?: 'hour' \| 'minute' \| 'second' \| 'meridiem' }) => React.ReactNode | - | 5.4.0 | | cellRender | Custom rendering function for picker cells | (current: dayjs, info: { originNode: React.ReactElement,today: DateType, range?: 'start' \| 'end', type: PanelMode, locale?: Locale, subType?: 'hour' \| 'minute' \| 'second' \| 'meridiem' }) => React.ReactNode | - | 5.4.0 |
| defaultPickerValue | To set default picker date | \[[dayjs](https://day.js.org/), [dayjs](https://day.js.org/)] | - | | | defaultPickerValue | To set default picker date | \[[dayjs](https://day.js.org/), [dayjs](https://day.js.org/)] | - | |
| defaultValue | To set default date | \[[dayjs](https://day.js.org/), [dayjs](https://day.js.org/)] | - | | | defaultValue | To set default date | \[[dayjs](https://day.js.org/), [dayjs](https://day.js.org/)] | - | |
| disabled | If disable start or end | \[boolean, boolean] | - | | | disabled | If disable start or end | \[boolean, boolean] | - | |
@ -248,8 +248,9 @@ Please use correct [language](/docs/react/i18n) ([#5605](https://github.com/ant-
```js ```js
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn'; import 'dayjs/locale/zh-cn';
import 'dayjs/plugin/updateLocale'; import updateLocale from 'dayjs/plugin/updateLocale';
dayjs.extend(updateLocale);
dayjs.updateLocale('zh-cn', { dayjs.updateLocale('zh-cn', {
weekStart: 0, weekStart: 0,
}); });

8
components/date-picker/index.zh-CN.md

@ -84,7 +84,7 @@ import locale from 'antd/locale/zh_CN';
| className | 选择器 className | string | - | | | className | 选择器 className | string | - | |
| dateRender | 自定义日期单元格的内容,5.4.0 起用 `cellRender` 代替 | function(currentDate: dayjs, today: dayjs) => React.ReactNode | - | < 5.4.0 | | dateRender | 自定义日期单元格的内容,5.4.0 起用 `cellRender` 代替 | function(currentDate: dayjs, today: dayjs) => React.ReactNode | - | < 5.4.0 |
| changeOnBlur | 失去焦点时触发 `change` 事件,例如 datetime 下不再需要点击确认按钮 | boolean | false | 5.5.0 | | changeOnBlur | 失去焦点时触发 `change` 事件,例如 datetime 下不再需要点击确认按钮 | boolean | false | 5.5.0 |
| cellRender | 自定义单元格的内容 | function(current: dayjs, today: dayjs, info: { originNode: React.ReactElement,today: DateType, range?: 'start' \| 'end', type: PanelMode, locale?: Locale, subType?: 'hour' \| 'minute' \| 'second' \| 'meridiem' }) => React.ReactNode | - | 5.4.0 | | cellRender | 自定义单元格的内容 | (current: dayjs, info: { originNode: React.ReactElement,today: DateType, range?: 'start' \| 'end', type: PanelMode, locale?: Locale, subType?: 'hour' \| 'minute' \| 'second' \| 'meridiem' }) => React.ReactNode | - | 5.4.0 |
| disabled | 禁用 | boolean | false | | | disabled | 禁用 | boolean | false | |
| disabledDate | 不可选择的日期 | (currentDate: dayjs) => boolean | - | | | disabledDate | 不可选择的日期 | (currentDate: dayjs) => boolean | - | |
| format | 设置日期格式,为数组时支持多格式匹配,展示以第一个为准。配置参考 [dayjs#format](https://day.js.org/docs/zh-CN/display/format#%E6%94%AF%E6%8C%81%E7%9A%84%E6%A0%BC%E5%BC%8F%E5%8C%96%E5%8D%A0%E4%BD%8D%E7%AC%A6%E5%88%97%E8%A1%A8)。示例:[自定义格式](#components-date-picker-demo-format) | [formatType](#formattype) | [rc-picker](https://github.com/react-component/picker/blob/f512f18ed59d6791280d1c3d7d37abbb9867eb0b/src/utils/uiUtil.ts#L155-L177) | | | format | 设置日期格式,为数组时支持多格式匹配,展示以第一个为准。配置参考 [dayjs#format](https://day.js.org/docs/zh-CN/display/format#%E6%94%AF%E6%8C%81%E7%9A%84%E6%A0%BC%E5%BC%8F%E5%8C%96%E5%8D%A0%E4%BD%8D%E7%AC%A6%E5%88%97%E8%A1%A8)。示例:[自定义格式](#components-date-picker-demo-format) | [formatType](#formattype) | [rc-picker](https://github.com/react-component/picker/blob/f512f18ed59d6791280d1c3d7d37abbb9867eb0b/src/utils/uiUtil.ts#L155-L177) | |
@ -188,7 +188,7 @@ import locale from 'antd/locale/zh_CN';
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| allowEmpty | 允许起始项部分为空 | \[boolean, boolean] | \[false, false] | | | allowEmpty | 允许起始项部分为空 | \[boolean, boolean] | \[false, false] | |
| dateRender | 自定义日期单元格的内容,5.4.0 起用 `cellRender` 代替 | function(currentDate: dayjs, today: dayjs) => React.ReactNode | - | < 5.4.0 | | dateRender | 自定义日期单元格的内容,5.4.0 起用 `cellRender` 代替 | function(currentDate: dayjs, today: dayjs) => React.ReactNode | - | < 5.4.0 |
| cellRender | 自定义单元格的内容。 | function(current: dayjs, today: dayjs, info: { originNode: React.ReactElement,today: DateType, range?: 'start' \| 'end', type: PanelMode, locale?: Locale, subType?: 'hour' \| 'minute' \| 'second' \| 'meridiem' }) => React.ReactNode | - | 5.4.0 | | cellRender | 自定义单元格的内容。 | (current: dayjs, info: { originNode: React.ReactElement,today: DateType, range?: 'start' \| 'end', type: PanelMode, locale?: Locale, subType?: 'hour' \| 'minute' \| 'second' \| 'meridiem' }) => React.ReactNode | - | 5.4.0 |
| defaultPickerValue | 默认面板日期 | [dayjs](https://day.js.org/)\[] | - | | | defaultPickerValue | 默认面板日期 | [dayjs](https://day.js.org/)\[] | - | |
| defaultValue | 默认日期 | [dayjs](https://day.js.org/)\[] | - | | | defaultValue | 默认日期 | [dayjs](https://day.js.org/)\[] | - | |
| disabled | 禁用起始项 | \[boolean, boolean] | - | | | disabled | 禁用起始项 | \[boolean, boolean] | - | |
@ -243,8 +243,10 @@ export type FormatType = Generic | GenericFn | Array<Generic | GenericFn>;
```js ```js
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn'; import 'dayjs/locale/zh-cn';
import 'dayjs/plugin/updateLocale';
import updateLocale from 'dayjs/plugin/updateLocale';
dayjs.extend(updateLocale);
dayjs.updateLocale('zh-cn', { dayjs.updateLocale('zh-cn', {
weekStart: 0, weekStart: 0,
}); });

9
components/form/FormList.tsx

@ -1,8 +1,8 @@
import { List } from 'rc-field-form'; import { List } from 'rc-field-form';
import type { StoreValue, ValidatorRule } from 'rc-field-form/lib/interface'; import type { StoreValue, ValidatorRule } from 'rc-field-form/lib/interface';
import * as React from 'react'; import * as React from 'react';
import { ConfigContext } from '../config-provider';
import warning from '../_util/warning'; import warning from '../_util/warning';
import { ConfigContext } from '../config-provider';
import { FormItemPrefixContext } from './context'; import { FormItemPrefixContext } from './context';
export interface FormListFieldData { export interface FormListFieldData {
@ -35,7 +35,12 @@ const FormList: React.FC<FormListProps> = ({
children, children,
...props ...props
}) => { }) => {
warning(!!props.name, 'Form.List', 'Miss `name` prop.'); warning(
typeof props.name === 'number' ||
(Array.isArray(props.name) ? !!props.name.length : !!props.name),
'Form.List',
'Miss `name` prop.',
);
const { getPrefixCls } = React.useContext(ConfigContext); const { getPrefixCls } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('form', customizePrefixCls); const prefixCls = getPrefixCls('form', customizePrefixCls);

8
components/form/__tests__/__snapshots__/demo-extend.test.ts.snap

@ -7591,9 +7591,9 @@ exports[`renders components/form/demo/layout-can-wrap.tsx extend context correct
<label <label
class="ant-form-item-required ant-form-item-no-colon" class="ant-form-item-required ant-form-item-no-colon"
for="wrap_username" for="wrap_username"
title="正常标签文案" title="Normal label"
> >
正常标签文案 Normal label
</label> </label>
</div> </div>
<div <div
@ -7631,9 +7631,9 @@ exports[`renders components/form/demo/layout-can-wrap.tsx extend context correct
<label <label
class="ant-form-item-required ant-form-item-no-colon" class="ant-form-item-required ant-form-item-no-colon"
for="wrap_password" for="wrap_password"
title="超长标签文案超长标签文案" title="A super long label text"
> >
超长标签文案超长标签文案 A super long label text
</label> </label>
</div> </div>
<div <div

8
components/form/__tests__/__snapshots__/demo.test.tsx.snap

@ -4844,9 +4844,9 @@ exports[`renders components/form/demo/layout-can-wrap.tsx correctly 1`] = `
<label <label
class="ant-form-item-required ant-form-item-no-colon" class="ant-form-item-required ant-form-item-no-colon"
for="wrap_username" for="wrap_username"
title="正常标签文案" title="Normal label"
> >
正常标签文案 Normal label
</label> </label>
</div> </div>
<div <div
@ -4884,9 +4884,9 @@ exports[`renders components/form/demo/layout-can-wrap.tsx correctly 1`] = `
<label <label
class="ant-form-item-required ant-form-item-no-colon" class="ant-form-item-required ant-form-item-no-colon"
for="wrap_password" for="wrap_password"
title="超长标签文案超长标签文案" title="A super long label text"
> >
超长标签文案超长标签文案 A super long label text
</label> </label>
</div> </div>
<div <div

66
components/form/__tests__/list.test.tsx

@ -262,4 +262,70 @@ describe('Form.List', () => {
errorSpy.mockRestore(); errorSpy.mockRestore();
}); });
it('no warning when name is 0', () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
render(
<Form>
<Form.List name={0}>
{(fields) =>
fields.map((field) => (
<Form.Item {...field} key={field.key}>
<Input />
</Form.Item>
))
}
</Form.List>
</Form>,
);
expect(errorSpy).not.toHaveBeenCalled();
errorSpy.mockRestore();
});
it('warning when name is empty array', () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
render(
<Form>
<Form.List name={[]}>
{(fields) =>
fields.map((field) => (
<Form.Item {...field} key={field.key}>
<Input />
</Form.Item>
))
}
</Form.List>
</Form>,
);
expect(errorSpy).toHaveBeenCalled();
errorSpy.mockRestore();
});
it('warning when name is null', () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
render(
<Form>
<Form.List name={null!!}>
{(fields) =>
fields.map((field) => (
<Form.Item {...field} key={field.key}>
<Input />
</Form.Item>
))
}
</Form.List>
</Form>,
);
expect(errorSpy).toHaveBeenCalled();
errorSpy.mockRestore();
});
}); });

6
components/form/demo/layout-can-wrap.tsx

@ -1,5 +1,5 @@
import React from 'react';
import { Button, Form, Input } from 'antd'; import { Button, Form, Input } from 'antd';
import React from 'react';
const App: React.FC = () => ( const App: React.FC = () => (
<Form <Form
@ -11,11 +11,11 @@ const App: React.FC = () => (
colon={false} colon={false}
style={{ maxWidth: 600 }} style={{ maxWidth: 600 }}
> >
<Form.Item label="正常标签文案" name="username" rules={[{ required: true }]}> <Form.Item label="Normal label" name="username" rules={[{ required: true }]}>
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item label="超长标签文案超长标签文案" name="password" rules={[{ required: true }]}> <Form.Item label="A super long label text" name="password" rules={[{ required: true }]}>
<Input /> <Input />
</Form.Item> </Form.Item>

7
components/popconfirm/style/index.tsx

@ -12,6 +12,7 @@ const genBaseStyle: GenerateStyle<PopconfirmToken> = (token) => {
const { const {
componentCls, componentCls,
iconCls, iconCls,
antCls,
zIndexPopup, zIndexPopup,
colorText, colorText,
colorWarning, colorWarning,
@ -25,7 +26,10 @@ const genBaseStyle: GenerateStyle<PopconfirmToken> = (token) => {
return { return {
[componentCls]: { [componentCls]: {
zIndex: zIndexPopup, zIndex: zIndexPopup,
color: colorText,
[`&${antCls}-popover`]: {
fontSize,
},
[`${componentCls}-message`]: { [`${componentCls}-message`]: {
marginBottom: marginXS, marginBottom: marginXS,
@ -51,6 +55,7 @@ const genBaseStyle: GenerateStyle<PopconfirmToken> = (token) => {
[`${componentCls}-description`]: { [`${componentCls}-description`]: {
marginTop: marginXXS, marginTop: marginXXS,
color: colorText,
}, },
}, },

41
components/progress/style/index.tsx

@ -1,8 +1,8 @@
import type { CSSObject } from '@ant-design/cssinjs'; import type { CSSObject } from '@ant-design/cssinjs';
import { Keyframes } from '@ant-design/cssinjs'; import { Keyframes } from '@ant-design/cssinjs';
import { resetComponent } from '../../style';
import type { FullToken, GenerateStyle } from '../../theme/internal'; import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal'; import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import { resetComponent } from '../../style';
export interface ComponentToken {} export interface ComponentToken {}
@ -16,20 +16,23 @@ interface ProgressToken extends FullToken<'Progress'> {
progressActiveMotionDuration: string; progressActiveMotionDuration: string;
} }
const antProgressActive = new Keyframes('antProgressActive', { const genAntProgressActive = (isRtl?: boolean) => {
'0%': { const direction = isRtl ? '100%' : '-100%';
transform: 'translateX(-100%) scaleX(0)', return new Keyframes(`antProgress${isRtl ? 'RTL' : 'LTR'}Active`, {
opacity: 0.1, '0%': {
}, transform: `translateX(${direction}) scaleX(0)`,
'20%': { opacity: 0.1,
transform: 'translateX(-100%) scaleX(0)', },
opacity: 0.5, '20%': {
}, transform: `translateX(${direction}) scaleX(0)`,
to: { opacity: 0.5,
transform: 'translateX(0) scaleX(1)', },
opacity: 0, to: {
}, transform: 'translateX(0) scaleX(1)',
}); opacity: 0,
},
});
};
const genBaseStyle: GenerateStyle<ProgressToken> = (token) => { const genBaseStyle: GenerateStyle<ProgressToken> = (token) => {
const { componentCls: progressCls, iconCls: iconPrefixCls } = token; const { componentCls: progressCls, iconCls: iconPrefixCls } = token;
@ -116,7 +119,7 @@ const genBaseStyle: GenerateStyle<ProgressToken> = (token) => {
backgroundColor: token.colorBgContainer, backgroundColor: token.colorBgContainer,
borderRadius: token.progressLineRadius, borderRadius: token.progressLineRadius,
opacity: 0, opacity: 0,
animationName: antProgressActive, animationName: genAntProgressActive(),
animationDuration: token.progressActiveMotionDuration, animationDuration: token.progressActiveMotionDuration,
animationTimingFunction: token.motionEaseOutQuint, animationTimingFunction: token.motionEaseOutQuint,
animationIterationCount: 'infinite', animationIterationCount: 'infinite',
@ -124,6 +127,12 @@ const genBaseStyle: GenerateStyle<ProgressToken> = (token) => {
}, },
}, },
[`&${progressCls}-rtl${progressCls}-status-active`]: {
[`${progressCls}-bg::before`]: {
animationName: genAntProgressActive(true),
},
},
[`&${progressCls}-status-exception`]: { [`&${progressCls}-status-exception`]: {
[`${progressCls}-bg`]: { [`${progressCls}-bg`]: {
backgroundColor: token.colorError, backgroundColor: token.colorError,

2
components/select/style/index.tsx

@ -235,7 +235,7 @@ const genBaseStyle: GenerateStyle<SelectToken> = (token) => {
// ========================= Feedback ========================== // ========================= Feedback ==========================
[`${componentCls}-has-feedback`]: { [`${componentCls}-has-feedback`]: {
[`${componentCls}-clear`]: { [`${componentCls}-clear`]: {
insetInlineEnd: inputPaddingHorizontalBase + token.fontSize + token.paddingXXS, insetInlineEnd: inputPaddingHorizontalBase + token.fontSize + token.paddingXS,
}, },
}, },
}; };

8
components/spin/index.tsx

@ -61,10 +61,10 @@ function renderIndicator(prefixCls: string, props: SpinClassProps): React.ReactN
return ( return (
<span className={classNames(dotClassName, `${prefixCls}-dot-spin`)}> <span className={classNames(dotClassName, `${prefixCls}-dot-spin`)}>
<i className={`${prefixCls}-dot-item`} /> <i className={`${prefixCls}-dot-item`} key={1} />
<i className={`${prefixCls}-dot-item`} /> <i className={`${prefixCls}-dot-item`} key={2} />
<i className={`${prefixCls}-dot-item`} /> <i className={`${prefixCls}-dot-item`} key={3} />
<i className={`${prefixCls}-dot-item`} /> <i className={`${prefixCls}-dot-item`} key={4} />
</span> </span>
); );
} }

15
components/table/demo/drag-sorting.tsx

@ -1,9 +1,9 @@
import type { DragEndEvent } from '@dnd-kit/core'; import type { DragEndEvent } from '@dnd-kit/core';
import { DndContext } from '@dnd-kit/core'; import { DndContext, PointerSensor, useSensor, useSensors } from '@dnd-kit/core';
import { restrictToVerticalAxis } from '@dnd-kit/modifiers'; import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
import { import {
arrayMove,
SortableContext, SortableContext,
arrayMove,
useSortable, useSortable,
verticalListSortingStrategy, verticalListSortingStrategy,
} from '@dnd-kit/sortable'; } from '@dnd-kit/sortable';
@ -77,6 +77,15 @@ const App: React.FC = () => {
}, },
]); ]);
const sensors = useSensors(
useSensor(PointerSensor, {
activationConstraint: {
// https://docs.dndkit.com/api-documentation/sensors/pointer#activation-constraints
distance: 1,
},
}),
);
const onDragEnd = ({ active, over }: DragEndEvent) => { const onDragEnd = ({ active, over }: DragEndEvent) => {
if (active.id !== over?.id) { if (active.id !== over?.id) {
setDataSource((prev) => { setDataSource((prev) => {
@ -88,7 +97,7 @@ const App: React.FC = () => {
}; };
return ( return (
<DndContext modifiers={[restrictToVerticalAxis]} onDragEnd={onDragEnd}> <DndContext sensors={sensors} modifiers={[restrictToVerticalAxis]} onDragEnd={onDragEnd}>
<SortableContext <SortableContext
// rowKey array // rowKey array
items={dataSource.map((i) => i.key)} items={dataSource.map((i) => i.key)}

16
components/tag/demo/draggable.tsx

@ -1,14 +1,14 @@
import React, { useState } from 'react'; import { DndContext, PointerSensor, closestCenter, useSensor, useSensors } from '@dnd-kit/core';
import { Tag } from 'antd'; import type { DragEndEvent } from '@dnd-kit/core/dist/types/index';
import { DndContext, PointerSensor, useSensor, useSensors, closestCenter } from '@dnd-kit/core';
import { import {
arrayMove,
useSortable,
SortableContext, SortableContext,
arrayMove,
horizontalListSortingStrategy, horizontalListSortingStrategy,
useSortable,
} from '@dnd-kit/sortable'; } from '@dnd-kit/sortable';
import { Tag } from 'antd';
import type { FC } from 'react'; import type { FC } from 'react';
import type { DragEndEvent } from '@dnd-kit/core/dist/types/index'; import React, { useState } from 'react';
type Item = { type Item = {
id: number; id: number;
@ -25,14 +25,14 @@ const DraggableTag: FC<DraggableTagProps> = (props) => {
const commonStyle = { const commonStyle = {
cursor: 'move', cursor: 'move',
transition: 'unset', // 防止拖拽完毕之后元素抖动 transition: 'unset', // Prevent element from shaking after drag
}; };
const style = transform const style = transform
? { ? {
...commonStyle, ...commonStyle,
transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`, transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,
transition: isDragging ? 'unset' : transition, // 处理拖拽中的元素不跟手的问题 transition: isDragging ? 'unset' : transition, // Improve performance/visual effect when dragging
} }
: commonStyle; : commonStyle;

1
components/time-picker/index.en-US.md

@ -50,6 +50,7 @@ dayjs.extend(customParseFormat)
| allowClear | Whether allow clearing text | boolean | true | | | allowClear | Whether allow clearing text | boolean | true | |
| autoFocus | If get focus when component mounted | boolean | false | | | autoFocus | If get focus when component mounted | boolean | false | |
| bordered | Whether has border style | boolean | true | | | bordered | Whether has border style | boolean | true | |
| cellRender | Custom rendering function for picker cells | (current: number, info: { originNode: React.ReactElement, today: dayjs, range?: 'start' \| 'end', subType: 'hour' \| 'minute' \| 'second' \| 'meridiem' }) => React.ReactNode | - | 5.4.0 |
| changeOnBlur | Trigger `change` when blur. e.g. datetime picker no need click confirm button | boolean | false | 5.5.0 | | changeOnBlur | Trigger `change` when blur. e.g. datetime picker no need click confirm button | boolean | false | 5.5.0 |
| className | The className of picker | string | - | | | className | The className of picker | string | - | |
| clearIcon | The custom clear icon | ReactNode | - | | | clearIcon | The custom clear icon | ReactNode | - | |

1
components/time-picker/index.zh-CN.md

@ -50,6 +50,7 @@ dayjs.extend(customParseFormat)
| allowClear | 是否展示清除按钮 | boolean | true | | | allowClear | 是否展示清除按钮 | boolean | true | |
| autoFocus | 自动获取焦点 | boolean | false | | | autoFocus | 自动获取焦点 | boolean | false | |
| bordered | 是否有边框 | boolean | true | | | bordered | 是否有边框 | boolean | true | |
| cellRender | 自定义单元格的内容 | (current: number, info: { originNode: React.ReactNode, today: dayjs, range?: 'start' \| 'end', subType: 'hour' \| 'minute' \| 'second' \| 'meridiem' }) => React.ReactNode | - | 5.4.0 |
| changeOnBlur | 失去焦点时触发 `change` 事件,例如 datetime 下不再需要点击确认按钮 | boolean | false | 5.5.0 | | changeOnBlur | 失去焦点时触发 `change` 事件,例如 datetime 下不再需要点击确认按钮 | boolean | false | 5.5.0 |
| className | 选择器类名 | string | - | | | className | 选择器类名 | string | - | |
| clearIcon | 自定义的清除图标 | ReactNode | - | | | clearIcon | 自定义的清除图标 | ReactNode | - | |

61
components/transfer/ListBody.tsx

@ -1,14 +1,17 @@
import classNames from 'classnames'; import classNames from 'classnames';
import * as React from 'react'; import * as React from 'react';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import type { KeyWiseTransferItem } from '.'; import type { KeyWiseTransferItem } from '.';
import type { PaginationType } from './interface';
import type { RenderedItem, TransferListProps } from './list';
import Pagination from '../pagination'; import Pagination from '../pagination';
import ListItem from './ListItem'; import ListItem from './ListItem';
import type { PaginationType } from './interface';
import type { RenderedItem, TransferListProps } from './list';
export const OmitProps = ['handleFilter', 'handleClear', 'checkedKeys'] as const; export const OmitProps = ['handleFilter', 'handleClear', 'checkedKeys'] as const;
export type OmitProp = typeof OmitProps[number]; export type OmitProp = (typeof OmitProps)[number];
type PartialTransferListProps<RecordType> = Omit<TransferListProps<RecordType>, OmitProp>; type PartialTransferListProps<RecordType> = Omit<TransferListProps<RecordType>, OmitProp>;
type ExistPagination = Exclude<PaginationType, boolean>;
export interface TransferListBodyProps<RecordType> extends PartialTransferListProps<RecordType> { export interface TransferListBodyProps<RecordType> extends PartialTransferListProps<RecordType> {
filteredItems: RecordType[]; filteredItems: RecordType[];
@ -16,25 +19,15 @@ export interface TransferListBodyProps<RecordType> extends PartialTransferListPr
selectedKeys: string[]; selectedKeys: string[];
} }
const parsePagination = (pagination?: PaginationType) => { const parsePagination = (pagination?: ExistPagination) => {
if (!pagination) {
return null;
}
const defaultPagination: PaginationType = { const defaultPagination: PaginationType = {
pageSize: 10,
simple: true, simple: true,
showSizeChanger: false, showSizeChanger: false,
showLessItems: false, showLessItems: false,
}; };
if (typeof pagination === 'object') { return { ...defaultPagination, ...pagination };
return { ...defaultPagination, ...pagination };
}
return defaultPagination;
}; };
export interface ListBodyRef<RecordType extends KeyWiseTransferItem> { export interface ListBodyRef<RecordType extends KeyWiseTransferItem> {
items?: RenderedItem<RecordType>[]; items?: RenderedItem<RecordType>[];
} }
@ -57,16 +50,28 @@ const TransferListBody: React.ForwardRefRenderFunction<
onItemSelect, onItemSelect,
onItemRemove, onItemRemove,
} = props; } = props;
const [current, setCurrent] = React.useState<number>(1); const [current, setCurrent] = React.useState<number>(1);
const mergedPagination = React.useMemo(() => {
if (!pagination) {
return null;
}
const convertPagination = typeof pagination === 'object' ? pagination : {};
return parsePagination(convertPagination);
}, [pagination]);
const [pageSize, setPageSize] = useMergedState<number>(10, {
value: mergedPagination?.pageSize,
});
React.useEffect(() => { React.useEffect(() => {
const mergedPagination = parsePagination(pagination);
if (mergedPagination) { if (mergedPagination) {
const maxPageCount = Math.ceil(filteredRenderItems.length / mergedPagination.pageSize!); const maxPageCount = Math.ceil(filteredRenderItems.length / pageSize!);
setCurrent(Math.min(current, maxPageCount)); setCurrent(Math.min(current, maxPageCount));
} }
}, [filteredRenderItems, pagination]); }, [filteredRenderItems, mergedPagination, pageSize]);
const onClick = (item: RecordType) => { const onClick = (item: RecordType) => {
onItemSelect?.(item.key, !selectedKeys.includes(item.key)); onItemSelect?.(item.key, !selectedKeys.includes(item.key));
@ -80,33 +85,33 @@ const TransferListBody: React.ForwardRefRenderFunction<
setCurrent(cur); setCurrent(cur);
}; };
const onSizeChange = (cur: number, size: number) => {
setCurrent(cur);
setPageSize(size);
};
const memoizedItems = React.useMemo<RenderedItem<RecordType>[]>(() => { const memoizedItems = React.useMemo<RenderedItem<RecordType>[]>(() => {
const mergedPagination = parsePagination(pagination);
const displayItems = mergedPagination const displayItems = mergedPagination
? filteredRenderItems.slice( ? filteredRenderItems.slice((current - 1) * pageSize!, current * pageSize!)
(current - 1) * mergedPagination.pageSize!,
current * mergedPagination.pageSize!,
)
: filteredRenderItems; : filteredRenderItems;
return displayItems; return displayItems;
}, [current, filteredRenderItems, pagination]); }, [current, filteredRenderItems, mergedPagination, pageSize]);
React.useImperativeHandle(ref, () => ({ items: memoizedItems })); React.useImperativeHandle(ref, () => ({ items: memoizedItems }));
const mergedPagination = parsePagination(pagination);
const paginationNode: React.ReactNode = mergedPagination ? ( const paginationNode: React.ReactNode = mergedPagination ? (
<Pagination <Pagination
size="small" size="small"
disabled={globalDisabled} disabled={globalDisabled}
simple={mergedPagination.simple} simple={mergedPagination.simple}
pageSize={mergedPagination.pageSize} pageSize={pageSize}
showLessItems={mergedPagination.showLessItems} showLessItems={mergedPagination.showLessItems}
showSizeChanger={mergedPagination.showSizeChanger} showSizeChanger={mergedPagination.showSizeChanger}
className={`${prefixCls}-pagination`} className={`${prefixCls}-pagination`}
total={filteredRenderItems.length} total={filteredRenderItems.length}
current={current} current={current}
onChange={onPageChange} onChange={onPageChange}
onShowSizeChange={onSizeChange}
/> />
) : null; ) : null;

39
components/transfer/__tests__/index.test.tsx

@ -73,6 +73,19 @@ const searchTransferProps = {
targetKeys: ['3', '4'], targetKeys: ['3', '4'],
}; };
const generateData = (n = 20) => {
const data = [];
for (let i = 0; i < n; i++) {
data.push({
key: `${i}`,
title: `content${i}`,
description: `description of content${i}`,
chosen: false,
});
}
return data;
};
describe('Transfer', () => { describe('Transfer', () => {
mountTest(Transfer); mountTest(Transfer);
rtlTest(Transfer); rtlTest(Transfer);
@ -595,6 +608,32 @@ describe('Transfer', () => {
); );
await waitFor(() => expect(getAllByTitle('1/1')).toHaveLength(2)); await waitFor(() => expect(getAllByTitle('1/1')).toHaveLength(2));
}); });
it('should support change pageSize', () => {
const dataSource = generateData();
const { container } = render(
<Transfer dataSource={dataSource} pagination={{ showSizeChanger: true, simple: false }} />,
);
fireEvent.mouseDown(container.querySelector('.ant-select-selector')!);
fireEvent.click(container.querySelectorAll('.ant-select-item-option')[1]);
expect(container.querySelectorAll('.ant-transfer-list-content-item').length).toBe(20);
});
it('should be used first when pagination has pagesize', () => {
const dataSource = generateData(30);
const { container } = render(
<Transfer
dataSource={dataSource}
pagination={{ showSizeChanger: true, simple: false, pageSize: 20 }}
/>,
);
fireEvent.mouseDown(container.querySelector('.ant-select-selector')!);
fireEvent.click(container.querySelectorAll('.ant-select-item-option')[2]);
expect(container.querySelectorAll('.ant-transfer-list-content-item').length).toBe(20);
});
}); });
it('remove by click icon', () => { it('remove by click icon', () => {

1
components/transfer/list.tsx

@ -110,7 +110,6 @@ const TransferList = <RecordType extends KeyWiseTransferItem>(
} = props; } = props;
const [filterValue, setFilterValue] = useState<string>(''); const [filterValue, setFilterValue] = useState<string>('');
const listBodyRef = useRef<ListBodyRef<RecordType>>({}); const listBodyRef = useRef<ListBodyRef<RecordType>>({});
const internalHandleFilter = (e: React.ChangeEvent<HTMLInputElement>) => { const internalHandleFilter = (e: React.ChangeEvent<HTMLInputElement>) => {

8
components/transfer/style/index.ts

@ -101,6 +101,7 @@ const genTransferListStyle: GenerateStyle<TransferToken> = (token: TransferToken
marginXS, marginXS,
paddingSM, paddingSM,
lineType, lineType,
antCls,
iconCls, iconCls,
motionDurationSlow, motionDurationSlow,
controlItemBgHover, controlItemBgHover,
@ -174,8 +175,9 @@ const genTransferListStyle: GenerateStyle<TransferToken> = (token: TransferToken
display: 'flex', display: 'flex',
flex: 'auto', flex: 'auto',
flexDirection: 'column', flexDirection: 'column',
overflow: 'hidden',
fontSize: token.fontSize, fontSize: token.fontSize,
// https://blog.csdn.net/qq449245884/article/details/107373672/
minHeight: 0,
'&-search-wrapper': { '&-search-wrapper': {
position: 'relative', position: 'relative',
@ -262,6 +264,10 @@ const genTransferListStyle: GenerateStyle<TransferToken> = (token: TransferToken
padding: `${token.paddingXS}px 0`, padding: `${token.paddingXS}px 0`,
textAlign: 'end', textAlign: 'end',
borderTop: `${lineWidth}px ${lineType} ${colorSplit}`, borderTop: `${lineWidth}px ${lineType} ${colorSplit}`,
[`${antCls}-pagination-options`]: {
paddingInlineEnd: token.paddingXS,
},
}, },
'&-body-not-found': { '&-body-not-found': {

12
components/tree/demo/drag-debug.tsx

@ -1,9 +1,9 @@
/* eslint-disable @typescript-eslint/no-shadow */ /* eslint-disable @typescript-eslint/no-shadow */
import React from 'react';
import type { TreeProps } from 'antd';
import { Tree, Switch } from 'antd';
import { CarryOutOutlined } from '@ant-design/icons'; import { CarryOutOutlined } from '@ant-design/icons';
import type { TreeProps } from 'antd';
import { Switch, Tree } from 'antd';
import type { DataNode } from 'rc-tree/lib/interface'; import type { DataNode } from 'rc-tree/lib/interface';
import React from 'react';
const x = 3; const x = 3;
const y = 2; const y = 2;
@ -40,7 +40,7 @@ const App: React.FC = () => {
const onDragEnter: TreeProps['onDragEnter'] = (info) => { const onDragEnter: TreeProps['onDragEnter'] = (info) => {
console.log(info); console.log(info);
// expandedKeys 需要受控时设置 // expandedKeys, set it when controlled is needed
setExpandedKeys(info.expandedKeys); setExpandedKeys(info.expandedKeys);
}; };
@ -79,7 +79,7 @@ const App: React.FC = () => {
// Drop on the content // Drop on the content
loop(data, dropKey, (item) => { loop(data, dropKey, (item) => {
item.children = item.children || []; item.children = item.children || [];
// where to insert 示例添加到尾部,可以是随意位置 // where to insert. New item was inserted to the end of the array in this example, but can be anywhere
item.children.push(dragObj); item.children.push(dragObj);
}); });
} else if ( } else if (
@ -89,7 +89,7 @@ const App: React.FC = () => {
) { ) {
loop(data, dropKey, (item) => { loop(data, dropKey, (item) => {
item.children = item.children || []; item.children = item.children || [];
// where to insert 示例添加到头部,可以是随意位置 // where to insert. New item was inserted to the start of the array in this example, but can be anywhere
item.children.unshift(dragObj); item.children.unshift(dragObj);
}); });
} else { } else {

8
components/tree/demo/draggable.tsx

@ -1,6 +1,6 @@
import React, { useState } from 'react';
import { Tree } from 'antd'; import { Tree } from 'antd';
import type { DataNode, TreeProps } from 'antd/es/tree'; import type { DataNode, TreeProps } from 'antd/es/tree';
import React, { useState } from 'react';
const x = 3; const x = 3;
const y = 2; const y = 2;
@ -36,7 +36,7 @@ const App: React.FC = () => {
const onDragEnter: TreeProps['onDragEnter'] = (info) => { const onDragEnter: TreeProps['onDragEnter'] = (info) => {
console.log(info); console.log(info);
// expandedKeys 需要受控时设置 // expandedKeys, set it when controlled is needed
// setExpandedKeys(info.expandedKeys) // setExpandedKeys(info.expandedKeys)
}; };
@ -74,7 +74,7 @@ const App: React.FC = () => {
// Drop on the content // Drop on the content
loop(data, dropKey, (item) => { loop(data, dropKey, (item) => {
item.children = item.children || []; item.children = item.children || [];
// where to insert 示例添加到头部,可以是随意位置 // where to insert. New item was inserted to the start of the array in this example, but can be anywhere
item.children.unshift(dragObj); item.children.unshift(dragObj);
}); });
} else if ( } else if (
@ -84,7 +84,7 @@ const App: React.FC = () => {
) { ) {
loop(data, dropKey, (item) => { loop(data, dropKey, (item) => {
item.children = item.children || []; item.children = item.children || [];
// where to insert 示例添加到头部,可以是随意位置 // where to insert. New item was inserted to the start of the array in this example, but can be anywhere
item.children.unshift(dragObj); item.children.unshift(dragObj);
// in previous version, we use item.children.push(dragObj) to insert the // in previous version, we use item.children.push(dragObj) to insert the
// item to the tail of the children // item to the tail of the children

16
components/typography/Base/index.tsx

@ -11,17 +11,17 @@ import useMergedState from 'rc-util/lib/hooks/useMergedState';
import omit from 'rc-util/lib/omit'; import omit from 'rc-util/lib/omit';
import { composeRef } from 'rc-util/lib/ref'; import { composeRef } from 'rc-util/lib/ref';
import * as React from 'react'; import * as React from 'react';
import { isStyleSupport } from '../../_util/styleChecker';
import TransButton from '../../_util/transButton';
import { ConfigContext } from '../../config-provider'; import { ConfigContext } from '../../config-provider';
import useLocale from '../../locale/useLocale'; import useLocale from '../../locale/useLocale';
import type { TooltipProps } from '../../tooltip'; import type { TooltipProps } from '../../tooltip';
import Tooltip from '../../tooltip'; import Tooltip from '../../tooltip';
import { isStyleSupport } from '../../_util/styleChecker';
import TransButton from '../../_util/transButton';
import Editable from '../Editable'; import Editable from '../Editable';
import useMergedConfig from '../hooks/useMergedConfig';
import useUpdatedEffect from '../hooks/useUpdatedEffect';
import type { TypographyProps } from '../Typography'; import type { TypographyProps } from '../Typography';
import Typography from '../Typography'; import Typography from '../Typography';
import useMergedConfig from '../hooks/useMergedConfig';
import useUpdatedEffect from '../hooks/useUpdatedEffect';
import Ellipsis from './Ellipsis'; import Ellipsis from './Ellipsis';
import EllipsisTooltip from './EllipsisTooltip'; import EllipsisTooltip from './EllipsisTooltip';
@ -193,7 +193,7 @@ const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
// ========================== Copyable ========================== // ========================== Copyable ==========================
const [enableCopy, copyConfig] = useMergedConfig<CopyConfig>(copyable); const [enableCopy, copyConfig] = useMergedConfig<CopyConfig>(copyable);
const [copied, setCopied] = React.useState(false); const [copied, setCopied] = React.useState(false);
const copyIdRef = React.useRef<number>(); const copyIdRef = React.useRef<NodeJS.Timeout | null>(null);
const copyOptions: Pick<CopyConfig, 'format'> = {}; const copyOptions: Pick<CopyConfig, 'format'> = {};
if (copyConfig.format) { if (copyConfig.format) {
@ -201,7 +201,9 @@ const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
} }
const cleanCopyId = () => { const cleanCopyId = () => {
window.clearTimeout(copyIdRef.current!); if (copyIdRef.current) {
clearTimeout(copyIdRef.current);
}
}; };
const onCopyClick = (e?: React.MouseEvent<HTMLDivElement>) => { const onCopyClick = (e?: React.MouseEvent<HTMLDivElement>) => {
@ -214,7 +216,7 @@ const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
// Trigger tips update // Trigger tips update
cleanCopyId(); cleanCopyId();
copyIdRef.current = window.setTimeout(() => { copyIdRef.current = setTimeout(() => {
setCopied(false); setCopied(false);
}, 3000); }, 3000);

31
docs/react/faq.en-US.md

@ -234,3 +234,34 @@ Here are some typical wrong examples:
## Do you guys have any channel or website for submitting monetary donations, like through PayPal or Alipay? ## Do you guys have any channel or website for submitting monetary donations, like through PayPal or Alipay?
[https://opencollective.com/ant-design](https://opencollective.com/ant-design) [https://opencollective.com/ant-design](https://opencollective.com/ant-design)
## Use Form's `setFieldsValue` method to report an error if the object type contains `null`
When we try to set the form value using the `setFieldsValue` method in the form instance of the form component, if the passed object contains the type null, such as:
```tsx
// This is not real world code, just for explain
import { Form } from 'antd';
type Test = {
value: string[] | null;
};
export default () => {
const [form] = Form.useForm<Test>();
form.setFieldsValue({
value: null, // Error: Type "null" cannot be assigned to type "string[] | undefined".
});
};
```
If you encounter the above error, please check the current project `tsconfig.json` contains the following configuration:
```json
{
"strictNullChecks": true
}
```
The above problem occurs if `strictNullChecks` is set to `true`, If you can determine the project don't need this configuration (see [strictNullChecks](https://www.typescriptlang.org/zh/tsconfig#strictNullChecks) to judge whether need the configuration). You can try changing to `false` to turn off the control strict check. However, if you do need to enable this feature, you can avoid this situation by using other types instead of `null` when designing types

31
docs/react/faq.zh-CN.md

@ -256,3 +256,34 @@ import { ConfigProvider } from 'antd';
## 你们有接受捐助的渠道吗,比如支付宝或者微信支付? ## 你们有接受捐助的渠道吗,比如支付宝或者微信支付?
[https://opencollective.com/ant-design](https://opencollective.com/ant-design) [https://opencollective.com/ant-design](https://opencollective.com/ant-design)
## 使用表单组件的 `setFieldsValue` 方法如果对象类型中含有 `null` 时 TS 类型报错
当我们尝试使用表单组件的表单实例当中的 `setFieldsValue` 方法设置表单值时,如果在传入的对象中包含有 `null` 类型,如:
```tsx
// This is not real world code, just for explain
import { Form } from 'antd';
type Test = {
value: string[] | null;
};
export default () => {
const [form] = Form.useForm<Test>();
form.setFieldsValue({
value: null, // Error: 不能将类型“null”分配给类型“string[] | undefined”。
});
};
```
如果你遇到上述报错,请检查当前项目的 `tsconfig.json` 中是否包含如下配置:
```json
{
"strictNullChecks": true
}
```
如果 `strictNullChecks` 的值被设置为 `true` 就会出现上述问题,如果你确定项目中可以不需要这个检测配置(查看[strictNullChecks](https://www.typescriptlang.org/zh/tsconfig#strictNullChecks)判断是否需要该配置),可以尝试改为 `false` 关闭控制严格检查功能。但如果你确实需要开启这个功能,那么,你可以在设计类型时,使用其他类型替代 `null` 以避免出现这种情况。

2
docs/spec/data-format.zh-CN.md

@ -66,7 +66,7 @@ title: 数据格式
<img class="preview-img bad" alt="不推荐示例" description="“千”不能以单位的形式展示。" src="https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*iuEARoq_-o0AAAAAAAAAAABkARQnAQ"> <img class="preview-img bad" alt="不推荐示例" description="“千”不能以单位的形式展示。" src="https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*iuEARoq_-o0AAAAAAAAAAABkARQnAQ">
</ImagePreview> </ImagePreview>
**大额计量:** 如果一个金额很大,那么数值中的“万”“亿”单位可采用汉字。如果一个数值很大,那么数值中的“万”“亿”单位可采用汉字。 **大额计量:** 如果一个数值很大,那么数值中的“万”“亿”单位可采用汉字。
### 日期时间 ### 日期时间

14
docs/spec/transition.en-US.md

@ -16,19 +16,19 @@ Our Gray Matter are wired to react to dynamic things like movement,shape change
## Maintain Context While Changing Views ## Maintain Context While Changing Views
<video class="transition-video-player" alt="example of Slide In and Slide Out <video class="transition-video-player" alt="example of Slide In and Slide Out
" src="https://os.alipayobjects.com/rmsportal/EejaUGsyExkXyXr.mp4" /> " src="https://os.alipayobjects.com/rmsportal/EejaUGsyExkXyXr.mp4"></video>
Slide In and Slide Out: Create an illusion of virtual space. Slide In and Slide Out: Create an illusion of virtual space.
<br> <br>
<video class="transition-video-player" alt="example of Carousel" src="https://os.alipayobjects.com/rmsportal/GIutPgZMTyfFfrH.mp4" /> <video class="transition-video-player" alt="example of Carousel" src="https://os.alipayobjects.com/rmsportal/GIutPgZMTyfFfrH.mp4"></video>
Carousel: Carousels are great for extending virtual space. Carousel: Carousels are great for extending virtual space.
<br> <br>
<video class="transition-video-player" alt="example of Accordion" src="https://os.alipayobjects.com/rmsportal/ERKhqHlcHiCDSQu.mp4" /> <video class="transition-video-player" alt="example of Accordion" src="https://os.alipayobjects.com/rmsportal/ERKhqHlcHiCDSQu.mp4"></video>
Accordion: Accordion helps maintain context while switching views. Accordion: Accordion helps maintain context while switching views.
@ -38,25 +38,25 @@ Accordion: Accordion helps maintain context while switching views.
## Explain What Just Happened ## Explain What Just Happened
<video class="transition-video-player" alt="example of Adding an Object" description="When an object is added, the highlighted area shows it to the user. The highlight fades in several seconds in order not to interfere the user flow." src="https://os.alipayobjects.com/rmsportal/FqkQMyFqNqielOw.mp4" /> <video class="transition-video-player" alt="example of Adding an Object" description="When an object is added, the highlighted area shows it to the user. The highlight fades in several seconds in order not to interfere the user flow." src="https://os.alipayobjects.com/rmsportal/FqkQMyFqNqielOw.mp4"></video>
Adding an Object: Add an object in the table or chart. Adding an Object: Add an object in the table or chart.
<br> <br>
<video class="transition-video-player" alt="example of Deleting Objects" src="https://os.alipayobjects.com/rmsportal/pnNkNIMoowmGUQy.mp4" /> <video class="transition-video-player" alt="example of Deleting Objects" src="https://os.alipayobjects.com/rmsportal/pnNkNIMoowmGUQy.mp4"></video>
Deleting an Object: Delete an object in the table or chart. Deleting an Object: Delete an object in the table or chart.
<br> <br>
<video class="transition-video-player" alt="example of Modifying an object" description="Status No.1: The user modifies the value of Detail. <br>Status No.2: After the user click the save button, a yellow fill is displayed in the grid of Detail, which indicates the change of the object. <br>Status No.3: The fill fades in several seconds and returned to normal." src="https://os.alipayobjects.com/rmsportal/XrUIWmsmOlEnZGc.mp4" /> <video class="transition-video-player" alt="example of Modifying an object" description="Status No.1: The user modifies the value of Detail. <br>Status No.2: After the user click the save button, a yellow fill is displayed in the grid of Detail, which indicates the change of the object. <br>Status No.3: The fill fades in several seconds and returned to normal." src="https://os.alipayobjects.com/rmsportal/XrUIWmsmOlEnZGc.mp4"></video>
Modifying an Object: Modify an object in the table or chart. Modifying an Object: Modify an object in the table or chart.
<br> <br>
<video class="transition-video-player" alt="example of Calling out an Object" src="https://os.alipayobjects.com/rmsportal/gSNilqbiXOufDXF.mp4" /> <video class="transition-video-player" alt="example of Calling out an Object" src="https://os.alipayobjects.com/rmsportal/gSNilqbiXOufDXF.mp4"></video>
Calling out an Object: Click the page element and call out a new object. Calling out an Object: Click the page element and call out a new object.

14
docs/spec/transition.zh-CN.md

@ -15,19 +15,19 @@ title: 巧用过渡
## 在视图变化时保持上下文 ## 在视图变化时保持上下文
<video class="transition-video-player" alt="滑入与滑出示例" src="https://os.alipayobjects.com/rmsportal/EejaUGsyExkXyXr.mp4" /> <video class="transition-video-player" alt="滑入与滑出示例" src="https://os.alipayobjects.com/rmsportal/EejaUGsyExkXyXr.mp4"></video>
滑入与滑出:可以有效构建虚拟空间。 滑入与滑出:可以有效构建虚拟空间。
<br> <br>
<video class="transition-video-player" alt="传送带示例" src="https://os.alipayobjects.com/rmsportal/GIutPgZMTyfFfrH.mp4" /> <video class="transition-video-player" alt="传送带示例" src="https://os.alipayobjects.com/rmsportal/GIutPgZMTyfFfrH.mp4"></video>
传送带:可极大地扩展虚拟空间。 传送带:可极大地扩展虚拟空间。
<br> <br>
<video class="transition-video-player" alt="折叠窗口示例" src="https://os.alipayobjects.com/rmsportal/ERKhqHlcHiCDSQu.mp4" /> <video class="transition-video-player" alt="折叠窗口示例" src="https://os.alipayobjects.com/rmsportal/ERKhqHlcHiCDSQu.mp4"></video>
折叠窗口:在视图切换时,有助于保持上下文,同时也能拓展虚拟空间。 折叠窗口:在视图切换时,有助于保持上下文,同时也能拓展虚拟空间。
@ -37,25 +37,25 @@ title: 巧用过渡
## 解释刚刚发生了什么 ## 解释刚刚发生了什么
<video class="transition-video-player" alt="对象增加示例" description="新增一条对象时,该行「高亮」告知用户这是新增项;几秒后「高亮」消失,以免过度干扰用户。" src="https://os.alipayobjects.com/rmsportal/FqkQMyFqNqielOw.mp4" /> <video class="transition-video-player" alt="对象增加示例" description="新增一条对象时,该行「高亮」告知用户这是新增项;几秒后「高亮」消失,以免过度干扰用户。" src="https://os.alipayobjects.com/rmsportal/FqkQMyFqNqielOw.mp4"></video>
对象增加:在列表/表格中,新增了一个对象。 对象增加:在列表/表格中,新增了一个对象。
<br> <br>
<video class="transition-video-player" alt="对象删除示例" src="https://os.alipayobjects.com/rmsportal/pnNkNIMoowmGUQy.mp4" /> <video class="transition-video-player" alt="对象删除示例" src="https://os.alipayobjects.com/rmsportal/pnNkNIMoowmGUQy.mp4"></video>
对象删除:在列表/表格中,删除了一个对象。 对象删除:在列表/表格中,删除了一个对象。
<br> <br>
<video class="transition-video-player" alt="对象更改示例" description="状态一:用户更改了「详情」中的值;<br>状态二:用户点击「保存」后,详情所在的网格出现「黄底」,表明该对象发生了更改;<br>状态三:底色持续几秒后,恢复正常。" src="https://os.alipayobjects.com/rmsportal/XrUIWmsmOlEnZGc.mp4" /> <video class="transition-video-player" alt="对象更改示例" description="状态一:用户更改了「详情」中的值;<br>状态二:用户点击「保存」后,详情所在的网格出现「黄底」,表明该对象发生了更改;<br>状态三:底色持续几秒后,恢复正常。" src="https://os.alipayobjects.com/rmsportal/XrUIWmsmOlEnZGc.mp4"></video>
对象更改:在列表/表格中,更改了一个对象。 对象更改:在列表/表格中,更改了一个对象。
<br> <br>
<video class="transition-video-player" alt="对象呼出示例" src="https://os.alipayobjects.com/rmsportal/gSNilqbiXOufDXF.mp4" /> <video class="transition-video-player" alt="对象呼出示例" src="https://os.alipayobjects.com/rmsportal/gSNilqbiXOufDXF.mp4"></video>
对象呼出:点击页面中元素,呼出一个新对象。 对象呼出:点击页面中元素,呼出一个新对象。

7
package.json

@ -1,6 +1,6 @@
{ {
"name": "antd", "name": "antd",
"version": "5.6.3", "version": "5.6.4",
"packageManager": "^npm@9.0.0", "packageManager": "^npm@9.0.0",
"description": "An enterprise-class UI design language and React components implementation", "description": "An enterprise-class UI design language and React components implementation",
"title": "Ant Design", "title": "Ant Design",
@ -128,7 +128,7 @@
"rc-dialog": "~9.1.0", "rc-dialog": "~9.1.0",
"rc-drawer": "~6.2.0", "rc-drawer": "~6.2.0",
"rc-dropdown": "~4.1.0", "rc-dropdown": "~4.1.0",
"rc-field-form": "~1.33.0", "rc-field-form": "~1.34.0",
"rc-image": "~6.1.0", "rc-image": "~6.1.0",
"rc-input": "~1.1.0", "rc-input": "~1.1.0",
"rc-input-number": "~8.0.0", "rc-input-number": "~8.0.0",
@ -299,8 +299,7 @@
"typescript": "~5.1.3", "typescript": "~5.1.3",
"vanilla-jsoneditor": "^0.17.1", "vanilla-jsoneditor": "^0.17.1",
"webpack-bundle-analyzer": "^4.1.0", "webpack-bundle-analyzer": "^4.1.0",
"xhr-mock": "^2.4.1", "xhr-mock": "^2.4.1"
"file-system-cache": "2.3.0"
}, },
"peerDependencies": { "peerDependencies": {
"react": ">=16.9.0", "react": ">=16.9.0",

Loading…
Cancel
Save