merge master

This commit is contained in:
zombiej 2020-05-14 21:03:29 +08:00
commit 9f38c46039
27 changed files with 108 additions and 75 deletions

View File

@ -1,8 +1,17 @@
import * as React from 'react'; import * as React from 'react';
// eslint-disable-next-line import/prefer-default-export export const isValidElement = React.isValidElement;
export function cloneElement(element: React.ReactNode, ...restArgs: any[]) {
if (!React.isValidElement(element)) return element;
return React.cloneElement(element, ...restArgs); export function replaceElement(
element: React.ReactNode,
replacement: React.ReactNode,
props: any,
): React.ReactNode {
if (!isValidElement(element)) return replacement;
return React.cloneElement(element, typeof props === 'function' ? props() : props);
}
export function cloneElement(element: React.ReactNode, props?: any): React.ReactElement {
return replaceElement(element, element, props) as React.ReactElement;
} }

View File

@ -15,6 +15,7 @@ import classNames from 'classnames';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import getDataOrAriaProps from '../_util/getDataOrAriaProps'; import getDataOrAriaProps from '../_util/getDataOrAriaProps';
import ErrorBoundary from './ErrorBoundary'; import ErrorBoundary from './ErrorBoundary';
import { replaceElement } from '../_util/reactNode';
export interface AlertProps { export interface AlertProps {
/** /**
@ -127,15 +128,11 @@ export default class Alert extends React.Component<AlertProps, AlertState> {
const { icon } = this.props; const { icon } = this.props;
const iconType = this.getIconType(); const iconType = this.getIconType();
if (icon) { if (icon) {
return React.isValidElement<{ className?: string }>(icon) ? ( return replaceElement(icon, <span className={`${prefixCls}-icon`}>{icon}</span>, () => ({
React.cloneElement(icon, {
className: classNames(`${prefixCls}-icon`, { className: classNames(`${prefixCls}-icon`, {
[icon.props.className as string]: icon.props.className, [(icon as any).props.className]: (icon as any).props.className,
}), }),
}) }));
) : (
<span className={`${prefixCls}-icon`}>{icon}</span>
);
} }
return React.createElement(iconType, { className: `${prefixCls}-icon` }); return React.createElement(iconType, { className: `${prefixCls}-icon` });
} }

View File

@ -13,6 +13,7 @@ import omit from 'omit.js';
import Select, { InternalSelectProps, OptionType } from '../select'; import Select, { InternalSelectProps, OptionType } from '../select';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import devWarning from '../_util/devWarning'; import devWarning from '../_util/devWarning';
import { isValidElement } from '../_util/reactNode';
const { Option } = Select; const { Option } = Select;
@ -49,7 +50,7 @@ const AutoComplete: React.RefForwardingComponent<Select, AutoCompleteProps> = (p
if ( if (
childNodes.length === 1 && childNodes.length === 1 &&
React.isValidElement(childNodes[0]) && isValidElement(childNodes[0]) &&
!isSelectOptionOrSelectOptGroup(childNodes[0]) !isSelectOptionOrSelectOptGroup(childNodes[0])
) { ) {
customizeInput = childNodes[0]; customizeInput = childNodes[0];
@ -66,7 +67,7 @@ const AutoComplete: React.RefForwardingComponent<Select, AutoCompleteProps> = (p
} else { } else {
optionChildren = dataSource optionChildren = dataSource
? dataSource.map(item => { ? dataSource.map(item => {
if (React.isValidElement(item)) { if (isValidElement(item)) {
return item; return item;
} }
switch (typeof item) { switch (typeof item) {

View File

@ -2,6 +2,7 @@ import * as React from 'react';
import omit from 'omit.js'; import omit from 'omit.js';
import classNames from 'classnames'; import classNames from 'classnames';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import { cloneElement } from '../_util/reactNode';
function getNumberArray(num: string | number | undefined | null) { function getNumberArray(num: string | number | undefined | null) {
return num return num
@ -175,7 +176,7 @@ const ScrollNumber: React.FC<ScrollNumberProps> = props => {
}; };
} }
if (displayComponent) { if (displayComponent) {
return React.cloneElement(displayComponent, { return cloneElement(displayComponent, {
className: classNames( className: classNames(
`${prefixCls}-custom-component`, `${prefixCls}-custom-component`,
displayComponent.props && displayComponent.props.className, displayComponent.props && displayComponent.props.className,

View File

@ -6,6 +6,7 @@ import ScrollNumber from './ScrollNumber';
import { PresetColorTypes, PresetColorType, PresetStatusColorType } from '../_util/colors'; import { PresetColorTypes, PresetColorType, PresetStatusColorType } from '../_util/colors';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import { LiteralUnion } from '../_util/type'; import { LiteralUnion } from '../_util/type';
import { cloneElement } from '../_util/reactNode';
export { ScrollNumberProps } from './ScrollNumber'; export { ScrollNumberProps } from './ScrollNumber';
@ -110,7 +111,7 @@ const Badge: React.FC<BadgeProps> = props => {
if (!customNode || typeof customNode !== 'object') { if (!customNode || typeof customNode !== 'object') {
return undefined; return undefined;
} }
return React.cloneElement(customNode, { return cloneElement(customNode, {
style: { style: {
...getStyleWithOffset(), ...getStyleWithOffset(),
...(customNode.props && customNode.props.style), ...(customNode.props && customNode.props.style),

View File

@ -8,6 +8,7 @@ import Menu from '../menu';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import devWarning from '../_util/devWarning'; import devWarning from '../_util/devWarning';
import { Omit } from '../_util/type'; import { Omit } from '../_util/type';
import { cloneElement } from '../_util/reactNode';
export interface Route { export interface Route {
path: string; path: string;
@ -138,9 +139,9 @@ export default class Breadcrumb extends React.Component<BreadcrumbProps, any> {
"Only accepts Breadcrumb.Item and Breadcrumb.Separator as it's children", "Only accepts Breadcrumb.Item and Breadcrumb.Separator as it's children",
); );
return React.cloneElement(element, { return cloneElement(element, {
separator, separator,
key: index, // eslint-disable-line react/no-array-index-key key: index,
}); });
}); });
} }

View File

@ -10,6 +10,7 @@ import { Omit, tuple } from '../_util/type';
import devWarning from '../_util/devWarning'; import devWarning from '../_util/devWarning';
import SizeContext, { SizeType } from '../config-provider/SizeContext'; import SizeContext, { SizeType } from '../config-provider/SizeContext';
import LoadingIcon from './LoadingIcon'; import LoadingIcon from './LoadingIcon';
import { cloneElement } from '../_util/reactNode';
const rxTwoCNChar = /^[\u4e00-\u9fa5]{2}$/; const rxTwoCNChar = /^[\u4e00-\u9fa5]{2}$/;
const isTwoCNChar = rxTwoCNChar.test.bind(rxTwoCNChar); const isTwoCNChar = rxTwoCNChar.test.bind(rxTwoCNChar);
@ -31,7 +32,9 @@ function insertSpace(child: React.ReactChild, needInserted: boolean) {
isString(child.type) && isString(child.type) &&
isTwoCNChar(child.props.children) isTwoCNChar(child.props.children)
) { ) {
return React.cloneElement(child, {}, child.props.children.split('').join(SPACE)); return cloneElement(child, {
children: child.props.children.split('').join(SPACE),
});
} }
if (typeof child === 'string') { if (typeof child === 'string') {
if (isTwoCNChar(child)) { if (isTwoCNChar(child)) {

View File

@ -16,6 +16,7 @@ import { ConfigConsumer, ConfigConsumerProps, RenderEmptyHandler } from '../conf
import LocaleReceiver from '../locale-provider/LocaleReceiver'; import LocaleReceiver from '../locale-provider/LocaleReceiver';
import devWarning from '../_util/devWarning'; import devWarning from '../_util/devWarning';
import SizeContext, { SizeType } from '../config-provider/SizeContext'; import SizeContext, { SizeType } from '../config-provider/SizeContext';
import { replaceElement } from '../_util/reactNode';
export interface CascaderOptionType { export interface CascaderOptionType {
value?: string; value?: string;
@ -554,17 +555,21 @@ class Cascader extends React.Component<CascaderProps, CascaderState> {
dropdownMenuColumnStyle.width = this.input.input.offsetWidth; dropdownMenuColumnStyle.width = this.input.input.offsetWidth;
} }
const inputIcon = (suffixIcon && let inputIcon: React.ReactNode;
(React.isValidElement<{ className?: string }>(suffixIcon) ? ( if (suffixIcon) {
React.cloneElement(suffixIcon, { inputIcon = replaceElement(
suffixIcon,
<span className={`${prefixCls}-picker-arrow`}>{suffixIcon}</span>,
() => ({
className: classNames({ className: classNames({
[suffixIcon.props.className!]: suffixIcon.props.className, [(suffixIcon as any).props.className!]: (suffixIcon as any).props.className,
[`${prefixCls}-picker-arrow`]: true, [`${prefixCls}-picker-arrow`]: true,
}), }),
}) }),
) : ( );
<span className={`${prefixCls}-picker-arrow`}>{suffixIcon}</span> } else {
))) || <DownOutlined className={arrowCls} />; inputIcon = <DownOutlined className={arrowCls} />;
}
const input = children || ( const input = children || (
<span style={style} className={pickerCls}> <span style={style} className={pickerCls}>

View File

@ -6,6 +6,7 @@ import RightOutlined from '@ant-design/icons/RightOutlined';
import CollapsePanel from './CollapsePanel'; import CollapsePanel from './CollapsePanel';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import animation from '../_util/openAnimation'; import animation from '../_util/openAnimation';
import { cloneElement } from '../_util/reactNode';
export type ExpandIconPosition = 'left' | 'right' | undefined; export type ExpandIconPosition = 'left' | 'right' | undefined;
@ -58,11 +59,9 @@ export default class Collapse extends React.Component<CollapseProps, any> {
<RightOutlined rotate={panelProps.isActive ? 90 : undefined} /> <RightOutlined rotate={panelProps.isActive ? 90 : undefined} />
)) as React.ReactNode; )) as React.ReactNode;
return React.isValidElement(icon) return cloneElement(icon, () => ({
? React.cloneElement(icon as any, { className: classNames((icon as any).props.className, `${prefixCls}-arrow`),
className: classNames(icon.props.className, `${prefixCls}-arrow`), }));
})
: icon;
}; };
renderCollapse = ({ getPrefixCls, direction }: ConfigConsumerProps) => { renderCollapse = ({ getPrefixCls, direction }: ConfigConsumerProps) => {

View File

@ -11,6 +11,7 @@ import devWarning from '../_util/devWarning';
import { ConfigContext } from '../config-provider'; import { ConfigContext } from '../config-provider';
import Row from './Row'; import Row from './Row';
import DescriptionsItem from './Item'; import DescriptionsItem from './Item';
import { cloneElement } from '../_util/reactNode';
const DEFAULT_COLUMN_MAP: Record<Breakpoint, number> = { const DEFAULT_COLUMN_MAP: Record<Breakpoint, number> = {
xxl: 3, xxl: 3,
@ -46,7 +47,7 @@ function getFilledItem(
let clone = node; let clone = node;
if (span === undefined || span > rowRestCol) { if (span === undefined || span > rowRestCol) {
clone = React.cloneElement(node, { clone = cloneElement(node, {
span: rowRestCol, span: rowRestCol,
}); });
devWarning( devWarning(

View File

@ -7,6 +7,7 @@ import DropdownButton from './dropdown-button';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import devWarning from '../_util/devWarning'; import devWarning from '../_util/devWarning';
import { tuple } from '../_util/type'; import { tuple } from '../_util/type';
import { cloneElement } from '../_util/reactNode';
const Placements = tuple( const Placements = tuple(
'topLeft', 'topLeft',
@ -107,7 +108,7 @@ export default class Dropdown extends React.Component<DropDownProps, any> {
const fixedModeOverlay = const fixedModeOverlay =
typeof overlayNode.type === 'string' typeof overlayNode.type === 'string'
? overlay ? overlay
: React.cloneElement(overlayNode, { : cloneElement(overlayNode, {
mode: 'vertical', mode: 'vertical',
selectable, selectable,
focusable, focusable,
@ -142,7 +143,7 @@ export default class Dropdown extends React.Component<DropDownProps, any> {
const prefixCls = getPrefixCls('dropdown', customizePrefixCls); const prefixCls = getPrefixCls('dropdown', customizePrefixCls);
const child = React.Children.only(children) as React.ReactElement<any>; const child = React.Children.only(children) as React.ReactElement<any>;
const dropdownTrigger = React.cloneElement(child, { const dropdownTrigger = cloneElement(child, {
className: classNames(child.props.className, `${prefixCls}-trigger`, { className: classNames(child.props.className, `${prefixCls}-trigger`, {
[`${prefixCls}-rtl`]: direction === 'rtl', [`${prefixCls}-rtl`]: direction === 'rtl',
}), }),

View File

@ -13,6 +13,7 @@ import FormItemLabel, { FormItemLabelProps } from './FormItemLabel';
import FormItemInput, { FormItemInputProps } from './FormItemInput'; import FormItemInput, { FormItemInputProps } from './FormItemInput';
import { FormContext, FormItemContext } from './context'; import { FormContext, FormItemContext } from './context';
import { toArray, getFieldId, useFrameState } from './util'; import { toArray, getFieldId, useFrameState } from './util';
import { cloneElement, isValidElement } from '../_util/reactNode';
const ValidateStatuses = tuple('success', 'warning', 'error', 'validating', ''); const ValidateStatuses = tuple('success', 'warning', 'error', 'validating', '');
export type ValidateStatus = typeof ValidateStatuses[number]; export type ValidateStatus = typeof ValidateStatuses[number];
@ -306,7 +307,7 @@ function FormItem(props: FormItemProps): React.ReactElement {
'Form.Item', 'Form.Item',
'Must set `name` or use render props when `dependencies` is set.', 'Must set `name` or use render props when `dependencies` is set.',
); );
} else if (React.isValidElement(children)) { } else if (isValidElement(children)) {
devWarning( devWarning(
children.props.defaultValue === undefined, children.props.defaultValue === undefined,
'Form.Item', 'Form.Item',
@ -330,7 +331,7 @@ function FormItem(props: FormItemProps): React.ReactElement {
value={mergedControl[props.valuePropName || 'value']} value={mergedControl[props.valuePropName || 'value']}
update={updateRef.current} update={updateRef.current}
> >
{React.cloneElement(children, childProps)} {cloneElement(children, childProps)}
</MemoInput> </MemoInput>
); );
} else if (isRenderProps && shouldUpdate && !hasName) { } else if (isRenderProps && shouldUpdate && !hasName) {

View File

@ -4,6 +4,7 @@ import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import { tuple } from '../_util/type'; import { tuple } from '../_util/type';
import { InputProps, getInputClassName } from './Input'; import { InputProps, getInputClassName } from './Input';
import { SizeType } from '../config-provider/SizeContext'; import { SizeType } from '../config-provider/SizeContext';
import { cloneElement } from '../_util/reactNode';
const ClearableInputType = tuple('text', 'input'); const ClearableInputType = tuple('text', 'input');
@ -103,7 +104,7 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
} = this.props; } = this.props;
const suffixNode = this.renderSuffix(prefixCls); const suffixNode = this.renderSuffix(prefixCls);
if (!hasPrefixSuffix(this.props)) { if (!hasPrefixSuffix(this.props)) {
return React.cloneElement(element, { return cloneElement(element, {
value, value,
}); });
} }
@ -126,7 +127,7 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
onMouseUp={this.onInputMouseUp} onMouseUp={this.onInputMouseUp}
> >
{prefixNode} {prefixNode}
{React.cloneElement(element, { {cloneElement(element, {
style: null, style: null,
value, value,
className: getInputClassName(prefixCls, size, disabled), className: getInputClassName(prefixCls, size, disabled),
@ -167,7 +168,7 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
<span className={mergedGroupClassName} style={style}> <span className={mergedGroupClassName} style={style}>
<span className={mergedWrapperClassName}> <span className={mergedWrapperClassName}>
{addonBeforeNode} {addonBeforeNode}
{React.cloneElement(labeledElement, { style: null })} {cloneElement(labeledElement, { style: null })}
{addonAfterNode} {addonAfterNode}
</span> </span>
</span> </span>
@ -177,7 +178,7 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
renderTextAreaWithClearIcon(prefixCls: string, element: React.ReactElement<any>) { renderTextAreaWithClearIcon(prefixCls: string, element: React.ReactElement<any>) {
const { value, allowClear, className, style, direction } = this.props; const { value, allowClear, className, style, direction } = this.props;
if (!allowClear) { if (!allowClear) {
return React.cloneElement(element, { return cloneElement(element, {
value, value,
}); });
} }
@ -189,7 +190,7 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
); );
return ( return (
<span className={affixWrapperCls} style={style}> <span className={affixWrapperCls} style={style}>
{React.cloneElement(element, { {cloneElement(element, {
style: null, style: null,
value, value,
})} })}

View File

@ -6,6 +6,7 @@ import Input, { InputProps } from './Input';
import Button from '../button'; import Button from '../button';
import SizeContext, { SizeType } from '../config-provider/SizeContext'; import SizeContext, { SizeType } from '../config-provider/SizeContext';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import { replaceElement, cloneElement } from '../_util/reactNode';
export interface SearchProps extends InputProps { export interface SearchProps extends InputProps {
inputPrefixCls?: string; inputPrefixCls?: string;
@ -97,11 +98,9 @@ export default class Search extends React.Component<SearchProps, any> {
if (suffix) { if (suffix) {
return [ return [
React.isValidElement(suffix) replaceElement(suffix, null, {
? React.cloneElement(suffix, {
key: 'suffix', key: 'suffix',
}) }),
: null,
icon, icon,
]; ];
} }
@ -125,7 +124,7 @@ export default class Search extends React.Component<SearchProps, any> {
enterButtonAsElement.type && enterButtonAsElement.type &&
(enterButtonAsElement.type as typeof Button).__ANT_BUTTON === true; (enterButtonAsElement.type as typeof Button).__ANT_BUTTON === true;
if (isAntdButton || enterButtonAsElement.type === 'button') { if (isAntdButton || enterButtonAsElement.type === 'button') {
button = React.cloneElement(enterButtonAsElement, { button = cloneElement(enterButtonAsElement, {
onMouseDown: this.onMouseDown, onMouseDown: this.onMouseDown,
onClick: this.onSearch, onClick: this.onSearch,
key: 'enterButton', key: 'enterButton',
@ -155,11 +154,9 @@ export default class Search extends React.Component<SearchProps, any> {
if (addonAfter) { if (addonAfter) {
return [ return [
button, button,
React.isValidElement(addonAfter) replaceElement(addonAfter, null, {
? React.cloneElement(addonAfter, {
key: 'addonAfter', key: 'addonAfter',
}) }),
: null,
]; ];
} }

View File

@ -8,6 +8,7 @@ import { RenderEmptyHandler, ConfigContext } from '../config-provider';
import Pagination, { PaginationConfig } from '../pagination'; import Pagination, { PaginationConfig } from '../pagination';
import { Row } from '../grid'; import { Row } from '../grid';
import Item from './Item'; import Item from './Item';
import { cloneElement } from '../_util/reactNode';
export { ListItemProps, ListItemMetaProps } from './Item'; export { ListItemProps, ListItemMetaProps } from './Item';
@ -247,7 +248,7 @@ function List<T>({ pagination, ...props }: ListProps<T>) {
if (splitDataSource.length > 0) { if (splitDataSource.length > 0) {
const items = splitDataSource.map((item: any, index: number) => renderItem(item, index)); const items = splitDataSource.map((item: any, index: number) => renderItem(item, index));
const childrenList = React.Children.map(items, (child: any, index) => const childrenList = React.Children.map(items, (child: any, index) =>
React.cloneElement(child, { cloneElement(child, {
key: keys[index], key: keys[index],
colStyle, colStyle,
}), }),

View File

@ -6,6 +6,7 @@ import { ClickParam } from '.';
import MenuContext, { MenuContextProps } from './MenuContext'; import MenuContext, { MenuContextProps } from './MenuContext';
import Tooltip, { TooltipProps } from '../tooltip'; import Tooltip, { TooltipProps } from '../tooltip';
import { SiderContext, SiderContextProps } from '../layout/Sider'; import { SiderContext, SiderContextProps } from '../layout/Sider';
import { isValidElement } from '../_util/reactNode';
export interface MenuItemProps export interface MenuItemProps
extends Omit< extends Omit<
@ -43,7 +44,7 @@ export default class MenuItem extends React.Component<MenuItemProps> {
const { icon, children } = this.props; const { icon, children } = this.props;
// inline-collapsed.md demo 依赖 span 来隐藏文字,有 icon 属性,则内部包裹一个 span // inline-collapsed.md demo 依赖 span 来隐藏文字,有 icon 属性,则内部包裹一个 span
// ref: https://github.com/ant-design/ant-design/pull/23456 // ref: https://github.com/ant-design/ant-design/pull/23456
if (!icon || (React.isValidElement(children) && children.type === 'span')) { if (!icon || (isValidElement(children) && children.type === 'span')) {
return children; return children;
} }
return <span>{children}</span>; return <span>{children}</span>;

View File

@ -4,6 +4,7 @@ import { SubMenu as RcSubMenu } from 'rc-menu';
import classNames from 'classnames'; import classNames from 'classnames';
import omit from 'omit.js'; import omit from 'omit.js';
import MenuContext, { MenuContextProps } from './MenuContext'; import MenuContext, { MenuContextProps } from './MenuContext';
import { isValidElement } from '../_util/reactNode';
interface TitleEventEntity { interface TitleEventEntity {
key: string; key: string;
@ -49,7 +50,7 @@ class SubMenu extends React.Component<SubMenuProps, any> {
} }
// inline-collapsed.md demo 依赖 span 来隐藏文字,有 icon 属性,则内部包裹一个 span // inline-collapsed.md demo 依赖 span 来隐藏文字,有 icon 属性,则内部包裹一个 span
// ref: https://github.com/ant-design/ant-design/pull/23456 // ref: https://github.com/ant-design/ant-design/pull/23456
const titleIsSpan = React.isValidElement(title) && title.type === 'span'; const titleIsSpan = isValidElement(title) && title.type === 'span';
return ( return (
<> <>
{icon} {icon}

View File

@ -4,6 +4,7 @@ import omit from 'omit.js';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import { tuple } from '../_util/type'; import { tuple } from '../_util/type';
import { isValidElement, cloneElement } from '../_util/reactNode';
const SpinSizes = tuple('small', 'default', 'large'); const SpinSizes = tuple('small', 'default', 'large');
export type SpinSize = typeof SpinSizes[number]; export type SpinSize = typeof SpinSizes[number];
@ -38,14 +39,14 @@ function renderIndicator(prefixCls: string, props: SpinProps): React.ReactNode {
return null; return null;
} }
if (React.isValidElement(indicator)) { if (isValidElement(indicator)) {
return React.cloneElement(indicator, { return cloneElement(indicator, {
className: classNames(indicator.props.className, dotClassName), className: classNames(indicator.props.className, dotClassName),
}); });
} }
if (React.isValidElement(defaultIndicator)) { if (isValidElement(defaultIndicator)) {
return React.cloneElement(defaultIndicator as SpinIndicator, { return cloneElement(defaultIndicator as SpinIndicator, {
className: classNames((defaultIndicator as SpinIndicator).props.className, dotClassName), className: classNames((defaultIndicator as SpinIndicator).props.className, dotClassName),
}); });
} }

View File

@ -1,6 +1,7 @@
import * as React from 'react'; import * as React from 'react';
import Statistic, { StatisticProps } from './Statistic'; import Statistic, { StatisticProps } from './Statistic';
import { formatCountdown, countdownValueType, FormatConfig } from './utils'; import { formatCountdown, countdownValueType, FormatConfig } from './utils';
import { cloneElement } from '../_util/reactNode';
const REFRESH_INTERVAL = 1000 / 30; const REFRESH_INTERVAL = 1000 / 30;
@ -72,7 +73,7 @@ class Countdown extends React.Component<CountdownProps, {}> {
// Countdown do not need display the timestamp // Countdown do not need display the timestamp
valueRender = (node: React.ReactElement<HTMLDivElement>) => valueRender = (node: React.ReactElement<HTMLDivElement>) =>
React.cloneElement(node, { cloneElement(node, {
title: undefined, title: undefined,
}); });

View File

@ -8,6 +8,7 @@ import RightOutlined from '@ant-design/icons/RightOutlined';
import { TabsProps } from './index'; import { TabsProps } from './index';
import { ConfigConsumerProps, ConfigConsumer } from '../config-provider'; import { ConfigConsumerProps, ConfigConsumer } from '../config-provider';
import { cloneElement } from '../_util/reactNode';
export default class TabBar extends React.Component<TabsProps> { export default class TabBar extends React.Component<TabsProps> {
static defaultProps = { static defaultProps = {
@ -81,7 +82,7 @@ export default class TabBar extends React.Component<TabsProps> {
RenderTabBar = <ScrollableInkTabBar {...renderProps} />; RenderTabBar = <ScrollableInkTabBar {...renderProps} />;
} }
return React.cloneElement(RenderTabBar); return cloneElement(RenderTabBar);
}; };
render() { render() {

View File

@ -10,6 +10,7 @@ import TabBar from './TabBar';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import { isFlexSupported } from '../_util/styleChecker'; import { isFlexSupported } from '../_util/styleChecker';
import { cloneElement, isValidElement } from '../_util/reactNode';
export type TabsType = 'line' | 'card' | 'editable-card'; export type TabsType = 'line' | 'card' | 'editable-card';
export type TabsPosition = 'top' | 'right' | 'bottom' | 'left'; export type TabsPosition = 'top' | 'right' | 'bottom' | 'left';
@ -129,7 +130,7 @@ export default class Tabs extends React.Component<TabsProps, any> {
if (type === 'editable-card') { if (type === 'editable-card') {
childrenWithClose = []; childrenWithClose = [];
React.Children.forEach(children as React.ReactNode, (child, index) => { React.Children.forEach(children as React.ReactNode, (child, index) => {
if (!React.isValidElement(child)) return child; if (!isValidElement(child)) return child;
let { closable } = child.props; let { closable } = child.props;
const { closeIcon: customCloseIcon } = child.props; const { closeIcon: customCloseIcon } = child.props;
closable = typeof closable === 'undefined' ? true : closable; closable = typeof closable === 'undefined' ? true : closable;
@ -146,7 +147,7 @@ export default class Tabs extends React.Component<TabsProps, any> {
); );
const closeIcon = closable ? customIcon : null; const closeIcon = closable ? customIcon : null;
childrenWithClose.push( childrenWithClose.push(
React.cloneElement(child, { cloneElement(child, {
tab: ( tab: (
<div className={closable ? undefined : `${prefixCls}-tab-unclosable`}> <div className={closable ? undefined : `${prefixCls}-tab-unclosable`}>
{child.props.tab} {child.props.tab}

View File

@ -4,6 +4,7 @@ import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
import TimelineItem, { TimeLineItemProps } from './TimelineItem'; import TimelineItem, { TimeLineItemProps } from './TimelineItem';
import { ConfigContext } from '../config-provider'; import { ConfigContext } from '../config-provider';
import { cloneElement } from '../_util/reactNode';
export interface TimelineProps { export interface TimelineProps {
prefixCls?: string; prefixCls?: string;
@ -64,7 +65,7 @@ const Timeline: TimelineType = props => {
const items = React.Children.map(truthyItems, (ele: React.ReactElement<any>, idx) => { const items = React.Children.map(truthyItems, (ele: React.ReactElement<any>, idx) => {
const pendingClass = idx === itemsCount - 2 ? lastCls : ''; const pendingClass = idx === itemsCount - 2 ? lastCls : '';
const readyClass = idx === itemsCount - 1 ? lastCls : ''; const readyClass = idx === itemsCount - 1 ? lastCls : '';
return React.cloneElement(ele, { return cloneElement(ele, {
className: classNames([ className: classNames([
ele.props.className, ele.props.className,
!reverse && !!pending ? pendingClass : readyClass, !reverse && !!pending ? pendingClass : readyClass,

View File

@ -5,6 +5,7 @@ import classNames from 'classnames';
import { BuildInPlacements } from 'rc-trigger/lib/interface'; import { BuildInPlacements } from 'rc-trigger/lib/interface';
import getPlacements, { AdjustOverflow, PlacementsConfig } from './placements'; import getPlacements, { AdjustOverflow, PlacementsConfig } from './placements';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import { cloneElement, isValidElement } from '../_util/reactNode';
export { AdjustOverflow, PlacementsConfig }; export { AdjustOverflow, PlacementsConfig };
@ -105,7 +106,7 @@ function getDisabledCompatibleChildren(element: React.ReactElement<any>, prefixC
...omitted, ...omitted,
pointerEvents: 'none', pointerEvents: 'none',
}; };
const child = React.cloneElement(element, { const child = cloneElement(element, {
style: buttonStyle, style: buttonStyle,
className: null, className: null,
}); });
@ -243,7 +244,7 @@ class Tooltip extends React.Component<TooltipProps, any> {
} }
const child = getDisabledCompatibleChildren( const child = getDisabledCompatibleChildren(
React.isValidElement(children) ? children : <span>{children}</span>, isValidElement(children) ? children : <span>{children}</span>,
prefixCls, prefixCls,
); );
@ -268,7 +269,7 @@ class Tooltip extends React.Component<TooltipProps, any> {
onVisibleChange={this.onVisibleChange} onVisibleChange={this.onVisibleChange}
onPopupAlign={this.onPopupAlign} onPopupAlign={this.onPopupAlign}
> >
{visible ? React.cloneElement(child, { className: childCls }) : child} {visible ? cloneElement(child, { className: childCls }) : child}
</RcTooltip> </RcTooltip>
); );
}; };

View File

@ -16,13 +16,14 @@ import {
import Search from './search'; import Search from './search';
import DefaultListBody, { TransferListBodyProps, OmitProps } from './ListBody'; import DefaultListBody, { TransferListBodyProps, OmitProps } from './ListBody';
import { PaginationType } from './interface'; import { PaginationType } from './interface';
import { isValidElement } from '../_util/reactNode';
const defaultRender = () => null; const defaultRender = () => null;
function isRenderResultPlainObject(result: RenderResult) { function isRenderResultPlainObject(result: RenderResult) {
return ( return (
result && result &&
!React.isValidElement(result) && !isValidElement(result) &&
Object.prototype.toString.call(result) === '[object Object]' Object.prototype.toString.call(result) === '[object Object]'
); );
} }

View File

@ -6,6 +6,7 @@ import MinusSquareOutlined from '@ant-design/icons/MinusSquareOutlined';
import PlusSquareOutlined from '@ant-design/icons/PlusSquareOutlined'; import PlusSquareOutlined from '@ant-design/icons/PlusSquareOutlined';
import CaretDownFilled from '@ant-design/icons/CaretDownFilled'; import CaretDownFilled from '@ant-design/icons/CaretDownFilled';
import { AntTreeNodeProps } from '../Tree'; import { AntTreeNodeProps } from '../Tree';
import { isValidElement, cloneElement } from '../../_util/reactNode';
export default function renderSwitcherIcon( export default function renderSwitcherIcon(
prefixCls: string, prefixCls: string,
@ -20,8 +21,8 @@ export default function renderSwitcherIcon(
return showLine ? <FileOutlined className={`${prefixCls}-switcher-line-icon`} /> : null; return showLine ? <FileOutlined className={`${prefixCls}-switcher-line-icon`} /> : null;
} }
const switcherCls = `${prefixCls}-switcher-icon`; const switcherCls = `${prefixCls}-switcher-icon`;
if (React.isValidElement(switcherIcon)) { if (isValidElement(switcherIcon)) {
return React.cloneElement(switcherIcon, { return cloneElement(switcherIcon, {
className: classNames(switcherIcon.props.className || '', switcherCls), className: classNames(switcherIcon.props.className || '', switcherCls),
}); });
} }

View File

@ -14,6 +14,7 @@ import { previewImage, isImageUrl } from './utils';
import Tooltip from '../tooltip'; import Tooltip from '../tooltip';
import Progress from '../progress'; import Progress from '../progress';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import { cloneElement, isValidElement } from '../_util/reactNode';
export default class UploadList extends React.Component<UploadListProps, any> { export default class UploadList extends React.Component<UploadListProps, any> {
static defaultProps = { static defaultProps = {
@ -98,8 +99,8 @@ export default class UploadList extends React.Component<UploadListProps, any> {
}; };
handleActionIconRender = (customIcon: React.ReactNode, callback: () => void, title?: string) => { handleActionIconRender = (customIcon: React.ReactNode, callback: () => void, title?: string) => {
if (React.isValidElement(customIcon)) { if (isValidElement(customIcon)) {
return React.cloneElement(customIcon, { return cloneElement(customIcon, {
...customIcon.props, ...customIcon.props,
title, title,
onClick: (e: React.MouseEvent<HTMLElement>) => { onClick: (e: React.MouseEvent<HTMLElement>) => {

View File

@ -56,7 +56,6 @@ function processWebpackThemeConfig(themeConfig, theme, vars) {
themeConfig.forEach(config => { themeConfig.forEach(config => {
ignoreMomentLocale(config); ignoreMomentLocale(config);
externalMoment(config); externalMoment(config);
injectWarningCondition(config);
// rename default entry to ${theme} entry // rename default entry to ${theme} entry
Object.keys(config.entry).forEach(entryName => { Object.keys(config.entry).forEach(entryName => {
@ -87,6 +86,10 @@ const webpackConfig = getWebpackConfig(false);
const webpackDarkConfig = getWebpackConfig(false); const webpackDarkConfig = getWebpackConfig(false);
const webpackCompactConfig = getWebpackConfig(false); const webpackCompactConfig = getWebpackConfig(false);
webpackConfig.forEach(config => {
injectWarningCondition(config);
});
if (process.env.RUN_ENV === 'PRODUCTION') { if (process.env.RUN_ENV === 'PRODUCTION') {
webpackConfig.forEach(config => { webpackConfig.forEach(config => {
ignoreMomentLocale(config); ignoreMomentLocale(config);