chore: next merge feature

This commit is contained in:
zombiej 2022-05-06 18:46:16 +08:00
commit 84662ce774
32 changed files with 5575 additions and 248 deletions

View File

@ -9,6 +9,7 @@ import Wave from '../_util/wave';
import { tuple } from '../_util/type'; import { 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 DisabledContext from '../config-provider/DisabledContext';
import LoadingIcon from './LoadingIcon'; import LoadingIcon from './LoadingIcon';
import { cloneElement } from '../_util/reactNode'; import { cloneElement } from '../_util/reactNode';
@ -104,6 +105,7 @@ export interface BaseButtonProps {
*/ */
shape?: ButtonShape; shape?: ButtonShape;
size?: SizeType; size?: SizeType;
disabled?: boolean;
loading?: boolean | { delay?: number }; loading?: boolean | { delay?: number };
prefixCls?: string; prefixCls?: string;
className?: string; className?: string;
@ -147,6 +149,7 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
danger, danger,
shape = 'default', shape = 'default',
size: customizeSize, size: customizeSize,
disabled: customDisabled,
className, className,
children, children,
icon, icon,
@ -165,6 +168,10 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
const [wrapSSR, hashId] = useStyle(prefixCls); const [wrapSSR, hashId] = useStyle(prefixCls);
const size = React.useContext(SizeContext); const size = React.useContext(SizeContext);
// ===================== Disabled =====================
const disabled = React.useContext(DisabledContext);
const mergedDisabled = customDisabled || disabled;
const groupSize = React.useContext(GroupSizeContext); const groupSize = React.useContext(GroupSizeContext);
const [innerLoading, setLoading] = React.useState<Loading>(!!loading); const [innerLoading, setLoading] = React.useState<Loading>(!!loading);
const [hasTwoCNChar, setHasTwoCNChar] = React.useState(false); const [hasTwoCNChar, setHasTwoCNChar] = React.useState(false);
@ -217,9 +224,9 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
React.useEffect(fixTwoCNChar, [buttonRef]); React.useEffect(fixTwoCNChar, [buttonRef]);
const handleClick = (e: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement, MouseEvent>) => { const handleClick = (e: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement, MouseEvent>) => {
const { onClick, disabled } = props; const { onClick } = props;
// https://github.com/ant-design/ant-design/issues/30207 // https://github.com/ant-design/ant-design/issues/30207
if (innerLoading || disabled) { if (innerLoading || mergedDisabled) {
e.preventDefault(); e.preventDefault();
return; return;
} }
@ -292,6 +299,7 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
type={htmlType} type={htmlType}
className={classes} className={classes}
onClick={handleClick} onClick={handleClick}
disabled={mergedDisabled}
ref={buttonRef} ref={buttonRef}
> >
{iconNode} {iconNode}

View File

@ -80,7 +80,6 @@
// } // }
// &-extra { // &-extra {
// float: right;
// // https://stackoverflow.com/a/22429853/3040605 // // https://stackoverflow.com/a/22429853/3040605
// margin-left: auto; // margin-left: auto;
// padding: @card-head-padding 0; // padding: @card-head-padding 0;
@ -99,13 +98,17 @@
// .clearfix(); // .clearfix();
// } // }
// &-contain-grid &-body {
// display: flex;
// flex-wrap: wrap;
// }
// &-contain-grid:not(&-loading) &-body { // &-contain-grid:not(&-loading) &-body {
// margin: -1px 0 0 -1px; // margin: -1px 0 0 -1px;
// padding: 0; // padding: 0;
// } // }
// &-grid { // &-grid {
// float: left;
// width: 33.33%; // width: 33.33%;
// padding: @card-padding-base; // padding: @card-padding-base;
// border: 0; // border: 0;
@ -115,10 +118,6 @@
// 0 1px 0 0 @border-color-split inset; // 0 1px 0 0 @border-color-split inset;
// transition: all 0.3s; // transition: all 0.3s;
// .@{card-prefix-cls}-rtl & {
// float: right;
// }
// &-hoverable { // &-hoverable {
// &:hover { // &:hover {
// position: relative; // position: relative;
@ -155,6 +154,7 @@
// } // }
// &-actions { // &-actions {
// display: flex;
// margin: 0; // margin: 0;
// padding: 0; // padding: 0;
// list-style: none; // list-style: none;
@ -163,15 +163,10 @@
// .clearfix(); // .clearfix();
// & > li { // & > li {
// float: left;
// margin: @card-actions-li-margin; // margin: @card-actions-li-margin;
// color: @text-color-secondary; // color: @text-color-secondary;
// text-align: center; // text-align: center;
// .@{card-prefix-cls}-rtl & {
// float: right;
// }
// > span { // > span {
// position: relative; // position: relative;
// display: block; // display: block;
@ -234,15 +229,14 @@
// } // }
// &-meta { // &-meta {
// display: flex;
// margin: -4px 0; // margin: -4px 0;
// .clearfix(); // .clearfix();
// &-avatar { // &-avatar {
// float: left;
// padding-right: 16px; // padding-right: 16px;
// .@{card-prefix-cls}-rtl & { // .@{card-prefix-cls}-rtl & {
// float: right;
// padding-right: 0; // padding-right: 0;
// padding-left: 16px; // padding-left: 16px;
// } // }

View File

@ -18,6 +18,7 @@ import devWarning from '../_util/devWarning';
import { ConfigContext } from '../config-provider'; import { ConfigContext } from '../config-provider';
import type { SizeType } from '../config-provider/SizeContext'; import type { SizeType } from '../config-provider/SizeContext';
import SizeContext from '../config-provider/SizeContext'; import SizeContext from '../config-provider/SizeContext';
import DisabledContext from '../config-provider/DisabledContext';
import getIcons from '../select/utils/iconUtil'; import getIcons from '../select/utils/iconUtil';
import { getTransitionName, getTransitionDirection, SelectCommonPlacement } from '../_util/motion'; import { getTransitionName, getTransitionDirection, SelectCommonPlacement } from '../_util/motion';
import { FormItemInputContext } from '../form/context'; import { FormItemInputContext } from '../form/context';
@ -100,6 +101,7 @@ type UnionCascaderProps = SingleCascaderProps | MultipleCascaderProps;
export type CascaderProps<DataNodeType> = UnionCascaderProps & { export type CascaderProps<DataNodeType> = UnionCascaderProps & {
multiple?: boolean; multiple?: boolean;
size?: SizeType; size?: SizeType;
disabled?: boolean;
bordered?: boolean; bordered?: boolean;
placement?: SelectCommonPlacement; placement?: SelectCommonPlacement;
suffixIcon?: React.ReactNode; suffixIcon?: React.ReactNode;
@ -116,6 +118,7 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
const { const {
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
size: customizeSize, size: customizeSize,
disabled: customDisabled,
className, className,
multiple, multiple,
bordered = true, bordered = true,
@ -218,6 +221,10 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
const size = React.useContext(SizeContext); const size = React.useContext(SizeContext);
const mergedSize = customizeSize || size; const mergedSize = customizeSize || size;
// ===================== Disabled =====================
const disabled = React.useContext(DisabledContext);
const mergedDisabled = customDisabled || disabled;
// ===================== Icon ====================== // ===================== Icon ======================
let mergedExpandIcon = expandIcon; let mergedExpandIcon = expandIcon;
if (!expandIcon) { if (!expandIcon) {
@ -274,6 +281,7 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
className, className,
hashId, hashId,
)} )}
disabled={mergedDisabled}
{...(restProps as any)} {...(restProps as any)}
direction={mergedDirection} direction={mergedDirection}
placement={getPlacement()} placement={getPlacement()}

View File

@ -0,0 +1,21 @@
import * as React from 'react';
export type DisabledType = true | false | undefined;
const DisabledContext = React.createContext<DisabledType>(false);
export interface DisabledContextProps {
disabled?: DisabledType;
children?: React.ReactNode;
}
export const DisabledContextProvider: React.FC<DisabledContextProps> = ({ children, disabled }) => {
const originDisabled = React.useContext(DisabledContext);
return (
<DisabledContext.Provider value={disabled || originDisabled}>
{children}
</DisabledContext.Provider>
);
};
export default DisabledContext;

View File

@ -10,6 +10,7 @@ import { GenerateConfig } from 'rc-picker/lib/generate/index';
import enUS from '../locale/en_US'; import enUS from '../locale/en_US';
import { ConfigConsumerProps, ConfigContext } from '../../config-provider'; import { ConfigConsumerProps, ConfigContext } from '../../config-provider';
import SizeContext from '../../config-provider/SizeContext'; import SizeContext from '../../config-provider/SizeContext';
import DisabledContext from '../../config-provider/DisabledContext';
import LocaleReceiver from '../../locale-provider/LocaleReceiver'; import LocaleReceiver from '../../locale-provider/LocaleReceiver';
import { getRangePlaceholder, transPlacement2DropdownAlign } from '../util'; import { getRangePlaceholder, transPlacement2DropdownAlign } from '../util';
import { Components, getTimeProps, PickerLocale, RangePickerProps } from '.'; import { Components, getTimeProps, PickerLocale, RangePickerProps } from '.';
@ -53,6 +54,7 @@ export default function generateRangePicker<DateType>(
className, className,
placement, placement,
size: customizeSize, size: customizeSize,
disabled: customDisabled,
bordered = true, bordered = true,
placeholder, placeholder,
status: customStatus, status: customStatus,
@ -72,67 +74,75 @@ export default function generateRangePicker<DateType>(
const rootPrefixCls = getPrefixCls(); const rootPrefixCls = getPrefixCls();
return ( return (
<SizeContext.Consumer> <DisabledContext.Consumer>
{size => { {disabled => {
const mergedSize = customizeSize || size; const mergedDisabled = customDisabled || disabled;
return ( return (
<FormItemInputContext.Consumer> <SizeContext.Consumer>
{({ hasFeedback, status: contextStatus, feedbackIcon }) => { {size => {
const suffixNode = ( const mergedSize = customizeSize || size;
<>
{picker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />}
{hasFeedback && feedbackIcon}
</>
);
return ( return (
<RCRangePicker<DateType> <FormItemInputContext.Consumer>
separator={ {({ hasFeedback, status: contextStatus, feedbackIcon }) => {
<span aria-label="to" className={`${prefixCls}-separator`}> const suffixNode = (
<SwapRightOutlined /> <>
</span> {picker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />}
} {hasFeedback && feedbackIcon}
ref={this.pickerRef} </>
dropdownAlign={transPlacement2DropdownAlign(direction, placement)} );
placeholder={getRangePlaceholder(picker, locale, placeholder)}
suffixIcon={suffixNode} return (
clearIcon={<CloseCircleFilled />} <RCRangePicker<DateType>
prevIcon={<span className={`${prefixCls}-prev-icon`} />} separator={
nextIcon={<span className={`${prefixCls}-next-icon`} />} <span aria-label="to" className={`${prefixCls}-separator`}>
superPrevIcon={<span className={`${prefixCls}-super-prev-icon`} />} <SwapRightOutlined />
superNextIcon={<span className={`${prefixCls}-super-next-icon`} />} </span>
allowClear }
transitionName={`${rootPrefixCls}-slide-up`} disabled={mergedDisabled}
{...restProps} ref={this.pickerRef}
{...additionalOverrideProps} dropdownAlign={transPlacement2DropdownAlign(direction, placement)}
className={classNames( placeholder={getRangePlaceholder(picker, locale, placeholder)}
{ suffixIcon={suffixNode}
[`${prefixCls}-${mergedSize}`]: mergedSize, clearIcon={<CloseCircleFilled />}
[`${prefixCls}-borderless`]: !bordered, prevIcon={<span className={`${prefixCls}-prev-icon`} />}
}, nextIcon={<span className={`${prefixCls}-next-icon`} />}
getStatusClassNames( superPrevIcon={<span className={`${prefixCls}-super-prev-icon`} />}
prefixCls as string, superNextIcon={<span className={`${prefixCls}-super-next-icon`} />}
getMergedStatus(contextStatus, customStatus), allowClear
hasFeedback, transitionName={`${rootPrefixCls}-slide-up`}
), {...restProps}
hashId, {...additionalOverrideProps}
className, className={classNames(
)} {
locale={locale!.lang} [`${prefixCls}-${mergedSize}`]: mergedSize,
prefixCls={prefixCls} [`${prefixCls}-borderless`]: !bordered,
getPopupContainer={customGetPopupContainer || getPopupContainer} },
generateConfig={generateConfig} getStatusClassNames(
components={Components} prefixCls as string,
direction={direction} getMergedStatus(contextStatus, customStatus),
dropdownClassName={classNames(hashId, dropdownClassName)} hasFeedback,
/> ),
hashId,
className,
)}
locale={locale!.lang}
prefixCls={prefixCls}
getPopupContainer={customGetPopupContainer || getPopupContainer}
generateConfig={generateConfig}
components={Components}
direction={direction}
dropdownClassName={classNames(hashId, dropdownClassName)}
/>
);
}}
</FormItemInputContext.Consumer>
); );
}} }}
</FormItemInputContext.Consumer> </SizeContext.Consumer>
); );
}} }}
</SizeContext.Consumer> </DisabledContext.Consumer>
); );
}; };

View File

@ -13,6 +13,7 @@ import devWarning from '../../_util/devWarning';
import { ConfigContext, ConfigConsumerProps } from '../../config-provider'; import { ConfigContext, ConfigConsumerProps } from '../../config-provider';
import LocaleReceiver from '../../locale-provider/LocaleReceiver'; import LocaleReceiver from '../../locale-provider/LocaleReceiver';
import SizeContext from '../../config-provider/SizeContext'; import SizeContext from '../../config-provider/SizeContext';
import DisabledContext from '../../config-provider/DisabledContext';
import { import {
PickerProps, PickerProps,
PickerLocale, PickerLocale,
@ -76,6 +77,7 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
getPopupContainer: customizeGetPopupContainer, getPopupContainer: customizeGetPopupContainer,
className, className,
size: customizeSize, size: customizeSize,
disabled: customDisabled,
bordered = true, bordered = true,
placement, placement,
placeholder, placeholder,
@ -106,63 +108,74 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
const rootPrefixCls = getPrefixCls(); const rootPrefixCls = getPrefixCls();
return ( return (
<SizeContext.Consumer> <DisabledContext.Consumer>
{size => { {disabled => {
const mergedSize = customizeSize || size; const mergedDisabled = customDisabled || disabled;
return ( return (
<FormItemInputContext.Consumer> <SizeContext.Consumer>
{({ hasFeedback, status: contextStatus, feedbackIcon }) => { {size => {
const suffixNode = ( const mergedSize = customizeSize || size;
<>
{mergedPicker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />}
{hasFeedback && feedbackIcon}
</>
);
return ( return (
<RCPicker<DateType> <FormItemInputContext.Consumer>
ref={this.pickerRef} {({ hasFeedback, status: contextStatus, feedbackIcon }) => {
placeholder={getPlaceholder(mergedPicker, locale, placeholder)} const suffixNode = (
suffixIcon={suffixNode} <>
dropdownAlign={transPlacement2DropdownAlign(direction, placement)} {mergedPicker === 'time' ? (
clearIcon={<CloseCircleFilled />} <ClockCircleOutlined />
prevIcon={<span className={`${prefixCls}-prev-icon`} />} ) : (
nextIcon={<span className={`${prefixCls}-next-icon`} />} <CalendarOutlined />
superPrevIcon={<span className={`${prefixCls}-super-prev-icon`} />} )}
superNextIcon={<span className={`${prefixCls}-super-next-icon`} />} {hasFeedback && feedbackIcon}
allowClear </>
transitionName={`${rootPrefixCls}-slide-up`} );
{...additionalProps}
{...restProps} return (
{...additionalOverrideProps} <RCPicker<DateType>
locale={locale!.lang} ref={this.pickerRef}
className={classNames( placeholder={getPlaceholder(mergedPicker, locale, placeholder)}
{ suffixIcon={suffixNode}
[`${prefixCls}-${mergedSize}`]: mergedSize, dropdownAlign={transPlacement2DropdownAlign(direction, placement)}
[`${prefixCls}-borderless`]: !bordered, clearIcon={<CloseCircleFilled />}
}, prevIcon={<span className={`${prefixCls}-prev-icon`} />}
getStatusClassNames( nextIcon={<span className={`${prefixCls}-next-icon`} />}
prefixCls as string, superPrevIcon={<span className={`${prefixCls}-super-prev-icon`} />}
getMergedStatus(contextStatus, customStatus), superNextIcon={<span className={`${prefixCls}-super-next-icon`} />}
hasFeedback, allowClear
), transitionName={`${rootPrefixCls}-slide-up`}
hashId, {...additionalProps}
className, {...restProps}
)} {...additionalOverrideProps}
prefixCls={prefixCls} locale={locale!.lang}
getPopupContainer={customizeGetPopupContainer || getPopupContainer} className={classNames(
generateConfig={generateConfig} {
components={Components} [`${prefixCls}-${mergedSize}`]: mergedSize,
direction={direction} [`${prefixCls}-borderless`]: !bordered,
dropdownClassName={classNames(hashId, dropdownClassName)} },
/> 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)}
/>
);
}}
</FormItemInputContext.Consumer>
); );
}} }}
</FormItemInputContext.Consumer> </SizeContext.Consumer>
); );
}} }}
</SizeContext.Consumer> </DisabledContext.Consumer>
); );
}; };

View File

@ -18,6 +18,7 @@ When there are more than a few options to choose from, you can wrap them in a `D
| Property | Description | Type | Default | Version | | Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| arrow | Whether the dropdown arrow should be visible | boolean \| { pointAtCenter: boolean } | false | | | arrow | Whether the dropdown arrow should be visible | boolean \| { pointAtCenter: boolean } | false | |
| autoFocus | Focus element in `overlay` when opened | boolean | false | 4.21.0 |
| disabled | Whether the dropdown menu is disabled | boolean | - | | | disabled | Whether the dropdown menu is disabled | boolean | - | |
| destroyPopupOnHide | Whether destroy dropdown when hidden | boolean | false | | | destroyPopupOnHide | Whether destroy dropdown when hidden | boolean | false | |
| getPopupContainer | To set the container of the dropdown menu. The default is to create a div element in body, but you can reset it to the scrolling area and make a relative reposition. [Example on CodePen](https://codepen.io/afc163/pen/zEjNOy?editors=0010) | (triggerNode: HTMLElement) => HTMLElement | () => document.body | | | getPopupContainer | To set the container of the dropdown menu. The default is to create a div element in body, but you can reset it to the scrolling area and make a relative reposition. [Example on CodePen](https://codepen.io/afc163/pen/zEjNOy?editors=0010) | (triggerNode: HTMLElement) => HTMLElement | () => document.body | |

View File

@ -22,6 +22,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/eedWN59yJ/Dropdown.svg
| 参数 | 说明 | 类型 | 默认值 | 版本 | | 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| arrow | 下拉框箭头是否显示 | boolean \| { pointAtCenter: boolean } | false | | | arrow | 下拉框箭头是否显示 | boolean \| { pointAtCenter: boolean } | false | |
| autoFocus | 打开后自动聚焦下拉框 | boolean | false | 4.21.0 |
| disabled | 菜单是否禁用 | boolean | - | | | disabled | 菜单是否禁用 | boolean | - | |
| destroyPopupOnHide | 关闭后是否销毁 Dropdown | boolean | false | | | destroyPopupOnHide | 关闭后是否销毁 Dropdown | boolean | false | |
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。[示例](https://codepen.io/afc163/pen/zEjNOy?editors=0010) | (triggerNode: HTMLElement) => HTMLElement | () => document.body | | | getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。[示例](https://codepen.io/afc163/pen/zEjNOy?editors=0010) | (triggerNode: HTMLElement) => HTMLElement | () => document.body | |

View File

@ -14,6 +14,7 @@ import useForm, { FormInstance } from './hooks/useForm';
import type { SizeType } from '../config-provider/SizeContext'; import type { SizeType } from '../config-provider/SizeContext';
import SizeContext, { SizeContextProvider } from '../config-provider/SizeContext'; import SizeContext, { SizeContextProvider } from '../config-provider/SizeContext';
import useStyle from './style'; import useStyle from './style';
import DisabledContext, { DisabledContextProvider } from '../config-provider/DisabledContext';
export type RequiredMark = boolean | 'optional'; export type RequiredMark = boolean | 'optional';
export type FormLayout = 'horizontal' | 'inline' | 'vertical'; export type FormLayout = 'horizontal' | 'inline' | 'vertical';
@ -29,6 +30,7 @@ export interface FormProps<Values = any> extends Omit<RcFormProps<Values>, 'form
wrapperCol?: ColProps; wrapperCol?: ColProps;
form?: FormInstance<Values>; form?: FormInstance<Values>;
size?: SizeType; size?: SizeType;
disabled?: boolean;
scrollToFirstError?: Options | boolean; scrollToFirstError?: Options | boolean;
requiredMark?: RequiredMark; requiredMark?: RequiredMark;
/** @deprecated Will warning in future branch. Pls use `requiredMark` instead. */ /** @deprecated Will warning in future branch. Pls use `requiredMark` instead. */
@ -37,12 +39,14 @@ export interface FormProps<Values = any> extends Omit<RcFormProps<Values>, 'form
const InternalForm: React.ForwardRefRenderFunction<FormInstance, FormProps> = (props, ref) => { const InternalForm: React.ForwardRefRenderFunction<FormInstance, FormProps> = (props, ref) => {
const contextSize = React.useContext(SizeContext); const contextSize = React.useContext(SizeContext);
const contextDisabled = React.useContext(DisabledContext);
const { getPrefixCls, direction, form: contextForm } = React.useContext(ConfigContext); const { getPrefixCls, direction, form: contextForm } = React.useContext(ConfigContext);
const { const {
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
className = '', className = '',
size = contextSize, size = contextSize,
disabled = contextDisabled,
form, form,
colon, colon,
labelAlign, labelAlign,
@ -129,18 +133,20 @@ const InternalForm: React.ForwardRefRenderFunction<FormInstance, FormProps> = (p
}; };
return wrapSSR( return wrapSSR(
<SizeContextProvider size={size}> <DisabledContextProvider disabled={disabled}>
<FormContext.Provider value={formContextValue}> <SizeContextProvider size={size}>
<FieldForm <FormContext.Provider value={formContextValue}>
id={name} <FieldForm
{...restFormProps} id={name}
name={name} {...restFormProps}
onFinishFailed={onInternalFinishFailed} name={name}
form={wrapForm} onFinishFailed={onInternalFinishFailed}
className={formClassName} form={wrapForm}
/> className={formClassName}
</FormContext.Provider> />
</SizeContextProvider>, </FormContext.Provider>
</SizeContextProvider>
</DisabledContextProvider>,
); );
}; };

View File

@ -1374,6 +1374,793 @@ exports[`renders ./components/form/demo/dep-debug.md correctly 1`] = `
</form> </form>
`; `;
exports[`renders ./components/form/demo/disabled.md correctly 1`] = `
<form
class="ant-form ant-form-horizontal"
>
<div
class="ant-row ant-form-item"
>
<div
class="ant-col ant-col-4 ant-form-item-label"
>
<label
class=""
for="disabled"
title="Form disabled"
>
Form disabled
</label>
</div>
<div
class="ant-col ant-col-14 ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<label
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-wrapper-in-form-item"
>
<span
class="ant-checkbox ant-checkbox-checked"
>
<input
checked=""
class="ant-checkbox-input"
id="disabled"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
<span>
disabled
</span>
</label>
</div>
</div>
</div>
</div>
<div
class="ant-row ant-form-item"
>
<div
class="ant-col ant-col-4 ant-form-item-label"
>
<label
class=""
title="Radio"
>
Radio
</label>
</div>
<div
class="ant-col ant-col-14 ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<div
class="ant-radio-group ant-radio-group-outline"
>
<label
class="ant-radio-wrapper ant-radio-wrapper-disabled ant-radio-wrapper-in-form-item"
>
<span
class="ant-radio ant-radio-disabled"
>
<input
class="ant-radio-input"
disabled=""
type="radio"
value="apple"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
Apple
</span>
</label>
<label
class="ant-radio-wrapper ant-radio-wrapper-disabled ant-radio-wrapper-in-form-item"
>
<span
class="ant-radio ant-radio-disabled"
>
<input
class="ant-radio-input"
disabled=""
type="radio"
value="pear"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
Pear
</span>
</label>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-row ant-form-item"
>
<div
class="ant-col ant-col-4 ant-form-item-label"
>
<label
class=""
title="Input"
>
Input
</label>
</div>
<div
class="ant-col ant-col-14 ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<input
class="ant-input ant-input-disabled"
disabled=""
type="text"
value=""
/>
</div>
</div>
</div>
</div>
<div
class="ant-row ant-form-item"
>
<div
class="ant-col ant-col-4 ant-form-item-label"
>
<label
class=""
title="Select"
>
Select
</label>
</div>
<div
class="ant-col ant-col-14 ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<div
class="ant-select ant-select-in-form-item ant-select-single ant-select-show-arrow ant-select-disabled"
>
<div
class="ant-select-selector"
>
<span
class="ant-select-selection-search"
>
<input
aria-activedescendant="undefined_list_0"
aria-autocomplete="list"
aria-controls="undefined_list"
aria-expanded="false"
aria-haspopup="listbox"
aria-owns="undefined_list"
autocomplete="off"
class="ant-select-selection-search-input"
disabled=""
readonly=""
role="combobox"
style="opacity:0"
type="search"
unselectable="on"
value=""
/>
</span>
<span
class="ant-select-selection-placeholder"
/>
</div>
<span
aria-hidden="true"
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-select-suffix"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-row ant-form-item"
>
<div
class="ant-col ant-col-4 ant-form-item-label"
>
<label
class=""
title="TreeSelect"
>
TreeSelect
</label>
</div>
<div
class="ant-col ant-col-14 ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<div
class="ant-select ant-tree-select ant-select-in-form-item ant-select-single ant-select-show-arrow ant-select-disabled"
>
<div
class="ant-select-selector"
>
<span
class="ant-select-selection-search"
>
<input
aria-autocomplete="list"
aria-controls="undefined_list"
aria-expanded="false"
aria-haspopup="listbox"
aria-owns="undefined_list"
autocomplete="off"
class="ant-select-selection-search-input"
disabled=""
readonly=""
role="combobox"
style="opacity:0"
type="search"
unselectable="on"
value=""
/>
</span>
<span
class="ant-select-selection-placeholder"
/>
</div>
<span
aria-hidden="true"
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-select-suffix"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-row ant-form-item"
>
<div
class="ant-col ant-col-4 ant-form-item-label"
>
<label
class=""
title="Cascader"
>
Cascader
</label>
</div>
<div
class="ant-col ant-col-14 ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<div
class="ant-select ant-cascader ant-select-in-form-item ant-select-single ant-select-allow-clear ant-select-show-arrow ant-select-disabled"
>
<div
class="ant-select-selector"
>
<span
class="ant-select-selection-search"
>
<input
aria-autocomplete="list"
aria-controls="undefined_list"
aria-expanded="false"
aria-haspopup="listbox"
aria-owns="undefined_list"
autocomplete="off"
class="ant-select-selection-search-input"
disabled=""
readonly=""
role="combobox"
style="opacity:0"
type="search"
unselectable="on"
value=""
/>
</span>
<span
class="ant-select-selection-placeholder"
/>
</div>
<span
aria-hidden="true"
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-select-suffix"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-row ant-form-item"
>
<div
class="ant-col ant-col-4 ant-form-item-label"
>
<label
class=""
title="DatePicker"
>
DatePicker
</label>
</div>
<div
class="ant-col ant-col-14 ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<div
class="ant-picker ant-picker-disabled"
>
<div
class="ant-picker-input"
>
<input
autocomplete="off"
disabled=""
placeholder="Select date"
readonly=""
size="12"
title=""
value=""
/>
<span
class="ant-picker-suffix"
>
<span
aria-label="calendar"
class="anticon anticon-calendar"
role="img"
>
<svg
aria-hidden="true"
data-icon="calendar"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
/>
</svg>
</span>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-row ant-form-item"
>
<div
class="ant-col ant-col-4 ant-form-item-label"
>
<label
class=""
title="RangePicker"
>
RangePicker
</label>
</div>
<div
class="ant-col ant-col-14 ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<div
class="ant-picker ant-picker-range ant-picker-disabled"
>
<div
class="ant-picker-input ant-picker-input-active"
>
<input
autocomplete="off"
disabled=""
placeholder="Start date"
readonly=""
size="12"
value=""
/>
</div>
<div
class="ant-picker-range-separator"
>
<span
aria-label="to"
class="ant-picker-separator"
>
<span
aria-label="swap-right"
class="anticon anticon-swap-right"
role="img"
>
<svg
aria-hidden="true"
data-icon="swap-right"
fill="currentColor"
focusable="false"
height="1em"
viewBox="0 0 1024 1024"
width="1em"
>
<path
d="M873.1 596.2l-164-208A32 32 0 00684 376h-64.8c-6.7 0-10.4 7.7-6.3 13l144.3 183H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h695.9c26.8 0 41.7-30.8 25.2-51.8z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-picker-input"
>
<input
autocomplete="off"
disabled=""
placeholder="End date"
readonly=""
size="12"
value=""
/>
</div>
<div
class="ant-picker-active-bar"
style="left:0;width:0;position:absolute"
/>
<span
class="ant-picker-suffix"
>
<span
aria-label="calendar"
class="anticon anticon-calendar"
role="img"
>
<svg
aria-hidden="true"
data-icon="calendar"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
/>
</svg>
</span>
</span>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-row ant-form-item"
>
<div
class="ant-col ant-col-4 ant-form-item-label"
>
<label
class=""
title="InputNumber"
>
InputNumber
</label>
</div>
<div
class="ant-col ant-col-14 ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<div
class="ant-input-number ant-input-number-in-form-item ant-input-number-disabled"
>
<div
class="ant-input-number-handler-wrap"
>
<span
aria-disabled="false"
aria-label="Increase Value"
class="ant-input-number-handler ant-input-number-handler-up"
role="button"
unselectable="on"
>
<span
aria-label="up"
class="anticon anticon-up ant-input-number-handler-up-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
/>
</svg>
</span>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="ant-input-number-handler ant-input-number-handler-down"
role="button"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-input-number-handler-down-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-input-number-input-wrap"
>
<input
autocomplete="off"
class="ant-input-number-input"
disabled=""
role="spinbutton"
step="1"
value=""
/>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-row ant-form-item"
>
<div
class="ant-col ant-col-4 ant-form-item-label"
>
<label
class=""
title="TextArea"
>
TextArea
</label>
</div>
<div
class="ant-col ant-col-14 ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<textarea
class="ant-input ant-input-disabled"
disabled=""
rows="4"
/>
</div>
</div>
</div>
</div>
<div
class="ant-row ant-form-item"
>
<div
class="ant-col ant-col-4 ant-form-item-label"
>
<label
class=""
title="Switch"
>
Switch
</label>
</div>
<div
class="ant-col ant-col-14 ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<button
aria-checked="false"
class="ant-switch ant-switch-disabled"
disabled=""
role="switch"
type="button"
>
<div
class="ant-switch-handle"
/>
<span
class="ant-switch-inner"
/>
</button>
</div>
</div>
</div>
</div>
<div
class="ant-row ant-form-item"
>
<div
class="ant-col ant-col-4 ant-form-item-label"
>
<label
class=""
title="Button"
>
Button
</label>
</div>
<div
class="ant-col ant-col-14 ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<button
class="ant-btn ant-btn-default"
disabled=""
type="button"
>
<span>
Button
</span>
</button>
</div>
</div>
</div>
</div>
</form>
`;
exports[`renders ./components/form/demo/disabled-input-debug.md correctly 1`] = ` exports[`renders ./components/form/demo/disabled-input-debug.md correctly 1`] = `
<form <form
class="ant-form ant-form-horizontal" class="ant-form ant-form-horizontal"

File diff suppressed because it is too large Load Diff

View File

@ -9,12 +9,23 @@ import Input from '../../input';
import Button from '../../button'; import Button from '../../button';
import Select from '../../select'; import Select from '../../select';
import Checkbox from '../../checkbox';
import Radio from '../../radio';
import TreeSelect from '../../tree-select';
import Cascader from '../../cascader';
import DatePicker from '../../date-picker';
import InputNumber from '../../input-number';
import Switch from '../../switch';
import mountTest from '../../../tests/shared/mountTest'; import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest'; import rtlTest from '../../../tests/shared/rtlTest';
import { sleep, render, fireEvent } from '../../../tests/utils'; import { sleep, render, fireEvent } from '../../../tests/utils';
import ConfigProvider from '../../config-provider'; import ConfigProvider from '../../config-provider';
import zhCN from '../../locale/zh_CN'; import zhCN from '../../locale/zh_CN';
const { RangePicker } = DatePicker;
const { TextArea } = Input;
jest.mock('scroll-into-view-if-needed'); jest.mock('scroll-into-view-if-needed');
describe('Form', () => { describe('Form', () => {
@ -872,6 +883,96 @@ describe('Form', () => {
expect(wrapper.find('form').hasClass('ant-form-hide-required-mark')).toBeTruthy(); expect(wrapper.find('form').hasClass('ant-form-hide-required-mark')).toBeTruthy();
}); });
it('form should support disabled', () => {
const App = () => {
const [componentDisabled, setComponentDisabled] = React.useState(false);
const onFormLayoutChange = ({ disabled }) => {
setComponentDisabled(disabled);
};
return (
<Form
labelCol={{ span: 4 }}
wrapperCol={{ span: 14 }}
layout="horizontal"
initialValues={{ disabled: componentDisabled }}
onValuesChange={onFormLayoutChange}
disabled={componentDisabled}
>
<Form.Item label="Form disabled" name="disabled" valuePropName="checked">
<Checkbox>disabled</Checkbox>
</Form.Item>
<Form.Item label="Radio">
<Radio.Group>
<Radio value="apple"> Apple </Radio>
<Radio value="pear"> Pear </Radio>
</Radio.Group>
</Form.Item>
<Form.Item label="Input">
<Input />
</Form.Item>
<Form.Item label="Select">
<Select>
<Select.Option value="demo">Demo</Select.Option>
</Select>
</Form.Item>
<Form.Item label="TreeSelect">
<TreeSelect
treeData={[
{
title: 'Light',
value: 'light',
children: [{ title: 'Bamboo', value: 'bamboo' }],
},
]}
/>
</Form.Item>
<Form.Item label="Cascader">
<Cascader
options={[
{
value: 'zhejiang',
label: 'Zhejiang',
children: [
{
value: 'hangzhou',
label: 'Hangzhou',
},
],
},
]}
/>
</Form.Item>
<Form.Item label="DatePicker">
<DatePicker />
</Form.Item>
<Form.Item label="RangePicker">
<RangePicker />
</Form.Item>
<Form.Item label="InputNumber">
<InputNumber />
</Form.Item>
<Form.Item label="TextArea">
<TextArea rows={4} />
</Form.Item>
<Form.Item label="Switch" valuePropName="checked">
<Switch />
</Form.Item>
<Form.Item label="Button">
<Button>Button</Button>
</Form.Item>
</Form>
);
};
const wrapper = mount(<App />);
expect(wrapper.render()).toMatchSnapshot();
act(() => {
wrapper.find('.ant-checkbox-input').at(0).simulate('change');
});
expect(wrapper.render()).toMatchSnapshot();
});
it('_internalItemRender api test', () => { it('_internalItemRender api test', () => {
const wrapper = mount( const wrapper = mount(
<Form> <Form>

View File

@ -0,0 +1,113 @@
---
order: 3.1
title:
zh-CN: 表单禁用
en-US: Form disabled
---
## zh-CN
设置表单组件禁用,仅对 antd 组件有效。
## en-US
Set component disabled, only works for antd components.
```tsx
import React, { useState } from 'react';
import {
Form,
Input,
Button,
Radio,
Select,
Cascader,
DatePicker,
InputNumber,
TreeSelect,
Switch,
Checkbox,
} from 'antd';
const { RangePicker } = DatePicker;
const { TextArea } = Input;
const FormDisabledDemo = () => {
const [componentDisabled, setComponentDisabled] = useState<boolean>(true);
const onFormLayoutChange = ({ disabled }: { disabled: boolean }) => {
setComponentDisabled(disabled);
};
return (
<Form
labelCol={{ span: 4 }}
wrapperCol={{ span: 14 }}
layout="horizontal"
initialValues={{ disabled: componentDisabled }}
onValuesChange={onFormLayoutChange}
disabled={componentDisabled}
>
<Form.Item label="Form disabled" name="disabled" valuePropName="checked">
<Checkbox>disabled</Checkbox>
</Form.Item>
<Form.Item label="Radio">
<Radio.Group>
<Radio value="apple"> Apple </Radio>
<Radio value="pear"> Pear </Radio>
</Radio.Group>
</Form.Item>
<Form.Item label="Input">
<Input />
</Form.Item>
<Form.Item label="Select">
<Select>
<Select.Option value="demo">Demo</Select.Option>
</Select>
</Form.Item>
<Form.Item label="TreeSelect">
<TreeSelect
treeData={[
{ title: 'Light', value: 'light', children: [{ title: 'Bamboo', value: 'bamboo' }] },
]}
/>
</Form.Item>
<Form.Item label="Cascader">
<Cascader
options={[
{
value: 'zhejiang',
label: 'Zhejiang',
children: [
{
value: 'hangzhou',
label: 'Hangzhou',
},
],
},
]}
/>
</Form.Item>
<Form.Item label="DatePicker">
<DatePicker />
</Form.Item>
<Form.Item label="RangePicker">
<RangePicker />
</Form.Item>
<Form.Item label="InputNumber">
<InputNumber />
</Form.Item>
<Form.Item label="TextArea">
<TextArea rows={4} />
</Form.Item>
<Form.Item label="Switch" valuePropName="checked">
<Switch />
</Form.Item>
<Form.Item label="Button">
<Button>Button</Button>
</Form.Item>
</Form>
);
};
export default () => <FormDisabledDemo />;
```

View File

@ -8,6 +8,7 @@ import { useContext } from 'react';
import { ConfigContext } from '../config-provider'; import { ConfigContext } from '../config-provider';
import type { SizeType } from '../config-provider/SizeContext'; import type { SizeType } from '../config-provider/SizeContext';
import SizeContext from '../config-provider/SizeContext'; import SizeContext from '../config-provider/SizeContext';
import DisabledContext from '../config-provider/DisabledContext';
import { FormItemInputContext, NoFormStatus } from '../form/context'; import { FormItemInputContext, NoFormStatus } from '../form/context';
import { cloneElement } from '../_util/reactNode'; import { cloneElement } from '../_util/reactNode';
import useStyle from './style'; import useStyle from './style';
@ -23,6 +24,7 @@ export interface InputNumberProps<T extends ValueType = ValueType>
addonAfter?: React.ReactNode; addonAfter?: React.ReactNode;
prefix?: React.ReactNode; prefix?: React.ReactNode;
size?: SizeType; size?: SizeType;
disabled?: boolean;
bordered?: boolean; bordered?: boolean;
status?: InputStatus; status?: InputStatus;
controls?: boolean | { upIcon?: React.ReactNode; downIcon?: React.ReactNode }; controls?: boolean | { upIcon?: React.ReactNode; downIcon?: React.ReactNode };
@ -39,6 +41,7 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
const { const {
className, className,
size: customizeSize, size: customizeSize,
disabled: customDisabled,
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
addonBefore, addonBefore,
addonAfter, addonAfter,
@ -83,6 +86,10 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
const mergedStatus = getMergedStatus(contextStatus, customStatus); const mergedStatus = getMergedStatus(contextStatus, customStatus);
const mergeSize = customizeSize || size; const mergeSize = customizeSize || size;
// ===================== Disabled =====================
const disabled = React.useContext(DisabledContext);
const mergedDisabled = customDisabled || disabled;
const inputNumberClass = classNames( const inputNumberClass = classNames(
{ {
[`${prefixCls}-lg`]: mergeSize === 'large', [`${prefixCls}-lg`]: mergeSize === 'large',
@ -100,6 +107,7 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
let element = ( let element = (
<RcInputNumber <RcInputNumber
ref={inputRef} ref={inputRef}
disabled={mergedDisabled}
className={inputNumberClass} className={inputNumberClass}
upHandler={upIcon} upHandler={upIcon}
downHandler={downIcon} downHandler={downIcon}
@ -179,7 +187,7 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
<div className={mergedGroupClassName} style={props.style}> <div className={mergedGroupClassName} style={props.style}>
<div className={mergedWrapperClassName}> <div className={mergedWrapperClassName}>
{addonBeforeNode && <NoFormStatus>{addonBeforeNode}</NoFormStatus>} {addonBeforeNode && <NoFormStatus>{addonBeforeNode}</NoFormStatus>}
{cloneElement(element, { style: null })} {cloneElement(element, { style: null, disabled: mergedDisabled })}
{addonAfterNode && <NoFormStatus>{addonAfterNode}</NoFormStatus>} {addonAfterNode && <NoFormStatus>{addonAfterNode}</NoFormStatus>}
</div> </div>
</div> </div>

View File

@ -7,6 +7,7 @@ import { composeRef } from 'rc-util/lib/ref';
import type { SizeType } from '../config-provider/SizeContext'; import type { SizeType } from '../config-provider/SizeContext';
import SizeContext from '../config-provider/SizeContext'; import SizeContext from '../config-provider/SizeContext';
import type { InputStatus } from '../_util/statusUtils'; import type { InputStatus } from '../_util/statusUtils';
import DisabledContext from '../config-provider/DisabledContext';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils'; import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import { ConfigContext } from '../config-provider'; import { ConfigContext } from '../config-provider';
import { FormItemInputContext, NoFormStatus } from '../form/context'; import { FormItemInputContext, NoFormStatus } from '../form/context';
@ -117,6 +118,7 @@ export interface InputProps
'wrapperClassName' | 'groupClassName' | 'inputClassName' | 'affixWrapperClassName' 'wrapperClassName' | 'groupClassName' | 'inputClassName' | 'affixWrapperClassName'
> { > {
size?: SizeType; size?: SizeType;
disabled?: boolean;
status?: InputStatus; status?: InputStatus;
bordered?: boolean; bordered?: boolean;
[key: `data-${string}`]: string; [key: `data-${string}`]: string;
@ -128,6 +130,7 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
bordered = true, bordered = true,
status: customStatus, status: customStatus,
size: customSize, size: customSize,
disabled: customDisabled,
onBlur, onBlur,
onFocus, onFocus,
suffix, suffix,
@ -148,6 +151,10 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
const size = React.useContext(SizeContext); const size = React.useContext(SizeContext);
const mergedSize = customSize || size; const mergedSize = customSize || size;
// ===================== Disabled =====================
const disabled = React.useContext(DisabledContext);
const mergedDisabled = customDisabled || disabled;
// ===================== Status ===================== // ===================== Status =====================
const { status: contextStatus, hasFeedback, feedbackIcon } = useContext(FormItemInputContext); const { status: contextStatus, hasFeedback, feedbackIcon } = useContext(FormItemInputContext);
const mergedStatus = getMergedStatus(contextStatus, customStatus); const mergedStatus = getMergedStatus(contextStatus, customStatus);
@ -218,6 +225,7 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
prefixCls={prefixCls} prefixCls={prefixCls}
autoComplete={input?.autoComplete} autoComplete={input?.autoComplete}
{...rest} {...rest}
disabled={mergedDisabled || undefined}
onBlur={handleBlur} onBlur={handleBlur}
onFocus={handleFocus} onFocus={handleFocus}
suffix={suffixNode} suffix={suffixNode}

View File

@ -8,6 +8,7 @@ import * as React from 'react';
import { ConfigContext } from '../config-provider'; import { ConfigContext } from '../config-provider';
import type { SizeType } from '../config-provider/SizeContext'; import type { SizeType } from '../config-provider/SizeContext';
import SizeContext from '../config-provider/SizeContext'; import SizeContext from '../config-provider/SizeContext';
import DisabledContext from '../config-provider/DisabledContext';
import { FormItemInputContext } from '../form/context'; import { FormItemInputContext } from '../form/context';
import type { InputStatus } from '../_util/statusUtils'; import type { InputStatus } from '../_util/statusUtils';
import { getStatusClassNames, getMergedStatus } from '../_util/statusUtils'; import { getStatusClassNames, getMergedStatus } from '../_util/statusUtils';
@ -49,6 +50,7 @@ export interface TextAreaProps extends RcTextAreaProps {
bordered?: boolean; bordered?: boolean;
showCount?: boolean | ShowCountProps; showCount?: boolean | ShowCountProps;
size?: SizeType; size?: SizeType;
disabled?: boolean;
status?: InputStatus; status?: InputStatus;
} }
@ -68,6 +70,7 @@ const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(
className, className,
style, style,
size: customizeSize, size: customizeSize,
disabled: customDisabled,
onCompositionStart, onCompositionStart,
onCompositionEnd, onCompositionEnd,
onChange, onChange,
@ -79,6 +82,10 @@ const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(
const { getPrefixCls, direction } = React.useContext(ConfigContext); const { getPrefixCls, direction } = React.useContext(ConfigContext);
const size = React.useContext(SizeContext); const size = React.useContext(SizeContext);
// ===================== Disabled =====================
const disabled = React.useContext(DisabledContext);
const mergedDisabled = customDisabled || disabled;
const { const {
status: contextStatus, status: contextStatus,
hasFeedback, hasFeedback,
@ -180,6 +187,7 @@ const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(
const textArea = ( const textArea = (
<RcTextArea <RcTextArea
{...omit(props, ['allowClear'])} {...omit(props, ['allowClear'])}
disabled={mergedDisabled}
className={classNames( className={classNames(
{ {
[`${prefixCls}-borderless`]: !bordered, [`${prefixCls}-borderless`]: !bordered,
@ -209,6 +217,7 @@ const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(
// TextArea // TextArea
const textareaNode = ( const textareaNode = (
<ClearableLabeledInput <ClearableLabeledInput
disabled={mergedDisabled}
{...props} {...props}
prefixCls={prefixCls} prefixCls={prefixCls}
direction={direction} direction={direction}

View File

@ -29,8 +29,8 @@ const Circle: React.FC<CircleProps> = props => {
prefixCls, prefixCls,
width, width,
strokeWidth, strokeWidth,
trailColor, trailColor = null as any,
strokeLinecap, strokeLinecap = 'round',
gapPosition, gapPosition,
gapDegree, gapDegree,
type, type,

View File

@ -70,9 +70,9 @@ const Line: React.FC<LineProps> = props => {
strokeWidth, strokeWidth,
size, size,
strokeColor, strokeColor,
strokeLinecap, strokeLinecap = 'round',
children, children,
trailColor, trailColor = null,
success, success,
} = props; } = props;

View File

@ -16,7 +16,7 @@ const Steps: React.FC<StepsProps> = props => {
percent = 0, percent = 0,
strokeWidth = 8, strokeWidth = 8,
strokeColor, strokeColor,
trailColor, trailColor = null as any,
prefixCls, prefixCls,
children, children,
} = props; } = props;

View File

@ -5,7 +5,7 @@ import CloseOutlined from '@ant-design/icons/CloseOutlined';
import CheckOutlined from '@ant-design/icons/CheckOutlined'; import CheckOutlined from '@ant-design/icons/CheckOutlined';
import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled'; import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled'; import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; import { ConfigContext } from '../config-provider';
import { tuple } from '../_util/type'; import { tuple } from '../_util/type';
import devWarning from '../_util/devWarning'; import devWarning from '../_util/devWarning';
import Line from './Line'; import Line from './Line';
@ -52,38 +52,28 @@ export interface ProgressProps {
children?: React.ReactNode; children?: React.ReactNode;
} }
export default class Progress extends React.Component<ProgressProps> { const Progress: React.FC<ProgressProps> = (props: ProgressProps) => {
static defaultProps = { const { percent = 0, size = 'default', showInfo = true, type = 'line' } = props;
type: 'line' as ProgressProps['type'],
percent: 0,
showInfo: true,
// null for different theme definition
trailColor: null,
size: 'default' as ProgressProps['size'],
gapDegree: undefined,
strokeLinecap: 'round' as ProgressProps['strokeLinecap'],
};
getPercentNumber() { function getPercentNumber() {
const { percent = 0 } = this.props; const successPercent = getSuccessPercent(props);
const successPercent = getSuccessPercent(this.props);
return parseInt( return parseInt(
successPercent !== undefined ? successPercent.toString() : percent.toString(), successPercent !== undefined ? successPercent.toString() : percent.toString(),
10, 10,
); );
} }
getProgressStatus() { function getProgressStatus() {
const { status } = this.props; const { status } = props;
if (ProgressStatuses.indexOf(status!) < 0 && this.getPercentNumber() >= 100) { if (ProgressStatuses.indexOf(status!) < 0 && getPercentNumber() >= 100) {
return 'success'; return 'success';
} }
return status || 'normal'; return status || 'normal';
} }
renderProcessInfo(prefixCls: string, progressStatus: typeof ProgressStatuses[number]) { function renderProcessInfo(prefixCls: string, progressStatus: typeof ProgressStatuses[number]) {
const { showInfo, format, type, percent } = this.props; const { format } = props;
const successPercent = getSuccessPercent(this.props); const successPercent = getSuccessPercent(props);
if (!showInfo) { if (!showInfo) {
return null; return null;
} }
@ -104,88 +94,77 @@ export default class Progress extends React.Component<ProgressProps> {
); );
} }
renderProgress = ({ getPrefixCls, direction }: ConfigConsumerProps) => { const { getPrefixCls, direction } = React.useContext(ConfigContext);
const { props } = this;
const {
prefixCls: customizePrefixCls,
className,
size,
type,
steps,
showInfo,
strokeColor,
...restProps
} = props;
const prefixCls = getPrefixCls('progress', customizePrefixCls);
const progressStatus = this.getProgressStatus();
const progressInfo = this.renderProcessInfo(prefixCls, progressStatus);
devWarning( const { prefixCls: customizePrefixCls, className, steps, strokeColor, ...restProps } = props;
!('successPercent' in props), const prefixCls = getPrefixCls('progress', customizePrefixCls);
'Progress', const progressStatus = getProgressStatus();
'`successPercent` is deprecated. Please use `success.percent` instead.', const progressInfo = renderProcessInfo(prefixCls, progressStatus);
);
let progress; devWarning(
// Render progress shape !('successPercent' in props),
if (type === 'line') { 'Progress',
progress = steps ? ( '`successPercent` is deprecated. Please use `success.percent` instead.',
<Steps );
{...this.props}
strokeColor={typeof strokeColor === 'string' ? strokeColor : undefined}
prefixCls={prefixCls}
steps={steps}
>
{progressInfo}
</Steps>
) : (
<Line {...this.props} prefixCls={prefixCls} direction={direction}>
{progressInfo}
</Line>
);
} else if (type === 'circle' || type === 'dashboard') {
progress = (
<Circle {...this.props} prefixCls={prefixCls} progressStatus={progressStatus}>
{progressInfo}
</Circle>
);
}
const classString = classNames( let progress;
prefixCls, // Render progress shape
{ if (type === 'line') {
[`${prefixCls}-${(type === 'dashboard' && 'circle') || (steps && 'steps') || type}`]: true, progress = steps ? (
[`${prefixCls}-status-${progressStatus}`]: true, <Steps
[`${prefixCls}-show-info`]: showInfo, {...props}
[`${prefixCls}-${size}`]: size, strokeColor={typeof strokeColor === 'string' ? strokeColor : undefined}
[`${prefixCls}-rtl`]: direction === 'rtl', prefixCls={prefixCls}
}, steps={steps}
className,
);
return (
<div
{...omit(restProps, [
'status',
'format',
'trailColor',
'strokeWidth',
'width',
'gapDegree',
'gapPosition',
'strokeLinecap',
'percent',
'success',
'successPercent',
])}
className={classString}
> >
{progress} {progressInfo}
</div> </Steps>
) : (
<Line {...props} prefixCls={prefixCls} direction={direction}>
{progressInfo}
</Line>
);
} else if (type === 'circle' || type === 'dashboard') {
progress = (
<Circle {...props} prefixCls={prefixCls} progressStatus={progressStatus}>
{progressInfo}
</Circle>
); );
};
render() {
return <ConfigConsumer>{this.renderProgress}</ConfigConsumer>;
} }
}
const classString = classNames(
prefixCls,
{
[`${prefixCls}-${(type === 'dashboard' && 'circle') || (steps && 'steps') || type}`]: true,
[`${prefixCls}-status-${progressStatus}`]: true,
[`${prefixCls}-show-info`]: showInfo,
[`${prefixCls}-${size}`]: size,
[`${prefixCls}-rtl`]: direction === 'rtl',
},
className,
);
return (
<div
{...omit(restProps, [
'status',
'format',
'trailColor',
'strokeWidth',
'width',
'gapDegree',
'gapPosition',
'strokeLinecap',
'percent',
'success',
'successPercent',
'type',
])}
className={classString}
>
{progress}
</div>
);
};
export default Progress;

View File

@ -2,6 +2,7 @@ import * as React from 'react';
import { AbstractCheckboxGroupProps } from '../checkbox/Group'; import { AbstractCheckboxGroupProps } from '../checkbox/Group';
import { AbstractCheckboxProps } from '../checkbox/Checkbox'; import { AbstractCheckboxProps } from '../checkbox/Checkbox';
import { SizeType } from '../config-provider/SizeContext'; import { SizeType } from '../config-provider/SizeContext';
import { DisabledType } from '../config-provider/DisabledContext';
export type RadioGroupButtonStyle = 'outline' | 'solid'; export type RadioGroupButtonStyle = 'outline' | 'solid';
export type RadioGroupOptionType = 'default' | 'button'; export type RadioGroupOptionType = 'default' | 'button';
@ -11,6 +12,7 @@ export interface RadioGroupProps extends AbstractCheckboxGroupProps {
value?: any; value?: any;
onChange?: (e: RadioChangeEvent) => void; onChange?: (e: RadioChangeEvent) => void;
size?: SizeType; size?: SizeType;
disabled?: DisabledType;
onMouseEnter?: React.MouseEventHandler<HTMLDivElement>; onMouseEnter?: React.MouseEventHandler<HTMLDivElement>;
onMouseLeave?: React.MouseEventHandler<HTMLDivElement>; onMouseLeave?: React.MouseEventHandler<HTMLDivElement>;
name?: string; name?: string;

View File

@ -7,6 +7,7 @@ import { FormItemInputContext } from '../form/context';
import { RadioProps, RadioChangeEvent } from './interface'; import { RadioProps, RadioChangeEvent } from './interface';
import { ConfigContext } from '../config-provider'; import { ConfigContext } from '../config-provider';
import RadioGroupContext, { RadioOptionTypeContext } from './context'; import RadioGroupContext, { RadioOptionTypeContext } from './context';
import DisabledContext from '../config-provider/DisabledContext';
import devWarning from '../_util/devWarning'; import devWarning from '../_util/devWarning';
import useStyle from './style'; import useStyle from './style';
@ -29,7 +30,14 @@ const InternalRadio: React.ForwardRefRenderFunction<HTMLElement, RadioProps> = (
groupContext?.onChange?.(e); groupContext?.onChange?.(e);
}; };
const { prefixCls: customizePrefixCls, className, children, style, ...restProps } = props; const {
prefixCls: customizePrefixCls,
className,
children,
style,
disabled: customDisabled,
...restProps
} = props;
const radioPrefixCls = getPrefixCls('radio', customizePrefixCls); const radioPrefixCls = getPrefixCls('radio', customizePrefixCls);
const prefixCls = const prefixCls =
(groupContext?.optionType || radioOptionTypeContext) === 'button' (groupContext?.optionType || radioOptionTypeContext) === 'button'
@ -40,11 +48,16 @@ const InternalRadio: React.ForwardRefRenderFunction<HTMLElement, RadioProps> = (
const [wrapSSR, hashId] = useStyle(radioPrefixCls, getPrefixCls()); const [wrapSSR, hashId] = useStyle(radioPrefixCls, getPrefixCls());
const radioProps: RadioProps = { ...restProps }; const radioProps: RadioProps = { ...restProps };
// ===================== Disabled =====================
const disabled = React.useContext(DisabledContext);
radioProps.disabled = customDisabled || disabled;
if (groupContext) { if (groupContext) {
radioProps.name = groupContext.name; radioProps.name = groupContext.name;
radioProps.onChange = onChange; radioProps.onChange = onChange;
radioProps.checked = props.value === groupContext.value; radioProps.checked = props.value === groupContext.value;
radioProps.disabled = props.disabled || groupContext.disabled; radioProps.disabled = radioProps.disabled || groupContext.disabled;
} }
const wrapperClassString = classNames( const wrapperClassString = classNames(
`${prefixCls}-wrapper`, `${prefixCls}-wrapper`,

View File

@ -10,6 +10,7 @@ import { useContext } from 'react';
import { ConfigContext } from '../config-provider'; import { ConfigContext } from '../config-provider';
import getIcons from './utils/iconUtil'; import getIcons from './utils/iconUtil';
import SizeContext, { SizeType } from '../config-provider/SizeContext'; import SizeContext, { SizeType } from '../config-provider/SizeContext';
import DisabledContext from '../config-provider/DisabledContext';
import { FormItemInputContext } from '../form/context'; import { FormItemInputContext } from '../form/context';
import { getMergedStatus, getStatusClassNames, InputStatus } from '../_util/statusUtils'; import { getMergedStatus, getStatusClassNames, InputStatus } from '../_util/statusUtils';
import { getTransitionName, getTransitionDirection, SelectCommonPlacement } from '../_util/motion'; import { getTransitionName, getTransitionDirection, SelectCommonPlacement } from '../_util/motion';
@ -34,6 +35,7 @@ export interface InternalSelectProps<
> extends Omit<RcSelectProps<ValueType, OptionType>, 'mode'> { > extends Omit<RcSelectProps<ValueType, OptionType>, 'mode'> {
suffixIcon?: React.ReactNode; suffixIcon?: React.ReactNode;
size?: SizeType; size?: SizeType;
disabled?: boolean;
mode?: 'multiple' | 'tags' | 'SECRET_COMBOBOX_MODE_DO_NOT_USE'; mode?: 'multiple' | 'tags' | 'SECRET_COMBOBOX_MODE_DO_NOT_USE';
bordered?: boolean; bordered?: boolean;
} }
@ -63,6 +65,7 @@ const InternalSelect = <OptionType extends BaseOptionType | DefaultOptionType =
placement, placement,
listItemHeight = 24, listItemHeight = 24,
size: customizeSize, size: customizeSize,
disabled: customDisabled,
notFoundContent, notFoundContent,
status: customStatus, status: customStatus,
showArrow, showArrow,
@ -143,6 +146,11 @@ const InternalSelect = <OptionType extends BaseOptionType | DefaultOptionType =
); );
const mergedSize = customizeSize || size; const mergedSize = customizeSize || size;
// ===================== Disabled =====================
const disabled = React.useContext(DisabledContext);
const mergedDisabled = customDisabled || disabled;
const mergedClassName = classNames( const mergedClassName = classNames(
{ {
[`${prefixCls}-lg`]: mergedSize === 'large', [`${prefixCls}-lg`]: mergedSize === 'large',
@ -192,6 +200,7 @@ const InternalSelect = <OptionType extends BaseOptionType | DefaultOptionType =
getPopupContainer={getPopupContainer || getContextPopupContainer} getPopupContainer={getPopupContainer || getContextPopupContainer}
dropdownClassName={rcSelectRtlDropdownClassName} dropdownClassName={rcSelectRtlDropdownClassName}
showArrow={hasFeedback || showArrow} showArrow={hasFeedback || showArrow}
disabled={mergedDisabled}
/>, />,
); );
}; };

View File

@ -6,6 +6,7 @@ import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
import Wave from '../_util/wave'; import Wave from '../_util/wave';
import { ConfigContext } from '../config-provider'; import { ConfigContext } from '../config-provider';
import SizeContext from '../config-provider/SizeContext'; import SizeContext from '../config-provider/SizeContext';
import DisabledContext from '../config-provider/DisabledContext';
import devWarning from '../_util/devWarning'; import devWarning from '../_util/devWarning';
import useStyle from './style'; import useStyle from './style';
@ -42,9 +43,9 @@ const Switch = React.forwardRef<unknown, SwitchProps>(
{ {
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
size: customizeSize, size: customizeSize,
disabled: customDisabled,
loading, loading,
className = '', className = '',
disabled,
...props ...props
}, },
ref, ref,
@ -57,6 +58,11 @@ const Switch = React.forwardRef<unknown, SwitchProps>(
const { getPrefixCls, direction } = React.useContext(ConfigContext); const { getPrefixCls, direction } = React.useContext(ConfigContext);
const size = React.useContext(SizeContext); const size = React.useContext(SizeContext);
// ===================== Disabled =====================
const disabled = React.useContext(DisabledContext);
const mergedDisabled = customDisabled || disabled || loading;
const prefixCls = getPrefixCls('switch', customizePrefixCls); const prefixCls = getPrefixCls('switch', customizePrefixCls);
const loadingIcon = ( const loadingIcon = (
<div className={`${prefixCls}-handle`}> <div className={`${prefixCls}-handle`}>
@ -83,7 +89,7 @@ const Switch = React.forwardRef<unknown, SwitchProps>(
{...props} {...props}
prefixCls={prefixCls} prefixCls={prefixCls}
className={classes} className={classes}
disabled={disabled || loading} disabled={mergedDisabled}
ref={ref} ref={ref}
loadingIcon={loadingIcon} loadingIcon={loadingIcon}
/> />

View File

@ -18,6 +18,7 @@ import { SwitcherIcon } from '../tree/Tree';
import getIcons from '../select/utils/iconUtil'; import getIcons from '../select/utils/iconUtil';
import renderSwitcherIcon from '../tree/utils/iconUtil'; import renderSwitcherIcon from '../tree/utils/iconUtil';
import SizeContext, { SizeType } from '../config-provider/SizeContext'; import SizeContext, { SizeType } from '../config-provider/SizeContext';
import DisabledContext from '../config-provider/DisabledContext';
import { getTransitionName, getTransitionDirection, SelectCommonPlacement } from '../_util/motion'; import { getTransitionName, getTransitionDirection, SelectCommonPlacement } from '../_util/motion';
import { FormItemInputContext } from '../form/context'; import { FormItemInputContext } from '../form/context';
import { getMergedStatus, getStatusClassNames, InputStatus } from '../_util/statusUtils'; import { getMergedStatus, getStatusClassNames, InputStatus } from '../_util/statusUtils';
@ -49,6 +50,7 @@ export interface TreeSelectProps<
> { > {
suffixIcon?: React.ReactNode; suffixIcon?: React.ReactNode;
size?: SizeType; size?: SizeType;
disabled?: boolean;
placement?: SelectCommonPlacement; placement?: SelectCommonPlacement;
bordered?: boolean; bordered?: boolean;
treeLine?: TreeProps['showLine']; treeLine?: TreeProps['showLine'];
@ -60,6 +62,7 @@ const InternalTreeSelect = <OptionType extends BaseOptionType | DefaultOptionTyp
{ {
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
size: customizeSize, size: customizeSize,
disabled: customDisabled,
bordered = true, bordered = true,
className, className,
treeCheckable, treeCheckable,
@ -164,6 +167,10 @@ const InternalTreeSelect = <OptionType extends BaseOptionType | DefaultOptionTyp
}; };
const mergedSize = customizeSize || size; const mergedSize = customizeSize || size;
// ===================== Disabled =====================
const disabled = React.useContext(DisabledContext);
const mergedDisabled = customDisabled || disabled;
const mergedClassName = classNames( const mergedClassName = classNames(
!customizePrefixCls && treeSelectPrefixCls, !customizePrefixCls && treeSelectPrefixCls,
{ {
@ -182,6 +189,7 @@ const InternalTreeSelect = <OptionType extends BaseOptionType | DefaultOptionTyp
<RcTreeSelect <RcTreeSelect
virtual={virtual} virtual={virtual}
dropdownMatchSelectWidth={dropdownMatchSelectWidth} dropdownMatchSelectWidth={dropdownMatchSelectWidth}
disabled={mergedDisabled}
{...selectProps} {...selectProps}
ref={ref as any} ref={ref as any}
prefixCls={prefixCls} prefixCls={prefixCls}

View File

@ -30,6 +30,7 @@ interface CopyConfig {
onCopy?: (event?: React.MouseEvent<HTMLDivElement>) => void; onCopy?: (event?: React.MouseEvent<HTMLDivElement>) => void;
icon?: React.ReactNode; icon?: React.ReactNode;
tooltips?: boolean | React.ReactNode; tooltips?: boolean | React.ReactNode;
format?: 'text/plain' | 'text/html';
} }
interface EditConfig { interface EditConfig {
@ -189,6 +190,11 @@ const Base = React.forwardRef((props: InternalBlockProps, ref: any) => {
const [copied, setCopied] = React.useState(false); const [copied, setCopied] = React.useState(false);
const copyIdRef = React.useRef<NodeJS.Timeout>(); const copyIdRef = React.useRef<NodeJS.Timeout>();
const copyOptions: Pick<CopyConfig, 'format'> = {};
if (copyConfig.format) {
copyOptions.format = copyConfig.format;
}
const cleanCopyId = () => { const cleanCopyId = () => {
clearTimeout(copyIdRef.current!); clearTimeout(copyIdRef.current!);
}; };
@ -197,7 +203,7 @@ const Base = React.forwardRef((props: InternalBlockProps, ref: any) => {
e?.preventDefault(); e?.preventDefault();
e?.stopPropagation(); e?.stopPropagation();
copy(copyConfig.text || String(children) || ''); copy(copyConfig.text || String(children) || '', copyOptions);
setCopied(true); setCopied(true);

View File

@ -89,12 +89,12 @@ describe('Typography', () => {
describe('Base', () => { describe('Base', () => {
describe('copyable', () => { describe('copyable', () => {
function copyTest(name, text, target, icon, tooltips) { function copyTest(name, text, target, icon, tooltips, format) {
it(name, async () => { it(name, async () => {
jest.useFakeTimers(); jest.useFakeTimers();
const onCopy = jest.fn(); const onCopy = jest.fn();
const wrapper = mount( const wrapper = mount(
<Base component="p" copyable={{ text, onCopy, icon, tooltips }}> <Base component="p" copyable={{ text, onCopy, icon, tooltips, format }}>
test copy test copy
</Base>, </Base>,
); );
@ -132,6 +132,7 @@ describe('Typography', () => {
} }
expect(copy.lastStr).toEqual(target); expect(copy.lastStr).toEqual(target);
expect(copy.lastOptions.format).toEqual(format);
wrapper.update(); wrapper.update();
expect(onCopy).toHaveBeenCalled(); expect(onCopy).toHaveBeenCalled();
@ -173,6 +174,22 @@ describe('Typography', () => {
copyTest('basic copy', undefined, 'test copy'); copyTest('basic copy', undefined, 'test copy');
copyTest('customize copy', 'bamboo', 'bamboo'); copyTest('customize copy', 'bamboo', 'bamboo');
copyTest(
'costomize copy with plain text',
'bamboo',
'bamboo',
undefined,
undefined,
'text/plain',
);
copyTest(
'costomize copy with html text',
'bamboo',
'bamboo',
undefined,
undefined,
'text/html',
);
copyTest('customize copy icon', 'bamboo', 'bamboo', <SmileOutlined />); copyTest('customize copy icon', 'bamboo', 'bamboo', <SmileOutlined />);
copyTest('customize copy icon by pass array', 'bamboo', 'bamboo', [ copyTest('customize copy icon by pass array', 'bamboo', 'bamboo', [
<SmileOutlined key="copy-icon" />, <SmileOutlined key="copy-icon" />,

View File

@ -74,6 +74,7 @@ Basic text writing, including headings, body text, lists, and more.
onCopy: function(event), onCopy: function(event),
icon: ReactNode, icon: ReactNode,
tooltips: false | [ReactNode, ReactNode], tooltips: false | [ReactNode, ReactNode],
format: 'text/plain' | 'text/html',
} }
| Property | Description | Type | Default | Version | | Property | Description | Type | Default | Version |
@ -81,6 +82,7 @@ Basic text writing, including headings, body text, lists, and more.
| icon | Custom copy icon: \[copyIcon, copiedIcon] | \[ReactNode, ReactNode] | - | 4.6.0 | | icon | Custom copy icon: \[copyIcon, copiedIcon] | \[ReactNode, ReactNode] | - | 4.6.0 |
| text | The text to copy | string | - | | | text | The text to copy | string | - | |
| tooltips | Custom tooltip text, hide when it is false | \[ReactNode, ReactNode] | \[`Copy`, `Copied`] | 4.4.0 | | tooltips | Custom tooltip text, hide when it is false | \[ReactNode, ReactNode] | \[`Copy`, `Copied`] | 4.4.0 |
| format | The Mime Type of the text | 'text/plain' \| 'text/html' | - | |
| onCopy | Called when copied text | function | - | | | onCopy | Called when copied text | function | - | |
### editable ### editable

View File

@ -75,6 +75,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/GOM1KQ24O/Typography.svg
onCopy: function(event), onCopy: function(event),
icon: ReactNode, icon: ReactNode,
tooltips: false | [ReactNode, ReactNode], tooltips: false | [ReactNode, ReactNode],
format: 'text/plain' | 'text/html',
} }
| 参数 | 说明 | 类型 | 默认值 | 版本 | | 参数 | 说明 | 类型 | 默认值 | 版本 |
@ -82,6 +83,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/GOM1KQ24O/Typography.svg
| icon | 自定义拷贝图标:\[默认图标, 拷贝后的图标] | \[ReactNode, ReactNode] | - | 4.6.0 | | icon | 自定义拷贝图标:\[默认图标, 拷贝后的图标] | \[ReactNode, ReactNode] | - | 4.6.0 |
| text | 拷贝到剪切板里的文本 | string | - | | | text | 拷贝到剪切板里的文本 | string | - | |
| tooltips | 自定义提示文案,为 false 时隐藏文案 | \[ReactNode, ReactNode] | \[`复制`, `复制成功`] | 4.4.0 | | tooltips | 自定义提示文案,为 false 时隐藏文案 | \[ReactNode, ReactNode] | \[`复制`, `复制成功`] | 4.4.0 |
| format | 剪切板数据的 Mime Type | 'text/plain' \| 'text/html' | - | |
| onCopy | 拷贝成功的回调函数 | function | - | | | onCopy | 拷贝成功的回调函数 | function | - | |
### editable ### editable

View File

@ -130,7 +130,7 @@
"rc-collapse": "~3.1.0", "rc-collapse": "~3.1.0",
"rc-dialog": "~8.8.1", "rc-dialog": "~8.8.1",
"rc-drawer": "~4.4.2", "rc-drawer": "~4.4.2",
"rc-dropdown": "~3.5.0", "rc-dropdown": "~3.6.0",
"rc-field-form": "~1.26.1", "rc-field-form": "~1.26.1",
"rc-image": "~5.6.0", "rc-image": "~5.6.0",
"rc-input": "~0.0.1-alpha.5", "rc-input": "~0.0.1-alpha.5",
@ -150,7 +150,7 @@
"rc-steps": "~4.1.0", "rc-steps": "~4.1.0",
"rc-switch": "~3.2.0", "rc-switch": "~3.2.0",
"rc-table": "~7.24.0", "rc-table": "~7.24.0",
"rc-tabs": "~11.13.0", "rc-tabs": "~11.14.0",
"rc-textarea": "~0.3.0", "rc-textarea": "~0.3.0",
"rc-tooltip": "~5.1.1", "rc-tooltip": "~5.1.1",
"rc-tree": "~5.5.0", "rc-tree": "~5.5.0",

View File

@ -1,5 +1,6 @@
function copy(str) { function copy(str, options = {}) {
copy.lastStr = str; copy.lastStr = str;
copy.lastOptions = options;
} }
export default copy; export default copy;