chore: merge feature

This commit is contained in:
zombiej 2022-05-16 17:16:52 +08:00
commit 8613d2ff2e
21 changed files with 320 additions and 370 deletions

View File

@ -28,6 +28,7 @@ jobs:
branch: 'master'
dingding-token: ${{ secrets.DINGDING_BOT_TOKEN }} ${{ secrets.DINGDING_BOT_BIGFISH_TOKEN }}
dingding-msg: 'CHANGELOG.zh-CN.md'
msg-title: '# Ant Design {{v}} 发布日志'
msg-poster: 'https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*zx7LTI_ECSAAAAAAAAAAAABkARQnAQ'
msg-footer: '💬 前往 [**Ant Design Releases**]({{url}}) 查看更新日志'
prettier: true

View File

@ -15,6 +15,13 @@ timeline: true
---
## 4.20.5
`2022-05-15`
- 🤖 Deprecated Table `rowSelection.onSelectNone` and `rowSelection.onSelectMultiple` in TS type. [#35545](https://github.com/ant-design/ant-design/pull/35545)
- 🐞 Ignore the decimal part in InputNumber when `precision` is negative. [#35520](https://github.com/ant-design/ant-design/pull/35520) [@ty888](https://github.com/ty888)
## 4.20.4
`2022-05-11`

View File

@ -15,6 +15,13 @@ timeline: true
---
## 4.20.5
`2022-05-15
- 🤖 在 TypeScript 定义中废弃 Table `rowSelection.onSelectNone``rowSelection.onSelectMultiple`。[#35545](https://github.com/ant-design/ant-design/pull/35545)
- 🐞 InputNumber 当精度为负数时忽略小数部分。[#35520](https://github.com/ant-design/ant-design/pull/35520) [@ty888](https://github.com/ty888)`
## 4.20.4
`2022-05-11`

View File

@ -16,6 +16,7 @@ import LeftOutlined from '@ant-design/icons/LeftOutlined';
import { useContext } from 'react';
import warning from '../_util/warning';
import { ConfigContext } from '../config-provider';
import defaultRenderEmpty from '../config-provider/defaultRenderEmpty';
import type { SizeType } from '../config-provider/SizeContext';
import SizeContext from '../config-provider/SizeContext';
import DisabledContext from '../config-provider/DisabledContext';
@ -177,7 +178,7 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
);
// =================== No Found ====================
const mergedNotFoundContent = notFoundContent || renderEmpty('Cascader');
const mergedNotFoundContent = notFoundContent || (renderEmpty || defaultRenderEmpty)('Cascader');
// ==================== Prefix =====================
const rootPrefixCls = getPrefixCls();

View File

@ -1,76 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ConfigProvider render empty 1`] = `
<div
class="ant-empty"
>
<div
class="ant-empty-image"
>
<svg
class="ant-empty-img-default"
height="152"
viewBox="0 0 184 152"
width="184"
xmlns="http://www.w3.org/2000/svg"
>
<g
fill="none"
fill-rule="evenodd"
>
<g
transform="translate(24 31.67)"
>
<ellipse
class="ant-empty-img-default-ellipse"
cx="67.797"
cy="106.89"
rx="67.797"
ry="12.668"
/>
<path
class="ant-empty-img-default-path-1"
d="M122.034 69.674L98.109 40.229c-1.148-1.386-2.826-2.225-4.593-2.225h-51.44c-1.766 0-3.444.839-4.592 2.225L13.56 69.674v15.383h108.475V69.674z"
/>
<path
class="ant-empty-img-default-path-2"
d="M101.537 86.214L80.63 61.102c-1.001-1.207-2.507-1.867-4.048-1.867H31.724c-1.54 0-3.047.66-4.048 1.867L6.769 86.214v13.792h94.768V86.214z"
transform="translate(13.56)"
/>
<path
class="ant-empty-img-default-path-3"
d="M33.83 0h67.933a4 4 0 0 1 4 4v93.344a4 4 0 0 1-4 4H33.83a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"
/>
<path
class="ant-empty-img-default-path-4"
d="M42.678 9.953h50.237a2 2 0 0 1 2 2V36.91a2 2 0 0 1-2 2H42.678a2 2 0 0 1-2-2V11.953a2 2 0 0 1 2-2zM42.94 49.767h49.713a2.262 2.262 0 1 1 0 4.524H42.94a2.262 2.262 0 0 1 0-4.524zM42.94 61.53h49.713a2.262 2.262 0 1 1 0 4.525H42.94a2.262 2.262 0 0 1 0-4.525zM121.813 105.032c-.775 3.071-3.497 5.36-6.735 5.36H20.515c-3.238 0-5.96-2.29-6.734-5.36a7.309 7.309 0 0 1-.222-1.79V69.675h26.318c2.907 0 5.25 2.448 5.25 5.42v.04c0 2.971 2.37 5.37 5.277 5.37h34.785c2.907 0 5.277-2.421 5.277-5.393V75.1c0-2.972 2.343-5.426 5.25-5.426h26.318v33.569c0 .617-.077 1.216-.221 1.789z"
/>
</g>
<path
class="ant-empty-img-default-path-5"
d="M149.121 33.292l-6.83 2.65a1 1 0 0 1-1.317-1.23l1.937-6.207c-2.589-2.944-4.109-6.534-4.109-10.408C138.802 8.102 148.92 0 161.402 0 173.881 0 184 8.102 184 18.097c0 9.995-10.118 18.097-22.599 18.097-4.528 0-8.744-1.066-12.28-2.902z"
/>
<g
class="ant-empty-img-default-g"
transform="translate(149.65 15.383)"
>
<ellipse
cx="20.654"
cy="3.167"
rx="2.849"
ry="2.815"
/>
<path
d="M5.698 5.63H0L2.898.704zM9.259.704h4.985V5.63H9.259z"
/>
</g>
</g>
</svg>
</div>
<div
class="ant-empty-description"
>
No Data
</div>
</div>
`;

View File

@ -1,12 +1,12 @@
import React, { useState } from 'react';
import { mount } from 'enzyme';
import { SmileOutlined } from '@ant-design/icons';
import { fireEvent, render } from '@testing-library/react';
import ConfigProvider, { ConfigContext } from '..';
import Button from '../../button';
import Table from '../../table';
import Input from '../../input';
import mountTest from '../../../tests/shared/mountTest';
import { render, fireEvent } from '../../../tests/utils';
describe('ConfigProvider', () => {
mountTest(() => (
@ -104,16 +104,23 @@ describe('ConfigProvider', () => {
});
it('render empty', () => {
let rendered = false;
let cacheRenderEmpty;
const App = () => {
const { renderEmpty } = React.useContext(ConfigContext);
return renderEmpty();
rendered = true;
cacheRenderEmpty = renderEmpty;
return null;
};
const wrapper = mount(
render(
<ConfigProvider>
<App />
</ConfigProvider>,
);
expect(wrapper.render()).toMatchSnapshot();
expect(rendered).toBeTruthy();
expect(cacheRenderEmpty).toBeFalsy();
});
});

View File

@ -1,8 +1,7 @@
import * as React from 'react';
import type { SeedToken } from '../_util/theme';
import type { OverrideToken } from '../_util/theme/interface';
import type { RenderEmptyHandler } from './renderEmpty';
import defaultRenderEmpty from './renderEmpty';
import type { RenderEmptyHandler } from './defaultRenderEmpty';
import type { Locale } from '../locale-provider';
import type { SizeType } from './SizeContext';
import type { RequiredMark } from '../form/Form';
@ -36,7 +35,7 @@ export interface ConfigConsumerProps {
rootPrefixCls?: string;
iconPrefixCls: string;
getPrefixCls: (suffixCls?: string, customizePrefixCls?: string) => string;
renderEmpty: RenderEmptyHandler;
renderEmpty?: RenderEmptyHandler;
csp?: CSPConfig;
autoInsertSpaceInButton?: boolean;
input?: {
@ -65,12 +64,10 @@ const defaultGetPrefixCls = (suffixCls?: string, customizePrefixCls?: string) =>
return suffixCls ? `ant-${suffixCls}` : 'ant';
};
// zombieJ: 🚨 Do not pass `defaultRenderEmpty` here since it will case circular dependency.
export const ConfigContext = React.createContext<ConfigConsumerProps>({
// We provide a default function for Context without provider
getPrefixCls: defaultGetPrefixCls,
renderEmpty: defaultRenderEmpty,
iconPrefixCls: defaultIconPrefixCls,
});

View File

@ -3,7 +3,7 @@ import Empty from '../empty';
import type { ConfigConsumerProps } from '.';
import { ConfigConsumer } from '.';
const renderEmpty = (componentName?: string): React.ReactNode => (
const defaultRenderEmpty = (componentName?: string): React.ReactNode => (
<ConfigConsumer>
{({ getPrefixCls }: ConfigConsumerProps) => {
const prefix = getPrefixCls('empty');
@ -19,13 +19,16 @@ const renderEmpty = (componentName?: string): React.ReactNode => (
case 'Transfer':
case 'Mentions':
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} className={`${prefix}-small`} />;
/* istanbul ignore next */
default:
// Should never hit if we take all the component into consider.
return <Empty />;
}
}}
</ConfigConsumer>
);
export type RenderEmptyHandler = typeof renderEmpty;
export type RenderEmptyHandler = typeof defaultRenderEmpty;
export default renderEmpty;
export default defaultRenderEmpty;

View File

@ -3,7 +3,7 @@ import IconContext from '@ant-design/icons/lib/components/Context';
import { FormProvider as RcFormProvider } from 'rc-field-form';
import type { ValidateMessages } from 'rc-field-form/lib/interface';
import useMemo from 'rc-util/lib/hooks/useMemo';
import { RenderEmptyHandler } from './renderEmpty';
import { RenderEmptyHandler } from './defaultRenderEmpty';
import type { Locale } from '../locale-provider';
import LocaleProvider, { ANT_MARK } from '../locale-provider';
import LocaleReceiver from '../locale-provider/LocaleReceiver';

View File

@ -1,5 +1,5 @@
import * as React from 'react';
import { forwardRef, useContext } from 'react';
import { forwardRef, useContext, useImperativeHandle } from 'react';
import classNames from 'classnames';
import CalendarOutlined from '@ant-design/icons/CalendarOutlined';
import ClockCircleOutlined from '@ant-design/icons/ClockCircleOutlined';
@ -18,131 +18,122 @@ import { Components, getTimeProps } from '.';
import { FormItemInputContext } from '../../form/context';
import { getMergedStatus, getStatusClassNames } from '../../_util/statusUtils';
import useStyle from '../style';
import type { PickerComponentClass } from './interface';
import type { PickerComponentClass, CommonPickerMethods } from './interface';
export default function generateRangePicker<DateType>(
generateConfig: GenerateConfig<DateType>,
): PickerComponentClass<RangePickerProps<DateType>> {
type InternalRangePickerProps = RangePickerProps<DateType> & {};
const RangePicker = forwardRef<InternalRangePickerProps, RangePickerProps<DateType>>(
(props, ref) => {
const {
prefixCls: customizePrefixCls,
getPopupContainer: customGetPopupContainer,
className,
placement,
size: customizeSize,
disabled: customDisabled,
bordered = true,
placeholder,
status: customStatus,
onBlur,
onFocus,
dropdownClassName,
...restProps
} = props;
const RangePicker = forwardRef<
InternalRangePickerProps | CommonPickerMethods,
RangePickerProps<DateType>
>((props, ref) => {
const {
prefixCls: customizePrefixCls,
getPopupContainer: customGetPopupContainer,
className,
placement,
size: customizeSize,
disabled: customDisabled,
bordered = true,
placeholder,
status: customStatus,
dropdownClassName,
...restProps
} = props;
const pickerRef = (ref as any) || React.createRef<RCRangePicker<DateType>>();
const { getPrefixCls, direction, getPopupContainer } = useContext(ConfigContext);
const prefixCls = getPrefixCls('picker', customizePrefixCls);
const { format, showTime, picker } = props as any;
const rootPrefixCls = getPrefixCls();
const innerRef = React.useRef<RCRangePicker<DateType>>(null);
const { getPrefixCls, direction, getPopupContainer } = useContext(ConfigContext);
const prefixCls = getPrefixCls('picker', customizePrefixCls);
const { format, showTime, picker } = props as any;
const rootPrefixCls = getPrefixCls();
const [wrapSSR, hashId] = useStyle(prefixCls);
const [wrapSSR, hashId] = useStyle(prefixCls);
let additionalOverrideProps: any = {};
additionalOverrideProps = {
...additionalOverrideProps,
...(showTime ? getTimeProps({ format, picker, ...showTime }) : {}),
...(picker === 'time' ? getTimeProps({ format, ...props, picker }) : {}),
};
let additionalOverrideProps: any = {};
additionalOverrideProps = {
...additionalOverrideProps,
...(showTime ? getTimeProps({ format, picker, ...showTime }) : {}),
...(picker === 'time' ? getTimeProps({ format, ...props, picker }) : {}),
};
// ===================== Size =====================
const size = React.useContext(SizeContext);
const mergedSize = customizeSize || size;
// ===================== Size =====================
const size = React.useContext(SizeContext);
const mergedSize = customizeSize || size;
// ===================== Disabled =====================
const disabled = React.useContext(DisabledContext);
const mergedDisabled = customDisabled || disabled;
// ===================== FormItemInput =====================
const formItemContext = useContext(FormItemInputContext);
const { hasFeedback, status: contextStatus, feedbackIcon } = formItemContext;
// ===================== Disabled =====================
const disabled = React.useContext(DisabledContext);
const mergedDisabled = customDisabled || disabled;
const suffixNode = (
<>
{picker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />}
{hasFeedback && feedbackIcon}
</>
);
// ===================== FormItemInput =====================
const formItemContext = useContext(FormItemInputContext);
const { hasFeedback, status: contextStatus, feedbackIcon } = formItemContext;
const focus = () => {
if (pickerRef.current) {
pickerRef.current.focus();
}
};
const suffixNode = (
<>
{picker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />}
{hasFeedback && feedbackIcon}
</>
);
const blur = () => {
if (pickerRef.current) {
pickerRef.current.blur();
}
};
useImperativeHandle(ref, () => ({
focus: () => innerRef.current?.focus(),
blur: () => innerRef.current?.blur(),
}));
return wrapSSR(
<LocaleReceiver componentName="DatePicker" defaultLocale={enUS}>
{(contextLocale: PickerLocale) => {
const locale = { ...contextLocale, ...props.locale };
return wrapSSR(
<LocaleReceiver componentName="DatePicker" defaultLocale={enUS}>
{(contextLocale: PickerLocale) => {
const locale = { ...contextLocale, ...props.locale };
return (
<RCRangePicker<DateType>
separator={
<span aria-label="to" className={`${prefixCls}-separator`}>
<SwapRightOutlined />
</span>
}
disabled={mergedDisabled}
ref={pickerRef}
onBlur={onBlur || blur}
onFocus={onFocus || focus}
dropdownAlign={transPlacement2DropdownAlign(direction, placement)}
placeholder={getRangePlaceholder(picker, locale, placeholder)}
suffixIcon={suffixNode}
clearIcon={<CloseCircleFilled />}
prevIcon={<span className={`${prefixCls}-prev-icon`} />}
nextIcon={<span className={`${prefixCls}-next-icon`} />}
superPrevIcon={<span className={`${prefixCls}-super-prev-icon`} />}
superNextIcon={<span className={`${prefixCls}-super-next-icon`} />}
allowClear
transitionName={`${rootPrefixCls}-slide-up`}
{...restProps}
{...additionalOverrideProps}
className={classNames(
{
[`${prefixCls}-${mergedSize}`]: mergedSize,
[`${prefixCls}-borderless`]: !bordered,
},
getStatusClassNames(
prefixCls as string,
getMergedStatus(contextStatus, customStatus),
hasFeedback,
),
hashId,
className,
)}
locale={locale!.lang}
prefixCls={prefixCls}
getPopupContainer={customGetPopupContainer || getPopupContainer}
generateConfig={generateConfig}
components={Components}
direction={direction}
dropdownClassName={classNames(hashId, dropdownClassName)}
/>
);
}}
</LocaleReceiver>,
);
},
);
return (
<RCRangePicker<DateType>
separator={
<span aria-label="to" className={`${prefixCls}-separator`}>
<SwapRightOutlined />
</span>
}
disabled={mergedDisabled}
ref={innerRef}
dropdownAlign={transPlacement2DropdownAlign(direction, placement)}
placeholder={getRangePlaceholder(picker, locale, placeholder)}
suffixIcon={suffixNode}
clearIcon={<CloseCircleFilled />}
prevIcon={<span className={`${prefixCls}-prev-icon`} />}
nextIcon={<span className={`${prefixCls}-next-icon`} />}
superPrevIcon={<span className={`${prefixCls}-super-prev-icon`} />}
superNextIcon={<span className={`${prefixCls}-super-next-icon`} />}
allowClear
transitionName={`${rootPrefixCls}-slide-up`}
{...restProps}
{...additionalOverrideProps}
className={classNames(
{
[`${prefixCls}-${mergedSize}`]: mergedSize,
[`${prefixCls}-borderless`]: !bordered,
},
getStatusClassNames(
prefixCls as string,
getMergedStatus(contextStatus, customStatus),
hasFeedback,
),
hashId,
className,
)}
locale={locale!.lang}
prefixCls={prefixCls}
getPopupContainer={customGetPopupContainer || getPopupContainer}
generateConfig={generateConfig}
components={Components}
direction={direction}
dropdownClassName={classNames(hashId, dropdownClassName)}
/>
);
}}
</LocaleReceiver>,
);
});
return RangePicker as unknown as PickerComponentClass<RangePickerProps<DateType>>;
}

View File

@ -6,7 +6,7 @@ import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import RCPicker from 'rc-picker';
import type { PickerMode } from 'rc-picker/lib/interface';
import type { GenerateConfig } from 'rc-picker/lib/generate/index';
import { forwardRef, useContext } from 'react';
import { forwardRef, useContext, useImperativeHandle } from 'react';
import enUS from '../locale/en_US';
import { getPlaceholder, transPlacement2DropdownAlign } from '../util';
import warning from '../../_util/warning';
@ -20,7 +20,7 @@ import { getTimeProps, Components } from '.';
import { FormItemInputContext } from '../../form/context';
import type { InputStatus } from '../../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../../_util/statusUtils';
import type { DatePickRef, PickerComponentClass } from './interface';
import type { DatePickRef, PickerComponentClass, CommonPickerMethods } from './interface';
export default function generatePicker<DateType>(generateConfig: GenerateConfig<DateType>) {
type DatePickerProps = PickerProps<DateType> & {
@ -32,136 +32,127 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
picker?: PickerMode,
displayName?: string,
) {
const Picker = forwardRef<DatePickRef<DateType>, InnerPickerProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
getPopupContainer: customizeGetPopupContainer,
className,
size: customizeSize,
bordered = true,
placement,
placeholder,
disabled: customDisabled,
status: customStatus,
onBlur,
onFocus,
dropdownClassName,
...restProps
} = props;
const Picker = forwardRef<DatePickRef<DateType> | CommonPickerMethods, InnerPickerProps>(
(props, ref) => {
const {
prefixCls: customizePrefixCls,
getPopupContainer: customizeGetPopupContainer,
className,
size: customizeSize,
bordered = true,
placement,
placeholder,
disabled: customDisabled,
status: customStatus,
dropdownClassName,
...restProps
} = props;
warning(
picker !== 'quarter',
displayName!,
`DatePicker.${displayName} is legacy usage. Please use DatePicker[picker='${picker}'] directly.`,
);
warning(
picker !== 'quarter',
displayName!,
`DatePicker.${displayName} is legacy usage. Please use DatePicker[picker='${picker}'] directly.`,
);
const { getPrefixCls, direction, getPopupContainer } = useContext(ConfigContext);
const prefixCls = getPrefixCls('picker', customizePrefixCls);
const pickerRef = (ref as any) || React.createRef<RCPicker<DateType>>();
const { format, showTime } = props as any;
const { getPrefixCls, direction, getPopupContainer } = useContext(ConfigContext);
const prefixCls = getPrefixCls('picker', customizePrefixCls);
const innerRef = React.useRef<RCPicker<DateType>>(null);
const { format, showTime } = props as any;
const [wrapSSR, hashId] = useStyle(prefixCls);
const [wrapSSR, hashId] = useStyle(prefixCls);
const additionalProps = {
showToday: true,
};
useImperativeHandle(ref, () => ({
focus: () => innerRef.current?.focus(),
blur: () => innerRef.current?.blur(),
}));
let additionalOverrideProps: any = {};
if (picker) {
additionalOverrideProps.picker = picker;
}
const mergedPicker = picker || props.picker;
const additionalProps = {
showToday: true,
};
additionalOverrideProps = {
...additionalOverrideProps,
...(showTime ? getTimeProps({ format, picker: mergedPicker, ...showTime }) : {}),
...(mergedPicker === 'time'
? getTimeProps({ format, ...props, picker: mergedPicker })
: {}),
};
const rootPrefixCls = getPrefixCls();
// ===================== Size =====================
const size = React.useContext(SizeContext);
const mergedSize = customizeSize || size;
// ===================== Disabled =====================
const disabled = React.useContext(DisabledContext);
const mergedDisabled = customDisabled || disabled;
// ===================== FormItemInput =====================
const formItemContext = useContext(FormItemInputContext);
const { hasFeedback, status: contextStatus, feedbackIcon } = formItemContext;
const suffixNode = (
<>
{mergedPicker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />}
{hasFeedback && feedbackIcon}
</>
);
const focus = () => {
if (pickerRef.current) {
pickerRef.current.focus();
let additionalOverrideProps: any = {};
if (picker) {
additionalOverrideProps.picker = picker;
}
};
const mergedPicker = picker || props.picker;
const blur = () => {
if (pickerRef.current) {
pickerRef.current.blur();
}
};
additionalOverrideProps = {
...additionalOverrideProps,
...(showTime ? getTimeProps({ format, picker: mergedPicker, ...showTime }) : {}),
...(mergedPicker === 'time'
? getTimeProps({ format, ...props, picker: mergedPicker })
: {}),
};
const rootPrefixCls = getPrefixCls();
return wrapSSR(
<LocaleReceiver componentName="DatePicker" defaultLocale={enUS}>
{(contextLocale: PickerLocale) => {
const locale = { ...contextLocale, ...props.locale };
// ===================== Size =====================
const size = React.useContext(SizeContext);
const mergedSize = customizeSize || size;
return (
<RCPicker<DateType>
ref={pickerRef}
placeholder={getPlaceholder(mergedPicker, locale, placeholder)}
suffixIcon={suffixNode}
dropdownAlign={transPlacement2DropdownAlign(direction, placement)}
clearIcon={<CloseCircleFilled />}
prevIcon={<span className={`${prefixCls}-prev-icon`} />}
nextIcon={<span className={`${prefixCls}-next-icon`} />}
superPrevIcon={<span className={`${prefixCls}-super-prev-icon`} />}
superNextIcon={<span className={`${prefixCls}-super-next-icon`} />}
allowClear
transitionName={`${rootPrefixCls}-slide-up`}
onBlur={onBlur || blur}
onFocus={onFocus || focus}
{...additionalProps}
{...restProps}
{...additionalOverrideProps}
locale={locale!.lang}
className={classNames(
{
[`${prefixCls}-${mergedSize}`]: mergedSize,
[`${prefixCls}-borderless`]: !bordered,
},
getStatusClassNames(
prefixCls as string,
getMergedStatus(contextStatus, customStatus),
hasFeedback,
),
hashId,
className,
)}
prefixCls={prefixCls}
getPopupContainer={customizeGetPopupContainer || getPopupContainer}
generateConfig={generateConfig}
components={Components}
direction={direction}
disabled={mergedDisabled}
dropdownClassName={classNames(hashId, dropdownClassName)}
/>
);
}}
</LocaleReceiver>,
);
});
// ===================== Disabled =====================
const disabled = React.useContext(DisabledContext);
const mergedDisabled = customDisabled || disabled;
// ===================== FormItemInput =====================
const formItemContext = useContext(FormItemInputContext);
const { hasFeedback, status: contextStatus, feedbackIcon } = formItemContext;
const suffixNode = (
<>
{mergedPicker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />}
{hasFeedback && feedbackIcon}
</>
);
return wrapSSR(
<LocaleReceiver componentName="DatePicker" defaultLocale={enUS}>
{(contextLocale: PickerLocale) => {
const locale = { ...contextLocale, ...props.locale };
return (
<RCPicker<DateType>
ref={innerRef}
placeholder={getPlaceholder(mergedPicker, locale, placeholder)}
suffixIcon={suffixNode}
dropdownAlign={transPlacement2DropdownAlign(direction, placement)}
clearIcon={<CloseCircleFilled />}
prevIcon={<span className={`${prefixCls}-prev-icon`} />}
nextIcon={<span className={`${prefixCls}-next-icon`} />}
superPrevIcon={<span className={`${prefixCls}-super-prev-icon`} />}
superNextIcon={<span className={`${prefixCls}-super-next-icon`} />}
allowClear
transitionName={`${rootPrefixCls}-slide-up`}
{...additionalProps}
{...restProps}
{...additionalOverrideProps}
locale={locale!.lang}
className={classNames(
{
[`${prefixCls}-${mergedSize}`]: mergedSize,
[`${prefixCls}-borderless`]: !bordered,
},
getStatusClassNames(
prefixCls as string,
getMergedStatus(contextStatus, customStatus),
hasFeedback,
),
hashId,
className,
)}
prefixCls={prefixCls}
getPopupContainer={customizeGetPopupContainer || getPopupContainer}
generateConfig={generateConfig}
components={Components}
direction={direction}
disabled={mergedDisabled}
dropdownClassName={classNames(hashId, dropdownClassName)}
/>
);
}}
</LocaleReceiver>,
);
},
);
if (displayName) {
Picker.displayName = displayName;

View File

@ -50,7 +50,9 @@ describe('Drawer', () => {
const { container, rerender } = render(getDrawer({ destroyOnClose: true }));
rerender(getDrawer({ destroyOnClose: true, visible: false }));
fireEvent.transitionEnd(container.querySelector('.ant-drawer-wrapper-body'));
const ev = new TransitionEvent('transitionend', { bubbles: true });
ev.propertyName = 'transform';
fireEvent(document.querySelector('.ant-drawer-content-wrapper'), ev);
expect(container.querySelector('.ant-drawer-wrapper-body')).toBeFalsy();
});
@ -60,8 +62,19 @@ describe('Drawer', () => {
expect(container.querySelector('.ant-drawer-wrapper-body')).toBeTruthy();
rerender(getDrawer({ visible: false }));
fireEvent.transitionEnd(container.querySelector('.ant-drawer-wrapper-body'));
const ev = new TransitionEvent('transitionend', { bubbles: true });
ev.propertyName = 'transform';
fireEvent(document.querySelector('.ant-drawer-content-wrapper'), ev);
expect(container.querySelector('.ant-drawer-wrapper-body')).toBeTruthy();
});
it('test afterVisibleChange', async () => {
const afterVisibleChange = jest.fn();
const { rerender } = render(getDrawer({ afterVisibleChange, visible: true }));
rerender(getDrawer({ afterVisibleChange, visible: false }));
const ev = new TransitionEvent('transitionend', { bubbles: true });
ev.propertyName = 'transform';
fireEvent(document.querySelector('.ant-drawer-content-wrapper'), ev);
expect(afterVisibleChange).toBeCalledTimes(1);
});
});

View File

@ -20,7 +20,6 @@ exports[`Drawer className is test_drawer 1`] = `
>
<div
class="ant-drawer-wrapper-body"
style="opacity: 0; transition: opacity .3s;"
>
<div
class="ant-drawer-header ant-drawer-header-close-only"
@ -120,7 +119,6 @@ exports[`Drawer destroyOnClose is true 1`] = `
>
<div
class="ant-drawer-wrapper-body"
style="opacity: 0; transition: opacity .3s;"
>
<div
class="ant-drawer-header ant-drawer-header-close-only"

View File

@ -7,8 +7,6 @@ import { tuple } from '../_util/type';
// CSSINJS
import useStyle from './style';
import useForceUpdate from '../_util/hooks/useForceUpdate';
type DrawerRef = {
push(): void;
pull(): void;
@ -90,7 +88,8 @@ const Drawer = React.forwardRef<DrawerRef, DrawerProps>(
bodyStyle,
drawerStyle,
className,
visible,
visible: propsVisible,
forceRender,
children,
zIndex,
destroyOnClose,
@ -103,14 +102,31 @@ const Drawer = React.forwardRef<DrawerRef, DrawerProps>(
prefixCls: customizePrefixCls,
getContainer: customizeGetContainer,
extra,
afterVisibleChange,
...rest
},
ref,
) => {
const forceUpdate = useForceUpdate();
const [internalPush, setPush] = React.useState(false);
const parentDrawer = React.useContext(DrawerContext);
const destroyClose = React.useRef<boolean>(false);
const destroyCloseRef = React.useRef<boolean>(false);
const [load, setLoad] = React.useState(false);
const [visible, setVisible] = React.useState(false);
React.useEffect(() => {
if (propsVisible) {
setLoad(true);
} else {
setVisible(false);
}
}, [propsVisible]);
React.useEffect(() => {
if (load && propsVisible) {
setVisible(true);
}
}, [load, propsVisible]);
const { getPopupContainer, getPrefixCls, direction } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('drawer', customizePrefixCls);
@ -127,7 +143,7 @@ const Drawer = React.forwardRef<DrawerRef, DrawerProps>(
React.useEffect(() => {
// fix: delete drawer in child and re-render, no push started.
// <Drawer>{show && <Drawer />}</Drawer>
if (visible && parentDrawer) {
if (propsVisible && parentDrawer) {
parentDrawer.push();
}
@ -167,18 +183,6 @@ const Drawer = React.forwardRef<DrawerRef, DrawerProps>(
React.useImperativeHandle(ref, () => operations, [operations]);
const isDestroyOnClose = destroyOnClose && !visible;
const onDestroyTransitionEnd = () => {
if (!isDestroyOnClose) {
return;
}
if (!visible) {
destroyClose.current = true;
forceUpdate();
}
};
const getOffsetStyle = () => {
// https://github.com/ant-design/ant-design/issues/24287
if (!visible && !mask) {
@ -267,28 +271,13 @@ const Drawer = React.forwardRef<DrawerRef, DrawerProps>(
// render drawer body dom
const renderBody = () => {
if (destroyClose.current && !visible) {
// destroyCloseRef.current =false Load the body only once by default
if (destroyCloseRef.current && !forceRender && !load) {
return null;
}
destroyClose.current = false;
const containerStyle: React.CSSProperties = {};
if (isDestroyOnClose) {
// Increase the opacity transition, delete children after closing.
containerStyle.opacity = 0;
containerStyle.transition = 'opacity .3s';
}
return (
<div
className={`${prefixCls}-wrapper-body`}
style={{
...containerStyle,
...drawerStyle,
}}
onTransitionEnd={onDestroyTransitionEnd}
>
<div className={`${prefixCls}-wrapper-body`} style={{ ...drawerStyle }}>
{renderHeader()}
<div className={`${prefixCls}-body`} style={bodyStyle}>
{children}
@ -320,6 +309,7 @@ const Drawer = React.forwardRef<DrawerRef, DrawerProps>(
keyboard,
children,
onClose,
forceRender,
...rest,
}}
{...offsetStyle}
@ -328,6 +318,18 @@ const Drawer = React.forwardRef<DrawerRef, DrawerProps>(
style={getRcDrawerStyle()}
className={drawerClassName}
getContainer={getContainer}
afterVisibleChange={open => {
if (!open) {
if (destroyCloseRef.current === false) {
// set true only once
destroyCloseRef.current = true;
}
if (destroyOnClose) {
setLoad(false);
}
}
afterVisibleChange?.(open);
}}
>
{renderBody()}
</RcDrawer>

View File

@ -12,6 +12,8 @@ import type { PaginationConfig } from '../pagination';
import Pagination from '../pagination';
import { Row } from '../grid';
import Item from './Item';
import defaultRenderEmpty from '../config-provider/defaultRenderEmpty';
// CSSINJS
import useStyle from './style';
@ -266,7 +268,7 @@ function List<T>({
<ul className={`${prefixCls}-items`}>{items}</ul>
);
} else if (!children && !isLoading) {
childrenContent = renderEmptyFunc(prefixCls, renderEmpty);
childrenContent = renderEmptyFunc(prefixCls, renderEmpty || defaultRenderEmpty);
}
const paginationPosition = paginationProps.position || 'bottom';

View File

@ -10,6 +10,7 @@ import { FormItemInputContext } from '../form/context';
import useStyle from './style';
import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import defaultRenderEmpty from '../config-provider/defaultRenderEmpty';
export const { Option } = RcMentions;
@ -96,7 +97,7 @@ const InternalMentions: React.ForwardRefRenderFunction<unknown, MentionProps> =
return notFoundContent;
}
return renderEmpty('Select');
return (renderEmpty || defaultRenderEmpty)('Select');
};
const getOptions = () => {

View File

@ -18,6 +18,7 @@ import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import type { SelectCommonPlacement } from '../_util/motion';
import { getTransitionName, getTransitionDirection } from '../_util/motion';
import defaultRenderEmpty from '../config-provider/defaultRenderEmpty';
import useStyle from './style';
@ -126,7 +127,7 @@ const InternalSelect = <OptionType extends BaseOptionType | DefaultOptionType =
} else if (mode === 'combobox') {
mergedNotFound = null;
} else {
mergedNotFound = renderEmpty('Select');
mergedNotFound = (renderEmpty || defaultRenderEmpty)('Select');
}
// ===================== Icons =====================

View File

@ -49,6 +49,7 @@ import Column from './Column';
import ColumnGroup from './ColumnGroup';
import warning from '../_util/warning';
import useBreakpoint from '../grid/hooks/useBreakpoint';
import defaultRenderEmpty from '../config-provider/defaultRenderEmpty';
export { ColumnsType, TablePaginationConfig };
@ -518,7 +519,7 @@ function InternalTable<RecordType extends object = any>(
data={pageData}
rowKey={getRowKey}
rowClassName={internalRowClassName}
emptyText={(locale && locale.emptyText) || renderEmpty('Table')}
emptyText={(locale && locale.emptyText) || (renderEmpty || defaultRenderEmpty)('Table')}
// Internal
internalHooks={INTERNAL_HOOKS}
internalRefs={internalRefs as any}

View File

@ -14,6 +14,8 @@ import warning from '../_util/warning';
import { FormItemInputContext } from '../form/context';
import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import defaultRenderEmpty from '../config-provider/defaultRenderEmpty';
import useStyle from './style';
export { TransferListProps } from './list';
@ -395,7 +397,7 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com
status: customStatus,
} = this.props;
const prefixCls = getPrefixCls('transfer', customizePrefixCls);
const locale = this.getLocale(transferLocale, renderEmpty);
const locale = this.getLocale(transferLocale, renderEmpty || defaultRenderEmpty);
const { sourceSelectedKeys, targetSelectedKeys } = this.state;
const mergedStatus = getMergedStatus(contextStatus, customStatus);

View File

@ -22,6 +22,7 @@ import { getTransitionName, getTransitionDirection } from '../_util/motion';
import { FormItemInputContext } from '../form/context';
import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import defaultRenderEmpty from '../config-provider/defaultRenderEmpty';
type RawValue = string | number;
@ -142,7 +143,7 @@ const InternalTreeSelect = <OptionType extends BaseOptionType | DefaultOptionTyp
if (notFoundContent !== undefined) {
mergedNotFound = notFoundContent;
} else {
mergedNotFound = renderEmpty('Select');
mergedNotFound = (renderEmpty || defaultRenderEmpty)('Select');
}
// ==================== Render =====================

View File

@ -1,6 +1,6 @@
{
"name": "antd",
"version": "4.20.4",
"version": "4.20.5",
"description": "An enterprise-class UI design language and React components implementation",
"title": "Ant Design",
"keywords": [