chore: merge master into feature

This commit is contained in:
栗嘉男 2023-07-04 11:41:25 +08:00
commit 60a2ae6319
44 changed files with 1826 additions and 1093 deletions

View File

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

View File

@ -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
`2023-06-25`

View File

@ -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
`2023-06-25`

View File

@ -13,7 +13,7 @@ Application wrapper for some global usages.
## When To Use
- 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
@ -95,7 +95,7 @@ export default () => {
return null;
};
export { message, notification, modal };
export { message, modal, notification };
```
```tsx

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -384,4 +384,19 @@ describe('Breadcrumb', () => {
);
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();
});
});

View File

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

View File

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

View File

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

View File

@ -83,7 +83,7 @@ The following APIs are shared by DatePicker, RangePicker.
| 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 |
| 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 | |
| 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) | |
@ -187,7 +187,7 @@ Added in `4.1.0`.
| --- | --- | --- | --- | --- |
| 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 |
| 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/)] | - | |
| defaultValue | To set default date | \[[dayjs](https://day.js.org/), [dayjs](https://day.js.org/)] | - | |
| 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
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
import 'dayjs/plugin/updateLocale';
import updateLocale from 'dayjs/plugin/updateLocale';
dayjs.extend(updateLocale);
dayjs.updateLocale('zh-cn', {
weekStart: 0,
});

View File

@ -84,7 +84,7 @@ import locale from 'antd/locale/zh_CN';
| className | 选择器 className | string | - | |
| dateRender | 自定义日期单元格的内容5.4.0 起用 `cellRender` 代替 | function(currentDate: dayjs, today: dayjs) => React.ReactNode | - | < 5.4.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 | |
| 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) | |
@ -188,7 +188,7 @@ import locale from 'antd/locale/zh_CN';
| --- | --- | --- | --- | --- |
| allowEmpty | 允许起始项部分为空 | \[boolean, boolean] | \[false, false] | |
| 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/)\[] | - | |
| defaultValue | 默认日期 | [dayjs](https://day.js.org/)\[] | - | |
| disabled | 禁用起始项 | \[boolean, boolean] | - | |
@ -243,8 +243,10 @@ export type FormatType = Generic | GenericFn | Array<Generic | GenericFn>;
```js
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
import 'dayjs/plugin/updateLocale';
import updateLocale from 'dayjs/plugin/updateLocale';
dayjs.extend(updateLocale);
dayjs.updateLocale('zh-cn', {
weekStart: 0,
});

View File

@ -1,8 +1,8 @@
import { List } from 'rc-field-form';
import type { StoreValue, ValidatorRule } from 'rc-field-form/lib/interface';
import * as React from 'react';
import { ConfigContext } from '../config-provider';
import warning from '../_util/warning';
import { ConfigContext } from '../config-provider';
import { FormItemPrefixContext } from './context';
export interface FormListFieldData {
@ -35,7 +35,12 @@ const FormList: React.FC<FormListProps> = ({
children,
...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 prefixCls = getPrefixCls('form', customizePrefixCls);

View File

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

View File

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

View File

@ -262,4 +262,70 @@ describe('Form.List', () => {
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();
});
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,9 @@
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 {
arrayMove,
SortableContext,
arrayMove,
useSortable,
verticalListSortingStrategy,
} 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) => {
if (active.id !== over?.id) {
setDataSource((prev) => {
@ -88,7 +97,7 @@ const App: React.FC = () => {
};
return (
<DndContext modifiers={[restrictToVerticalAxis]} onDragEnd={onDragEnd}>
<DndContext sensors={sensors} modifiers={[restrictToVerticalAxis]} onDragEnd={onDragEnd}>
<SortableContext
// rowKey array
items={dataSource.map((i) => i.key)}

View File

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

View File

@ -50,6 +50,7 @@ dayjs.extend(customParseFormat)
| allowClear | Whether allow clearing text | boolean | true | |
| autoFocus | If get focus when component mounted | boolean | false | |
| 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 |
| className | The className of picker | string | - | |
| clearIcon | The custom clear icon | ReactNode | - | |

View File

@ -50,6 +50,7 @@ dayjs.extend(customParseFormat)
| allowClear | 是否展示清除按钮 | boolean | true | |
| autoFocus | 自动获取焦点 | boolean | false | |
| 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 |
| className | 选择器类名 | string | - | |
| clearIcon | 自定义的清除图标 | ReactNode | - | |

View File

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

View File

@ -73,6 +73,19 @@ const searchTransferProps = {
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', () => {
mountTest(Transfer);
rtlTest(Transfer);
@ -595,6 +608,32 @@ describe('Transfer', () => {
);
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', () => {

View File

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

View File

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

View File

@ -1,9 +1,9 @@
/* 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 type { TreeProps } from 'antd';
import { Switch, Tree } from 'antd';
import type { DataNode } from 'rc-tree/lib/interface';
import React from 'react';
const x = 3;
const y = 2;
@ -40,7 +40,7 @@ const App: React.FC = () => {
const onDragEnter: TreeProps['onDragEnter'] = (info) => {
console.log(info);
// expandedKeys 需要受控时设置
// expandedKeys, set it when controlled is needed
setExpandedKeys(info.expandedKeys);
};
@ -79,7 +79,7 @@ const App: React.FC = () => {
// Drop on the content
loop(data, dropKey, (item) => {
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);
});
} else if (
@ -89,7 +89,7 @@ const App: React.FC = () => {
) {
loop(data, dropKey, (item) => {
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);
});
} else {

View File

@ -1,6 +1,6 @@
import React, { useState } from 'react';
import { Tree } from 'antd';
import type { DataNode, TreeProps } from 'antd/es/tree';
import React, { useState } from 'react';
const x = 3;
const y = 2;
@ -36,7 +36,7 @@ const App: React.FC = () => {
const onDragEnter: TreeProps['onDragEnter'] = (info) => {
console.log(info);
// expandedKeys 需要受控时设置
// expandedKeys, set it when controlled is needed
// setExpandedKeys(info.expandedKeys)
};
@ -74,7 +74,7 @@ const App: React.FC = () => {
// Drop on the content
loop(data, dropKey, (item) => {
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);
});
} else if (
@ -84,7 +84,7 @@ const App: React.FC = () => {
) {
loop(data, dropKey, (item) => {
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);
// in previous version, we use item.children.push(dragObj) to insert the
// item to the tail of the children

View File

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

View File

@ -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?
[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

View File

@ -256,3 +256,34 @@ import { ConfigProvider } from 'antd';
## 你们有接受捐助的渠道吗,比如支付宝或者微信支付?
[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` 以避免出现这种情况。

View File

@ -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">
</ImagePreview>
**大额计量:** 如果一个金额很大,那么数值中的“万”“亿”单位可采用汉字。如果一个数值很大,那么数值中的“万”“亿”单位可采用汉字。
**大额计量:** 如果一个数值很大,那么数值中的“万”“亿”单位可采用汉字。
### 日期时间

View File

@ -16,19 +16,19 @@ Our Gray Matter are wired to react to dynamic things like movement,shape change
## Maintain Context While Changing Views
<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.
<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.
<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.
@ -38,25 +38,25 @@ Accordion: Accordion helps maintain context while switching views.
## 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.
<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.
<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.
<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.

View File

@ -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>
<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>
<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>
<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>
<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>
<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>
对象呼出:点击页面中元素,呼出一个新对象。

View File

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