chore: next merge feature

This commit is contained in:
zombiej 2022-07-19 17:51:35 +08:00
commit c71ce67e14
57 changed files with 44474 additions and 37145 deletions

View File

@ -15,6 +15,20 @@ timeline: true
--- ---
## 4.21.7
`2022-07-18`
- 🆕 Add Skeleton.Node sub-component, allow user customize content of Skeleton. [#36441](https://github.com/ant-design/ant-design/pull/36441) [@DawnLck](https://github.com/DawnLck)
- Form
- 🐞 Fix Form with percentage width Select may leaves unexpected height. [#36484](https://github.com/ant-design/ant-design/pull/36484)
- 🐞 Fix Form disabled but Upload component still uploadable in it. [#36573](https://github.com/ant-design/ant-design/pull/36573) [@cwjTerrace](https://github.com/cwjTerrace)
- 🐞 Fix Tree non-draggable element still render the draggable icon. [#36511](https://github.com/ant-design/ant-design/pull/36511) [@Wxh16144](https://github.com/Wxh16144)
- 🐞 Fix Upload preview error on SVG with `<foreignObject>` and cross-origin links. [#36402](https://github.com/ant-design/ant-design/pull/36402) [@jonioni](https://github.com/jonioni)
- 🐞 Fix that Tooltip cannot close automaticly on disabled Radio. [#36483](https://github.com/ant-design/ant-design/pull/36483)
- 💄 Add Modal Less variable `@modal-border-radius`. [#36527](https://github.com/ant-design/ant-design/pull/36527) [@kkkisme](https://github.com/kkkisme)
- 💄 Fix Table expand icon wrap style when column is fixed and ellipsis. [#36496](https://github.com/ant-design/ant-design/pull/36496)
## 4.21.6 ## 4.21.6
`2022-07-11` `2022-07-11`

View File

@ -15,6 +15,20 @@ timeline: true
--- ---
## 4.21.7
`2022-07-18`
- 🆕 新增 Skeleton.Node 子组件,允许用户自定义 Skeleton 的内容。[#36441](https://github.com/ant-design/ant-design/pull/36441) [@DawnLck](https://github.com/DawnLck)
- Form
- 🐞 修复 Form 下设置 Select 百分比宽度时会有未预期空行问题。[#36484](https://github.com/ant-design/ant-design/pull/36484)
- 🐞 修复 Form 设为禁用时、内部的 Upload 组件仍能上传文件的问题。[#36573](https://github.com/ant-design/ant-design/pull/36573) [@cwjTerrace](https://github.com/cwjTerrace)
- 🐞 修复 Tree 非 `draggable` 依然展示可拖拽图标的问题。[#36511](https://github.com/ant-design/ant-design/pull/36511) [@Wxh16144](https://github.com/Wxh16144)
- 🐞 修复 Upload 在预览包含 `<foreignObject>` 及跨域链接的 SVG 文件时发生错误的问题。[#36402](https://github.com/ant-design/ant-design/pull/36402) [@jonioni](https://github.com/jonioni)
- 🐞 修复 Tooltip 在被禁用的 Radio 上时无法自动关闭的问题。[#36483](https://github.com/ant-design/ant-design/pull/36483)
- 💄 添加 Modal Less 变量 `@modal-border-radius`。[#36527](https://github.com/ant-design/ant-design/pull/36527) [@kkkisme](https://github.com/kkkisme)
- 💄 修复 Table 开启固定和省略功能的列内的展开按钮换行样式。[#36496](https://github.com/ant-design/ant-design/pull/36496)
## 4.21.6 ## 4.21.6
`2022-07-11` `2022-07-11`

View File

@ -521,7 +521,10 @@ exports[`renders ./components/auto-complete/demo/form-debug.md extend context co
style="margin:0 auto" style="margin:0 auto"
> >
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8" class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8"
@ -587,8 +590,12 @@ exports[`renders ./components/auto-complete/demo/form-debug.md extend context co
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8" class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8"
@ -727,8 +734,12 @@ exports[`renders ./components/auto-complete/demo/form-debug.md extend context co
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8" class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8"
@ -913,8 +924,12 @@ exports[`renders ./components/auto-complete/demo/form-debug.md extend context co
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8" class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8"
@ -1007,8 +1022,12 @@ exports[`renders ./components/auto-complete/demo/form-debug.md extend context co
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8" class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8"
@ -1220,8 +1239,12 @@ exports[`renders ./components/auto-complete/demo/form-debug.md extend context co
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8" class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8"
@ -1442,8 +1465,12 @@ exports[`renders ./components/auto-complete/demo/form-debug.md extend context co
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8" class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8"
@ -1691,6 +1718,7 @@ exports[`renders ./components/auto-complete/demo/form-debug.md extend context co
</div> </div>
</div> </div>
</div> </div>
</div>
</form> </form>
`; `;

View File

@ -173,7 +173,10 @@ exports[`renders ./components/auto-complete/demo/form-debug.md correctly 1`] = `
style="margin:0 auto" style="margin:0 auto"
> >
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8" class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8"
@ -225,8 +228,12 @@ exports[`renders ./components/auto-complete/demo/form-debug.md correctly 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8" class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8"
@ -306,8 +313,12 @@ exports[`renders ./components/auto-complete/demo/form-debug.md correctly 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8" class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8"
@ -419,8 +430,12 @@ exports[`renders ./components/auto-complete/demo/form-debug.md correctly 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8" class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8"
@ -499,8 +514,12 @@ exports[`renders ./components/auto-complete/demo/form-debug.md correctly 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8" class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8"
@ -639,8 +658,12 @@ exports[`renders ./components/auto-complete/demo/form-debug.md correctly 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8" class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8"
@ -788,8 +811,12 @@ exports[`renders ./components/auto-complete/demo/form-debug.md correctly 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8" class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8"
@ -964,6 +991,7 @@ exports[`renders ./components/auto-complete/demo/form-debug.md correctly 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
</form> </form>
`; `;

View File

@ -0,0 +1,3 @@
import siLK from '../../date-picker/locale/si_LK';
export default siLK;

View File

@ -15381,7 +15381,10 @@ exports[`ConfigProvider components Form configProvider 1`] = `
class="config-form config-form-horizontal" class="config-form config-form-horizontal"
> >
<div <div
class="config-row config-form-item config-form-item-with-help config-form-item-has-error" class="config-form-item config-form-item-with-help config-form-item-has-error"
>
<div
class="config-row config-form-item-row"
> >
<div <div
class="config-col config-form-item-control" class="config-col config-form-item-control"
@ -15399,15 +15402,6 @@ exports[`ConfigProvider components Form configProvider 1`] = `
/> />
</div> </div>
</div> </div>
<div
class="config-form-item-explain config-form-item-explain-connected"
>
<div
class="config-form-item-explain-error"
role="alert"
>
Bamboo is Light
</div>
</div> </div>
</div> </div>
</div> </div>
@ -15419,7 +15413,10 @@ exports[`ConfigProvider components Form configProvider componentDisabled 1`] = `
class="config-form config-form-horizontal" class="config-form config-form-horizontal"
> >
<div <div
class="config-row config-form-item config-form-item-with-help config-form-item-has-error" class="config-form-item config-form-item-with-help config-form-item-has-error"
>
<div
class="config-row config-form-item-row"
> >
<div <div
class="config-col config-form-item-control" class="config-col config-form-item-control"
@ -15438,15 +15435,6 @@ exports[`ConfigProvider components Form configProvider componentDisabled 1`] = `
/> />
</div> </div>
</div> </div>
<div
class="config-form-item-explain config-form-item-explain-connected"
>
<div
class="config-form-item-explain-error"
role="alert"
>
Bamboo is Light
</div>
</div> </div>
</div> </div>
</div> </div>
@ -15458,7 +15446,10 @@ exports[`ConfigProvider components Form configProvider componentSize large 1`] =
class="config-form config-form-horizontal config-form-large" class="config-form config-form-horizontal config-form-large"
> >
<div <div
class="config-row config-form-item config-form-item-with-help config-form-item-has-error" class="config-form-item config-form-item-with-help config-form-item-has-error"
>
<div
class="config-row config-form-item-row"
> >
<div <div
class="config-col config-form-item-control" class="config-col config-form-item-control"
@ -15476,15 +15467,6 @@ exports[`ConfigProvider components Form configProvider componentSize large 1`] =
/> />
</div> </div>
</div> </div>
<div
class="config-form-item-explain config-form-item-explain-connected"
>
<div
class="config-form-item-explain-error"
role="alert"
>
Bamboo is Light
</div>
</div> </div>
</div> </div>
</div> </div>
@ -15496,7 +15478,10 @@ exports[`ConfigProvider components Form configProvider componentSize middle 1`]
class="config-form config-form-horizontal config-form-middle" class="config-form config-form-horizontal config-form-middle"
> >
<div <div
class="config-row config-form-item config-form-item-with-help config-form-item-has-error" class="config-form-item config-form-item-with-help config-form-item-has-error"
>
<div
class="config-row config-form-item-row"
> >
<div <div
class="config-col config-form-item-control" class="config-col config-form-item-control"
@ -15514,15 +15499,6 @@ exports[`ConfigProvider components Form configProvider componentSize middle 1`]
/> />
</div> </div>
</div> </div>
<div
class="config-form-item-explain config-form-item-explain-connected"
>
<div
class="config-form-item-explain-error"
role="alert"
>
Bamboo is Light
</div>
</div> </div>
</div> </div>
</div> </div>
@ -15534,7 +15510,10 @@ exports[`ConfigProvider components Form configProvider virtual and dropdownMatch
class="ant-form ant-form-horizontal" class="ant-form ant-form-horizontal"
> >
<div <div
class="ant-row ant-form-item ant-form-item-with-help ant-form-item-has-error" class="ant-form-item ant-form-item-with-help ant-form-item-has-error"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-control" class="ant-col ant-form-item-control"
@ -15552,15 +15531,6 @@ exports[`ConfigProvider components Form configProvider virtual and dropdownMatch
/> />
</div> </div>
</div> </div>
<div
class="ant-form-item-explain ant-form-item-explain-connected"
>
<div
class="ant-form-item-explain-error"
role="alert"
>
Bamboo is Light
</div>
</div> </div>
</div> </div>
</div> </div>
@ -15572,7 +15542,10 @@ exports[`ConfigProvider components Form normal 1`] = `
class="ant-form ant-form-horizontal" class="ant-form ant-form-horizontal"
> >
<div <div
class="ant-row ant-form-item ant-form-item-with-help ant-form-item-has-error" class="ant-form-item ant-form-item-with-help ant-form-item-has-error"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-control" class="ant-col ant-form-item-control"
@ -15590,15 +15563,6 @@ exports[`ConfigProvider components Form normal 1`] = `
/> />
</div> </div>
</div> </div>
<div
class="ant-form-item-explain ant-form-item-explain-connected"
>
<div
class="ant-form-item-explain-error"
role="alert"
>
Bamboo is Light
</div>
</div> </div>
</div> </div>
</div> </div>
@ -15610,7 +15574,10 @@ exports[`ConfigProvider components Form prefixCls 1`] = `
class="prefix-Form prefix-Form-horizontal" class="prefix-Form prefix-Form-horizontal"
> >
<div <div
class="ant-row prefix-Form-item prefix-Form-item-with-help prefix-Form-item-has-error" class="prefix-Form-item prefix-Form-item-with-help prefix-Form-item-has-error"
>
<div
class="ant-row prefix-Form-item-row"
> >
<div <div
class="ant-col prefix-Form-item-control" class="ant-col prefix-Form-item-control"
@ -15628,15 +15595,6 @@ exports[`ConfigProvider components Form prefixCls 1`] = `
/> />
</div> </div>
</div> </div>
<div
class="prefix-Form-item-explain prefix-Form-item-explain-connected"
>
<div
class="prefix-Form-item-explain-error"
role="alert"
>
Bamboo is Light
</div>
</div> </div>
</div> </div>
</div> </div>
@ -41877,12 +41835,11 @@ exports[`ConfigProvider components Upload configProvider componentDisabled 1`] =
class="config-upload-wrapper" class="config-upload-wrapper"
> >
<div <div
class="config-upload config-upload-select" class="config-upload config-upload-select config-upload-disabled"
> >
<span <span
class="config-upload" class="config-upload config-upload-disabled"
role="button" role="button"
tabindex="0"
> >
<input <input
accept="" accept=""
@ -41932,35 +41889,7 @@ exports[`ConfigProvider components Upload configProvider componentDisabled 1`] =
</span> </span>
<span <span
class="config-upload-list-item-actions" class="config-upload-list-item-actions"
>
<button
class="config-btn config-btn-text config-btn-sm config-btn-icon-only config-upload-list-item-action"
disabled=""
title="Remove file"
type="button"
>
<span
aria-label="delete"
class="anticon anticon-delete"
role="img"
tabindex="-1"
>
<svg
aria-hidden="true"
data-icon="delete"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"
/> />
</svg>
</span>
</button>
</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -5,7 +5,10 @@ exports[`ConfigProvider.Form form requiredMark set requiredMark optional 1`] = `
class="ant-form ant-form-horizontal" class="ant-form ant-form-horizontal"
> >
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -41,5 +44,6 @@ exports[`ConfigProvider.Form form requiredMark set requiredMark optional 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
</form> </form>
`; `;

View File

@ -0,0 +1,28 @@
import CalendarLocale from 'rc-picker/lib/locale/si_LK';
import TimePickerLocale from '../../time-picker/locale/si_LK';
import type { PickerLocale } from '../generatePicker';
// Merge into a locale object
const locale: PickerLocale = {
lang: {
placeholder: 'දිනය තෝරන්න',
yearPlaceholder: 'අවුරුද්ද තෝරන්න',
quarterPlaceholder: 'කාර්තුව තෝරන්න',
monthPlaceholder: 'මාසය තෝරන්න',
weekPlaceholder: 'සතිය තෝරන්න',
rangePlaceholder: ['ආරම්භක දිනය', 'නිමවන දිනය'],
rangeYearPlaceholder: ['ආර්ම්භක අවුරුද්ද', 'නිමවන අවුරුද්ද'],
rangeQuarterPlaceholder: ['ආරම්භක කාර්තුව', 'නිමවන කාර්තුව'],
rangeMonthPlaceholder: ['ආරම්භක මාසය', 'නිමවන මාසය'],
rangeWeekPlaceholder: ['ආරම්භක සතිය', 'නිමවන සතිය'],
...CalendarLocale,
},
timePickerLocale: {
...TimePickerLocale,
},
};
// All settings at:
// https://github.com/ant-design/ant-design/blob/master/components/date-picker/locale/example.json
export default locale;

View File

@ -34,6 +34,7 @@ export interface ErrorListProps {
errors?: React.ReactNode[]; errors?: React.ReactNode[];
warnings?: React.ReactNode[]; warnings?: React.ReactNode[];
className?: string; className?: string;
onVisibleChanged?: (visible: boolean) => void;
} }
export default function ErrorList({ export default function ErrorList({
@ -42,6 +43,7 @@ export default function ErrorList({
errors = EMPTY_LIST, errors = EMPTY_LIST,
warnings = EMPTY_LIST, warnings = EMPTY_LIST,
className: rootClassName, className: rootClassName,
onVisibleChanged,
}: ErrorListProps) { }: ErrorListProps) {
const { prefixCls } = React.useContext(FormItemPrefixContext); const { prefixCls } = React.useContext(FormItemPrefixContext);
const { getPrefixCls } = React.useContext(ConfigContext); const { getPrefixCls } = React.useContext(ConfigContext);
@ -69,16 +71,10 @@ export default function ErrorList({
return ( return (
<CSSMotion <CSSMotion
{...collapseMotion} motionDeadline={collapseMotion.motionDeadline}
motionName={`${rootPrefixCls}-show-help`} motionName={`${rootPrefixCls}-show-help`}
motionAppear={false}
motionEnter={false}
visible={!!fullKeyList.length} visible={!!fullKeyList.length}
onLeaveStart={node => { onVisibleChanged={onVisibleChanged}
// Force disable css override style in index.less configured
node.style.height = 'auto';
return { height: node.offsetHeight };
}}
> >
{holderProps => { {holderProps => {
const { className: holderClassName, style: holderStyle } = holderProps; const { className: holderClassName, style: holderStyle } = holderProps;

View File

@ -0,0 +1,209 @@
import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
import classNames from 'classnames';
import * as React from 'react';
import omit from 'rc-util/lib/omit';
import type { Meta } from 'rc-field-form/lib/interface';
import { Row } from '../../grid';
import FormItemLabel from '../FormItemLabel';
import FormItemInput from '../FormItemInput';
import type { FormItemStatusContextProps, ReportMetaChange } from '../context';
import { FormContext, FormItemInputContext, NoStyleItemContext } from '../context';
import type { FormItemProps, ValidateStatus } from '.';
import useDebounce from '../hooks/useDebounce';
const iconMap = {
success: CheckCircleFilled,
warning: ExclamationCircleFilled,
error: CloseCircleFilled,
validating: LoadingOutlined,
};
export interface ItemHolderProps extends FormItemProps {
prefixCls: string;
className?: string;
style?: React.CSSProperties;
errors: React.ReactNode[];
warnings: React.ReactNode[];
meta: Meta;
children?: React.ReactNode;
fieldId?: string;
isRequired?: boolean;
onSubItemMetaChange: ReportMetaChange;
}
export default function ItemHolder(props: ItemHolderProps) {
const {
prefixCls,
className,
style,
help,
errors,
warnings,
validateStatus,
meta,
hasFeedback,
hidden,
children,
fieldId,
isRequired,
onSubItemMetaChange,
...restProps
} = props;
const itemPrefixCls = `${prefixCls}-item`;
const { requiredMark } = React.useContext(FormContext);
// ======================== Margin ========================
const itemRef = React.useRef<HTMLDivElement>(null);
const debounceErrors = useDebounce(errors);
const debounceWarnings = useDebounce(warnings);
const hasHelp = help !== undefined && help !== null;
const hasError = !!(hasHelp || errors.length || warnings.length);
const [marginBottom, setMarginBottom] = React.useState<number | null>(null);
useLayoutEffect(() => {
if (hasError && itemRef.current) {
const itemStyle = getComputedStyle(itemRef.current);
setMarginBottom(parseInt(itemStyle.marginBottom, 10));
}
}, [hasError]);
const onErrorVisibleChanged = (nextVisible: boolean) => {
if (!nextVisible) {
setMarginBottom(null);
}
};
// ======================== Status ========================
let mergedValidateStatus: ValidateStatus = '';
if (validateStatus !== undefined) {
mergedValidateStatus = validateStatus;
} else if (meta.validating) {
mergedValidateStatus = 'validating';
} else if (debounceErrors.length) {
mergedValidateStatus = 'error';
} else if (debounceWarnings.length) {
mergedValidateStatus = 'warning';
} else if (meta.touched) {
mergedValidateStatus = 'success';
}
const formItemStatusContext = React.useMemo<FormItemStatusContextProps>(() => {
let feedbackIcon: React.ReactNode;
if (hasFeedback) {
const IconNode = mergedValidateStatus && iconMap[mergedValidateStatus];
feedbackIcon = IconNode ? (
<span
className={classNames(
`${itemPrefixCls}-feedback-icon`,
`${itemPrefixCls}-feedback-icon-${mergedValidateStatus}`,
)}
>
<IconNode />
</span>
) : null;
}
return {
status: mergedValidateStatus,
hasFeedback,
feedbackIcon,
isFormItemInput: true,
};
}, [mergedValidateStatus, hasFeedback]);
// ======================== Render ========================
const itemClassName = {
[itemPrefixCls]: true,
[`${itemPrefixCls}-with-help`]: hasHelp || debounceErrors.length || debounceWarnings.length,
[`${className}`]: !!className,
// Status
[`${itemPrefixCls}-has-feedback`]: mergedValidateStatus && hasFeedback,
[`${itemPrefixCls}-has-success`]: mergedValidateStatus === 'success',
[`${itemPrefixCls}-has-warning`]: mergedValidateStatus === 'warning',
[`${itemPrefixCls}-has-error`]: mergedValidateStatus === 'error',
[`${itemPrefixCls}-is-validating`]: mergedValidateStatus === 'validating',
[`${itemPrefixCls}-hidden`]: hidden,
};
return (
<div className={classNames(itemClassName)} style={style} ref={itemRef}>
<Row
className={`${itemPrefixCls}-row`}
{...omit(restProps, [
'_internalItemRender' as any,
'colon',
'dependencies',
'extra',
'fieldKey',
'getValueFromEvent',
'getValueProps',
'htmlFor',
'id', // It is deprecated because `htmlFor` is its replacement.
'initialValue',
'isListField',
'label',
'labelAlign',
'labelCol',
'labelWrap',
'messageVariables',
'name',
'normalize',
'noStyle',
'preserve',
'required',
'requiredMark',
'rules',
'shouldUpdate',
'trigger',
'tooltip',
'validateFirst',
'validateTrigger',
'valuePropName',
'wrapperCol',
])}
>
{/* Label */}
<FormItemLabel
htmlFor={fieldId}
required={isRequired}
requiredMark={requiredMark}
{...props}
prefixCls={prefixCls}
/>
{/* Input Group */}
<FormItemInput
{...props}
{...meta}
errors={debounceErrors}
warnings={debounceWarnings}
prefixCls={prefixCls}
status={mergedValidateStatus}
help={help}
marginBottom={marginBottom}
onErrorVisibleChanged={onErrorVisibleChanged}
>
<NoStyleItemContext.Provider value={onSubItemMetaChange}>
<FormItemInputContext.Provider value={formItemStatusContext}>
{children}
</FormItemInputContext.Provider>
</NoStyleItemContext.Provider>
</FormItemInput>
</Row>
{!!marginBottom && (
<div
className={`${itemPrefixCls}-margin-offset`}
style={{
marginBottom: -marginBottom,
}}
/>
)}
</div>
);
}

View File

@ -1,34 +1,26 @@
import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
import classNames from 'classnames'; import classNames from 'classnames';
import type { FormInstance } from 'rc-field-form'; import type { FormInstance } from 'rc-field-form';
import { Field, FieldContext, ListContext } from 'rc-field-form'; import { Field, FieldContext, ListContext } from 'rc-field-form';
import type { FieldProps } from 'rc-field-form/lib/Field'; import type { FieldProps } from 'rc-field-form/lib/Field';
import type { Meta, NamePath } from 'rc-field-form/lib/interface'; import type { Meta, NamePath } from 'rc-field-form/lib/interface';
import useState from 'rc-util/lib/hooks/useState'; import useState from 'rc-util/lib/hooks/useState';
import omit from 'rc-util/lib/omit';
import { supportRef } from 'rc-util/lib/ref'; import { supportRef } from 'rc-util/lib/ref';
import type { ReactNode } from 'react';
import * as React from 'react'; import * as React from 'react';
import { useContext, useMemo } from 'react'; import { useContext } from 'react';
import { ConfigContext } from '../config-provider'; import useFormItemStatus from '../hooks/useFormItemStatus';
import Row from '../grid/row'; import { ConfigContext } from '../../config-provider';
import { cloneElement, isValidElement } from '../_util/reactNode'; import { cloneElement, isValidElement } from '../../_util/reactNode';
import { tuple } from '../_util/type'; import { tuple } from '../../_util/type';
import warning from '../_util/warning'; import warning from '../../_util/warning';
import type { FormItemStatusContextProps } from './context'; import { FormContext, NoStyleItemContext } from '../context';
import { FormContext, FormItemInputContext, NoStyleItemContext } from './context'; import type { FormItemInputProps } from '../FormItemInput';
import type { FormItemInputProps } from './FormItemInput'; import type { FormItemLabelProps, LabelTooltipType } from '../FormItemLabel';
import FormItemInput from './FormItemInput'; import useFrameState from '../hooks/useFrameState';
import type { FormItemLabelProps, LabelTooltipType } from './FormItemLabel'; import useItemRef from '../hooks/useItemRef';
import FormItemLabel from './FormItemLabel'; import { getFieldId, toArray } from '../util';
import useDebounce from './hooks/useDebounce'; import ItemHolder from './ItemHolder';
import useFrameState from './hooks/useFrameState';
import useItemRef from './hooks/useItemRef'; import useStyle from '../style';
import useStyle from './style';
import { getFieldId, toArray } from './util';
const NAME_SPLIT = '__SPLIT__'; const NAME_SPLIT = '__SPLIT__';
@ -93,26 +85,15 @@ function genEmptyMeta(): Meta {
}; };
} }
const iconMap = { function InternalFormItem<Values = any>(props: FormItemProps<Values>): React.ReactElement {
success: CheckCircleFilled,
warning: ExclamationCircleFilled,
error: CloseCircleFilled,
validating: LoadingOutlined,
};
function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElement {
const { const {
name, name,
noStyle, noStyle,
className,
dependencies, dependencies,
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
style,
className,
shouldUpdate, shouldUpdate,
hasFeedback,
help,
rules, rules,
validateStatus,
children, children,
required, required,
label, label,
@ -120,10 +101,9 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
trigger = 'onChange', trigger = 'onChange',
validateTrigger, validateTrigger,
hidden, hidden,
...restProps
} = props; } = props;
const { getPrefixCls } = useContext(ConfigContext); const { getPrefixCls } = useContext(ConfigContext);
const { name: formName, requiredMark } = useContext(FormContext); const { name: formName } = useContext(FormContext);
const isRenderProps = typeof children === 'function'; const isRenderProps = typeof children === 'function';
const notifyParentMetaChange = useContext(NoStyleItemContext); const notifyParentMetaChange = useContext(NoStyleItemContext);
@ -214,50 +194,9 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
return [errorList, warningList]; return [errorList, warningList];
}, [subFieldErrors, meta.errors, meta.warnings]); }, [subFieldErrors, meta.errors, meta.warnings]);
const debounceErrors = useDebounce(mergedErrors);
const debounceWarnings = useDebounce(mergedWarnings);
// ===================== Children Ref ===================== // ===================== Children Ref =====================
const getItemRef = useItemRef(); const getItemRef = useItemRef();
// ======================== Status ========================
let mergedValidateStatus: ValidateStatus = '';
if (validateStatus !== undefined) {
mergedValidateStatus = validateStatus;
} else if (meta?.validating) {
mergedValidateStatus = 'validating';
} else if (debounceErrors.length) {
mergedValidateStatus = 'error';
} else if (debounceWarnings.length) {
mergedValidateStatus = 'warning';
} else if (meta?.touched) {
mergedValidateStatus = 'success';
}
const formItemStatusContext = useMemo<FormItemStatusContextProps>(() => {
let feedbackIcon: ReactNode;
if (hasFeedback) {
const IconNode = mergedValidateStatus && iconMap[mergedValidateStatus];
feedbackIcon = IconNode ? (
<span
className={classNames(
`${prefixCls}-item-feedback-icon`,
`${prefixCls}-item-feedback-icon-${mergedValidateStatus}`,
)}
>
<IconNode />
</span>
) : null;
}
return {
status: mergedValidateStatus,
hasFeedback,
feedbackIcon,
isFormItemInput: true,
};
}, [mergedValidateStatus, hasFeedback]);
// ======================== Render ======================== // ======================== Render ========================
function renderLayout( function renderLayout(
baseChildren: React.ReactNode, baseChildren: React.ReactNode,
@ -268,75 +207,21 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
return baseChildren; return baseChildren;
} }
const itemClassName = {
[`${prefixCls}-item`]: true,
[`${prefixCls}-item-with-help`]:
(help !== undefined && help !== null) || debounceErrors.length || debounceWarnings.length,
[`${className}`]: !!className,
// Status
[`${prefixCls}-item-has-feedback`]: mergedValidateStatus && hasFeedback,
[`${prefixCls}-item-has-success`]: mergedValidateStatus === 'success',
[`${prefixCls}-item-has-warning`]: mergedValidateStatus === 'warning',
[`${prefixCls}-item-has-error`]: mergedValidateStatus === 'error',
[`${prefixCls}-item-is-validating`]: mergedValidateStatus === 'validating',
[`${prefixCls}-item-hidden`]: hidden,
};
// ======================= Children =======================
return ( return (
<Row <ItemHolder
className={classNames(itemClassName, hashId)}
style={style}
key="row" key="row"
{...omit(restProps, [
'colon',
'extra',
'fieldKey',
'requiredMark',
'getValueFromEvent',
'getValueProps',
'htmlFor',
'id', // It is deprecated because `htmlFor` is its replacement.
'initialValue',
'isListField',
'labelAlign',
'labelWrap',
'labelCol',
'normalize',
'preserve',
'tooltip',
'validateFirst',
'valuePropName',
'wrapperCol',
'_internalItemRender' as any,
])}
>
{/* Label */}
<FormItemLabel
htmlFor={fieldId}
required={isRequired}
requiredMark={requiredMark}
{...props} {...props}
className={classNames(className, hashId)}
prefixCls={prefixCls} prefixCls={prefixCls}
/> fieldId={fieldId}
{/* Input Group */} isRequired={isRequired}
<FormItemInput errors={mergedErrors}
{...props} warnings={mergedWarnings}
{...meta} meta={meta}
errors={debounceErrors} onSubItemMetaChange={onSubItemMetaChange}
warnings={debounceWarnings}
prefixCls={prefixCls}
status={mergedValidateStatus}
help={help}
> >
<NoStyleItemContext.Provider value={onSubItemMetaChange}>
<FormItemInputContext.Provider value={formItemStatusContext}>
{baseChildren} {baseChildren}
</FormItemInputContext.Provider> </ItemHolder>
</NoStyleItemContext.Provider>
</FormItemInput>
</Row>
); );
} }
@ -467,4 +352,13 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
); );
} }
type InternalFormItemType = typeof InternalFormItem;
interface FormItemInterface extends InternalFormItemType {
useStatus: typeof useFormItemStatus;
}
const FormItem = InternalFormItem as FormItemInterface;
FormItem.useStatus = useFormItemStatus;
export default FormItem; export default FormItem;

View File

@ -11,6 +11,8 @@ interface FormItemInputMiscProps {
children: React.ReactNode; children: React.ReactNode;
errors: React.ReactNode[]; errors: React.ReactNode[];
warnings: React.ReactNode[]; warnings: React.ReactNode[];
marginBottom?: number | null;
onErrorVisibleChanged?: (visible: boolean) => void;
/** @private Internal Usage, do not use in any of your production. */ /** @private Internal Usage, do not use in any of your production. */
_internalItemRender?: { _internalItemRender?: {
mark: string; mark: string;
@ -18,7 +20,7 @@ interface FormItemInputMiscProps {
props: FormItemInputProps & FormItemInputMiscProps, props: FormItemInputProps & FormItemInputMiscProps,
domList: { domList: {
input: JSX.Element; input: JSX.Element;
errorList: JSX.Element; errorList: JSX.Element | null;
extra: JSX.Element | null; extra: JSX.Element | null;
}, },
) => React.ReactNode; ) => React.ReactNode;
@ -43,6 +45,8 @@ const FormItemInput: React.FC<FormItemInputProps & FormItemInputMiscProps> = pro
_internalItemRender: formItemRender, _internalItemRender: formItemRender,
extra, extra,
help, help,
marginBottom,
onErrorVisibleChanged,
} = props; } = props;
const baseClassName = `${prefixCls}-item`; const baseClassName = `${prefixCls}-item`;
@ -63,7 +67,9 @@ const FormItemInput: React.FC<FormItemInputProps & FormItemInputMiscProps> = pro
</div> </div>
); );
const formItemContext = React.useMemo(() => ({ prefixCls, status }), [prefixCls, status]); const formItemContext = React.useMemo(() => ({ prefixCls, status }), [prefixCls, status]);
const errorListDom = ( const errorListDom =
marginBottom !== null || errors.length || warnings.length ? (
<div style={{ display: 'flex', flexWrap: 'nowrap' }}>
<FormItemPrefixContext.Provider value={formItemContext}> <FormItemPrefixContext.Provider value={formItemContext}>
<ErrorList <ErrorList
errors={errors} errors={errors}
@ -71,9 +77,12 @@ const FormItemInput: React.FC<FormItemInputProps & FormItemInputMiscProps> = pro
help={help} help={help}
helpStatus={status} helpStatus={status}
className={`${baseClassName}-explain-connected`} className={`${baseClassName}-explain-connected`}
onVisibleChanged={onErrorVisibleChanged}
/> />
</FormItemPrefixContext.Provider> </FormItemPrefixContext.Provider>
); {!!marginBottom && <div style={{ width: 0, height: marginBottom }} />}
</div>
) : null;
// If extra = 0, && will goes wrong // If extra = 0, && will goes wrong
// 0&&error -> 0 // 0&&error -> 0

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,10 @@ exports[`Form Form item hidden noStyle should not work when hidden 1`] = `
class="ant-form ant-form-horizontal" class="ant-form ant-form-horizontal"
> >
<div <div
class="ant-row ant-form-item ant-form-item-hidden" class="ant-form-item ant-form-item-hidden"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-control" class="ant-col ant-form-item-control"
@ -26,6 +29,7 @@ exports[`Form Form item hidden noStyle should not work when hidden 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
</form> </form>
`; `;
@ -34,7 +38,10 @@ exports[`Form Form item hidden should work 1`] = `
class="ant-form ant-form-horizontal" class="ant-form ant-form-horizontal"
> >
<div <div
class="ant-row ant-form-item ant-form-item-hidden" class="ant-form-item ant-form-item-hidden"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-control" class="ant-col ant-form-item-control"
@ -55,17 +62,21 @@ exports[`Form Form item hidden should work 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
</form> </form>
`; `;
exports[`Form Form.Item should support data-*、aria-* and custom attribute 1`] = ` exports[`Form Form.Item should support data-*、aria-* and custom attribute 1`] = `
<form <form
class="ant-form ant-form-horizontal" class="ant-form ant-form-horizontal"
>
<div
class="ant-form-item"
> >
<div <div
aria-hidden="true" aria-hidden="true"
cccc="bbbb" cccc="bbbb"
class="ant-row ant-form-item" class="ant-row ant-form-item-row"
data-text="123" data-text="123"
> >
<div <div
@ -82,6 +93,7 @@ exports[`Form Form.Item should support data-*、aria-* and custom attribute 1`]
</div> </div>
</div> </div>
</div> </div>
</div>
</form> </form>
`; `;
@ -90,7 +102,10 @@ exports[`Form form should support disabled 1`] = `
class="ant-form ant-form-horizontal" class="ant-form ant-form-horizontal"
> >
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-col-4 ant-form-item-label" class="ant-col ant-col-4 ant-form-item-label"
@ -137,8 +152,12 @@ exports[`Form form should support disabled 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-col-4 ant-form-item-label" class="ant-col ant-col-4 ant-form-item-label"
@ -207,8 +226,12 @@ exports[`Form form should support disabled 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-col-4 ant-form-item-label" class="ant-col ant-col-4 ant-form-item-label"
@ -239,8 +262,12 @@ exports[`Form form should support disabled 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-col-4 ant-form-item-label" class="ant-col ant-col-4 ant-form-item-label"
@ -324,8 +351,12 @@ exports[`Form form should support disabled 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-col-4 ant-form-item-label" class="ant-col ant-col-4 ant-form-item-label"
@ -408,8 +439,12 @@ exports[`Form form should support disabled 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-col-4 ant-form-item-label" class="ant-col ant-col-4 ant-form-item-label"
@ -492,8 +527,12 @@ exports[`Form form should support disabled 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-col-4 ant-form-item-label" class="ant-col ant-col-4 ant-form-item-label"
@ -558,8 +597,12 @@ exports[`Form form should support disabled 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-col-4 ant-form-item-label" class="ant-col ant-col-4 ant-form-item-label"
@ -667,8 +710,12 @@ exports[`Form form should support disabled 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-col-4 ant-form-item-label" class="ant-col ant-col-4 ant-form-item-label"
@ -767,8 +814,12 @@ exports[`Form form should support disabled 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-col-4 ant-form-item-label" class="ant-col ant-col-4 ant-form-item-label"
@ -798,8 +849,12 @@ exports[`Form form should support disabled 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-col-4 ant-form-item-label" class="ant-col ant-col-4 ant-form-item-label"
@ -838,8 +893,64 @@ exports[`Form form should support disabled 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
>
<div
class="ant-col ant-col-4 ant-form-item-label"
>
<label
class=""
title="Upload"
>
Upload
</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"
>
<span
class="ant-upload-wrapper"
>
<div
class="ant-upload ant-upload-select ant-upload-disabled"
style="display: none;"
>
<span
class="ant-upload ant-upload-disabled"
role="button"
>
<input
accept=""
style="display: none;"
type="file"
/>
</span>
</div>
<div
class="ant-upload-list ant-upload-list-text"
/>
</span>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-col-4 ant-form-item-label" class="ant-col ant-col-4 ant-form-item-label"
@ -873,6 +984,7 @@ exports[`Form form should support disabled 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
</form> </form>
`; `;
@ -884,7 +996,10 @@ exports[`Form rtl render component should be rendered correctly in RTL direction
exports[`Form rtl render component should be rendered correctly in RTL direction 2`] = ` exports[`Form rtl render component should be rendered correctly in RTL direction 2`] = `
<div <div
class="ant-row ant-row-rtl ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-row-rtl ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-control ant-col-rtl" class="ant-col ant-form-item-control ant-col-rtl"
@ -898,4 +1013,5 @@ exports[`Form rtl render component should be rendered correctly in RTL direction
</div> </div>
</div> </div>
</div> </div>
</div>
`; `;

View File

@ -2,13 +2,14 @@ import { mount } from 'enzyme';
import React, { Component, useState } from 'react'; import React, { Component, useState } from 'react';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
import scrollIntoView from 'scroll-into-view-if-needed'; import scrollIntoView from 'scroll-into-view-if-needed';
import classNames from 'classnames';
import Form from '..'; import Form from '..';
import * as Util from '../util'; import * as Util from '../util';
import Button from '../../button'; import Button from '../../button';
import Input from '../../input'; import Input from '../../input';
import Select from '../../select'; import Select from '../../select';
import Upload from '../../upload';
import Cascader from '../../cascader'; import Cascader from '../../cascader';
import Checkbox from '../../checkbox'; import Checkbox from '../../checkbox';
import DatePicker from '../../date-picker'; import DatePicker from '../../date-picker';
@ -947,6 +948,9 @@ describe('Form', () => {
<Form.Item label="Switch" valuePropName="checked"> <Form.Item label="Switch" valuePropName="checked">
<Switch /> <Switch />
</Form.Item> </Form.Item>
<Form.Item label="Upload" valuePropName="fileList">
<Upload />
</Form.Item>
<Form.Item label="Button"> <Form.Item label="Button">
<Button>Button</Button> <Button>Button</Button>
</Form.Item> </Form.Item>
@ -1245,4 +1249,85 @@ describe('Form', () => {
expect(container.querySelector('.drawer-select')?.className).not.toContain('in-form-item'); expect(container.querySelector('.drawer-select')?.className).not.toContain('in-form-item');
expect(container.querySelector('.drawer-select')?.className).not.toContain('status-error'); expect(container.querySelector('.drawer-select')?.className).not.toContain('status-error');
}); });
it('Form.Item.useStatus should work', async () => {
const {
Item: { useStatus },
} = Form;
const CustomInput = ({ className, value }) => {
const { status } = useStatus();
return <div className={classNames(className, `custom-input-status-${status}`)}>{value}</div>;
};
const Demo = () => {
const [form] = Form.useForm();
return (
<Form form={form} name="my-form">
<Form.Item name="required" rules={[{ required: true }]}>
<CustomInput className="custom-input-required" value="" />
</Form.Item>
<Form.Item name="warning" validateStatus="warning">
<CustomInput className="custom-input-warning" />
</Form.Item>
<Form.Item name="normal">
<CustomInput className="custom-input" />
</Form.Item>
<CustomInput className="custom-input-wrong" />
<Button onClick={() => form.submit()} className="submit-button">
Submit
</Button>
</Form>
);
};
const { container } = render(<Demo />);
expect(container.querySelector('.custom-input-required')?.classList).toContain(
'custom-input-status-',
);
expect(container.querySelector('.custom-input-warning')?.classList).toContain(
'custom-input-status-warning',
);
expect(container.querySelector('.custom-input')?.classList).toContain('custom-input-status-');
expect(container.querySelector('.custom-input-wrong')?.classList).toContain(
'custom-input-status-undefined',
);
expect(errorSpy).toHaveBeenCalledWith(
expect.stringContaining('Form.Item.useStatus should be used under Form.Item component.'),
);
fireEvent.click(container.querySelector('.submit-button'));
await sleep(0);
expect(container.querySelector('.custom-input-required')?.classList).toContain(
'custom-input-status-error',
);
});
it('item customize margin', async () => {
const computeSpy = jest.spyOn(window, 'getComputedStyle').mockImplementation(() => ({
marginBottom: 24,
}));
const { container } = render(
<Form>
<Form.Item name="required" initialValue="bamboo" rules={[{ required: true }]}>
<Input />
</Form.Item>
</Form>,
);
fireEvent.change(container.querySelector('input'), {
target: {
value: '',
},
});
await sleep(0);
computeSpy.mockRestore();
expect(container.querySelector('.ant-form-item-margin-offset')).toHaveStyle({
marginBottom: -24,
});
});
}); });

View File

@ -15,6 +15,7 @@ Set component disabled, only works for antd components.
```tsx ```tsx
import React, { useState } from 'react'; import React, { useState } from 'react';
import { PlusOutlined } from '@ant-design/icons';
import { import {
Form, Form,
Input, Input,
@ -27,6 +28,7 @@ import {
TreeSelect, TreeSelect,
Switch, Switch,
Checkbox, Checkbox,
Upload,
} from 'antd'; } from 'antd';
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
@ -102,6 +104,14 @@ const FormDisabledDemo = () => {
<Form.Item label="Switch" valuePropName="checked"> <Form.Item label="Switch" valuePropName="checked">
<Switch /> <Switch />
</Form.Item> </Form.Item>
<Form.Item label="Upload" valuePropName="fileList">
<Upload action="/upload.do" listType="picture-card">
<div>
<PlusOutlined />
<div style={{ marginTop: 8 }}>Upload</div>
</div>
</Upload>
</Form.Item>
<Form.Item label="Button"> <Form.Item label="Button">
<Button>Button</Button> <Button>Button</Button>
</Form.Item> </Form.Item>

View File

@ -0,0 +1,22 @@
import { useContext } from 'react';
import type { ValidateStatus } from 'antd/es/form/FormItem';
import { FormItemInputContext } from '../context';
import warning from '../../_util/warning';
type UseFormItemStatus = () => {
status?: ValidateStatus;
};
const useFormItemStatus: UseFormItemStatus = () => {
const { status } = useContext(FormItemInputContext);
warning(
status !== undefined,
'Form.Item',
`Form.Item.useStatus should be used under Form.Item component. For more information: ${window.location.protocol}//${window.location.host}/components/form-cn/#Form.Item.useStatus`,
);
return { status };
};
export default useFormItemStatus;

View File

@ -246,7 +246,8 @@ Provide linkage between forms. If a sub form with `name` prop update, it will au
| resetFields | Reset fields to `initialValues` | (fields?: [NamePath](#NamePath)\[]) => void | | | resetFields | Reset fields to `initialValues` | (fields?: [NamePath](#NamePath)\[]) => void | |
| scrollToField | Scroll to field position | (name: [NamePath](#NamePath), options: \[[ScrollOptions](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options)]) => void | | | scrollToField | Scroll to field position | (name: [NamePath](#NamePath), options: \[[ScrollOptions](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options)]) => void | |
| setFields | Set fields status | (fields: [FieldData](#FieldData)\[]) => void | | | setFields | Set fields status | (fields: [FieldData](#FieldData)\[]) => void | |
| setFieldsValue | Set fields value(Will directly pass to form store. If you do not want to modify passed object, please clone first) | (values) => void | | | setFieldValue | Set fields value(Will directly pass to form store. If you do not want to modify passed object, please clone first) | (name: [NamePath](#NamePath), value: any) => void | 4.22.0 |
| setFieldsValue | Set fields value(Will directly pass to form store. If you do not want to modify passed object, please clone first). Use `setFieldValue` instead if you want to only config single value in Form.List | (values) => void | |
| submit | Submit the form. It's same as click `submit` button | () => void | | | submit | Submit the form. It's same as click `submit` button | () => void | |
| validateFields | Validate fields | (nameList?: [NamePath](#NamePath)\[]) => Promise | | | validateFields | Validate fields | (nameList?: [NamePath](#NamePath)\[]) => Promise | |
@ -335,6 +336,27 @@ const Demo = () => {
}; };
``` ```
### Form.Item.useStatus
`type Form.useFormItemStatus = (): { status: ValidateStatus | undefined }`
Added in `4.22.0`. Could be used to get validate status of Form.Item. If this hook is not used under Form.Item, `status` would be `undefined`:
```tsx
const CustomInput = ({ value }) => {
const { status } = Form.Item.useStatus();
return <input value={value} className={`custom-input-${status}`} />;
};
export default () => (
<Form>
<Form.Item name="username">
<CustomInput />
</Form.Item>
</Form>
);
```
#### Difference between other data fetching method #### Difference between other data fetching method
Form only update the Field which changed to avoid full refresh perf issue. Thus you can not get real time value with `getFieldsValue` in render. And `useWatch` will rerender current component to sync with latest value. You can also use Field renderProps to get better performance if only want to do conditional render. If component no need care field value change, you can use `onValuesChange` to give to parent component to avoid current one rerender. Form only update the Field which changed to avoid full refresh perf issue. Thus you can not get real time value with `getFieldsValue` in render. And `useWatch` will rerender current component to sync with latest value. You can also use Field renderProps to get better performance if only want to do conditional render. If component no need care field value change, you can use `onValuesChange` to give to parent component to avoid current one rerender.

View File

@ -245,7 +245,8 @@ Form.List 渲染表单相关操作函数。
| resetFields | 重置一组字段到 `initialValues` | (fields?: [NamePath](#NamePath)\[]) => void | | | resetFields | 重置一组字段到 `initialValues` | (fields?: [NamePath](#NamePath)\[]) => void | |
| scrollToField | 滚动到对应字段位置 | (name: [NamePath](#NamePath), options: [ScrollOptions](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options)) => void | | | scrollToField | 滚动到对应字段位置 | (name: [NamePath](#NamePath), options: [ScrollOptions](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options)) => void | |
| setFields | 设置一组字段状态 | (fields: [FieldData](#FieldData)\[]) => void | | | setFields | 设置一组字段状态 | (fields: [FieldData](#FieldData)\[]) => void | |
| setFieldsValue | 设置表单的值(该值将直接传入 form store 中。如果你不希望传入对象被修改,请克隆后传入) | (values) => void | | | setFieldValue | 设置表单的值(该值将直接传入 form store 中。如果你不希望传入对象被修改,请克隆后传入) | (name: [NamePath](#NamePath), value: any) => void | 4.22.0 |
| setFieldsValue | 设置表单的值(该值将直接传入 form store 中。如果你不希望传入对象被修改,请克隆后传入)。如果你只想修改 Form.List 中单项值,请通过 `setFieldValue` 进行指定 | (values) => void | |
| submit | 提交表单,与点击 `submit` 按钮效果相同 | () => void | | | submit | 提交表单,与点击 `submit` 按钮效果相同 | () => void | |
| validateFields | 触发表单验证 | (nameList?: [NamePath](#NamePath)\[]) => Promise | | | validateFields | 触发表单验证 | (nameList?: [NamePath](#NamePath)\[]) => Promise | |
@ -334,6 +335,27 @@ const Demo = () => {
}; };
``` ```
### Form.Item.useStatus
`type Form.Item.useStatus = (): { status: ValidateStatus | undefined }`
`4.22.0` 新增,可用于获取当前 Form.Item 的校验状态,如果上层没有 Form.Item`status` 将会返回 `undefined`
```tsx
const CustomInput = ({ value }) => {
const { status } = Form.Item.useStatus();
return <input value={value} className={`custom-input-${status}`} />;
};
export default () => (
<Form>
<Form.Item name="username">
<CustomInput />
</Form.Item>
</Form>
);
```
#### 与其他获取数据的方式的区别 #### 与其他获取数据的方式的区别
Form 仅会对变更的 Field 进行刷新,从而避免完整的组件刷新可能引发的性能问题。因而你无法在 render 阶段通过 `form.getFieldsValue` 来实时获取字段值,而 `useWatch` 提供了一种特定字段访问的方式,从而使得在当前组件中可以直接消费字段的值。同时,如果为了更好的渲染性能,你可以通过 Field 的 renderProps 仅更新需要更新的部分。而当当前组件更新或者 effect 都不需要消费字段值时,则可以通过 `onValuesChange` 将数据抛出,从而避免组件更新。 Form 仅会对变更的 Field 进行刷新,从而避免完整的组件刷新可能引发的性能问题。因而你无法在 render 阶段通过 `form.getFieldsValue` 来实时获取字段值,而 `useWatch` 提供了一种特定字段访问的方式,从而使得在当前组件中可以直接消费字段的值。同时,如果为了更好的渲染性能,你可以通过 Field 的 renderProps 仅更新需要更新的部分。而当当前组件更新或者 effect 都不需要消费字段值时,则可以通过 `onValuesChange` 将数据抛出,从而避免组件更新。

View File

@ -114,11 +114,8 @@ const genFormItemStyle: GenerateStyle<FormToken> = token => {
marginBottom: token.marginLG, marginBottom: token.marginLG,
verticalAlign: 'top', verticalAlign: 'top',
// We delay one frame (0.017s) here to let CSSMotion goes
transition: `margin-bottom ${token.motionDurationSlow} 0.017s linear`,
'&-with-help': { '&-with-help': {
marginBottom: 0,
transition: 'none', transition: 'none',
}, },
@ -262,9 +259,7 @@ const genFormItemStyle: GenerateStyle<FormToken> = token => {
}, },
'&-explain-connected': { '&-explain-connected': {
height: 0, width: '100%',
minHeight: 0,
opacity: 0,
}, },
'&-extra': { '&-extra': {
@ -284,7 +279,6 @@ const genFormItemStyle: GenerateStyle<FormToken> = token => {
[`&-with-help ${formItemCls}-explain`]: { [`&-with-help ${formItemCls}-explain`]: {
height: 'auto', height: 'auto',
minHeight: token.controlHeightSM,
opacity: 1, opacity: 1,
}, },
@ -327,16 +321,21 @@ const genFormMotionStyle: GenerateStyle<FormToken> = token => {
[componentCls]: { [componentCls]: {
// Explain holder // Explain holder
[`.${rootPrefixCls}-show-help`]: { [`.${rootPrefixCls}-show-help`]: {
transition: `height ${token.motionDurationSlow} linear, transition: `opacity ${token.motionDurationSlow} ${token.motionEaseInOut}`,
min-height ${token.motionDurationSlow} linear,
margin-bottom ${token.motionDurationSlow} ${token.motionEaseInOut},
opacity ${token.motionDurationSlow} ${token.motionEaseInOut}`,
'&-leave': { '&-appear, &-enter': {
minHeight: token.controlHeightSM, opacity: 0,
'&-active': { '&-active': {
minHeight: 0, opacity: 1,
},
},
'&-leave': {
opacity: 1,
'&-active': {
opacity: 0,
}, },
}, },
}, },

View File

@ -364,7 +364,10 @@ exports[`Input should support size in form 1`] = `
class="ant-form ant-form-horizontal ant-form-large" class="ant-form ant-form-horizontal ant-form-large"
> >
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-control" class="ant-col ant-form-item-control"
@ -384,5 +387,6 @@ exports[`Input should support size in form 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
</form> </form>
`; `;

View File

@ -133,6 +133,7 @@ import ptBR from '../pt_BR';
import ptPT from '../pt_PT'; import ptPT from '../pt_PT';
import roRO from '../ro_RO'; import roRO from '../ro_RO';
import ruRU from '../ru_RU'; import ruRU from '../ru_RU';
import siLK from '../si_LK';
import skSK from '../sk_SK'; import skSK from '../sk_SK';
import slSI from '../sl_SI'; import slSI from '../sl_SI';
import srRS from '../sr_RS'; import srRS from '../sr_RS';
@ -201,6 +202,7 @@ const locales = [
ptPT, ptPT,
roRO, roRO,
ruRU, ruRU,
siLK,
skSK, skSK,
slSI, slSI,
srRS, srRS,

View File

@ -0,0 +1,3 @@
import locale from '../locale/si_LK';
export default locale;

136
components/locale/si_LK.tsx Normal file
View File

@ -0,0 +1,136 @@
/* eslint-disable no-template-curly-in-string */
import Pagination from 'rc-pagination/lib/locale/si_LK';
import DatePicker from '../date-picker/locale/si_LK';
import TimePicker from '../time-picker/locale/si_LK';
import Calendar from '../calendar/locale/si_LK';
import type { Locale } from '../locale-provider';
const typeTemplate = '${label} වලංගු ${type} ක් නොවේ';
const localeValues: Locale = {
locale: 'si',
Pagination,
DatePicker,
TimePicker,
Calendar,
global: {
placeholder: 'කරුණාකර තෝරන්න',
},
Table: {
filterTitle: 'පෙරහන්',
filterConfirm: 'හරි',
filterReset: 'යළි සකසන්න',
filterEmptyText: 'පෙරහන් නැත',
filterCheckall: 'සියළු අථක තෝරන්න',
filterSearchPlaceholder: 'පෙරහන් තුළ සොයන්න',
emptyText: 'දත්ත නැත',
selectAll: 'වත්මන් පිටුව තෝරන්න',
selectInvert: 'වත්මන් පිටුව යටියනය',
selectNone: 'සියළු දත්ත ඉවතලන්න',
selectionAll: 'සියළු දත්ත තෝරන්න',
sortTitle: 'පෙළගැසීම',
expand: 'පේළිය දිගහරින්න',
collapse: 'පේළිය හකුළන්න',
triggerDesc: 'අවරෝහණව පෙළගැසීමට ඔබන්න',
triggerAsc: 'ආරෝහණව පෙළගැසීමට ඔබන්න',
cancelSort: 'පෙළගැසීම අවලංගු කිරීමට ඔබන්න',
},
Modal: {
okText: 'හරි',
cancelText: 'අවලංගු කරන්න',
justOkText: 'හරි',
},
Popconfirm: {
okText: 'හරි',
cancelText: 'අවලංගු කරන්න',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'මෙතැන සොයන්න',
itemUnit: 'අථකය',
itemsUnit: 'අථක',
remove: 'ඉවත් කරන්න',
selectCurrent: 'වත්මන් පිටුව තෝරන්න',
removeCurrent: 'වත්මන් පිටුව ඉවත් කරන්න',
selectAll: 'සියළු දත්ත තෝරන්න',
removeAll: 'සියළු දත්ත ඉවතලන්න',
selectInvert: 'වත්මන් පිටුව යටියනය',
},
Upload: {
uploading: 'උඩුගත වෙමින්...',
removeFile: 'ගොනුව ඉවතලන්න',
uploadError: 'උඩුගත වීමේ දෝෂයකි',
previewFile: 'ගොනුවේ පෙරදසුන',
downloadFile: 'ගොනුව බාගන්න',
},
Empty: {
description: 'දත්ත නැත',
},
Icon: {
icon: 'නිරූපකය',
},
Text: {
edit: 'සංස්කරණය',
copy: 'පිටපත්',
copied: 'පිටපත් විය',
expand: 'විහිදුවන්න',
},
PageHeader: {
back: 'ආපසු',
},
Form: {
optional: '(විකල්පයකි)',
defaultValidateMessages: {
default: '${label} සඳහා ක්‍ෂේත්‍රය වලංගුකරණයේ දෝෂයකි',
required: '${label} ඇතුල් කරන්න',
enum: '[${enum}] වලින් එකක් ${label} විය යුතුය',
whitespace: '${label} හිස් අකුරක් නොවිය යුතුය',
date: {
format: '${label} දිනයේ ආකෘතිය වැරදිය',
parse: '${label} දිනයකට පරිවර්තනය කළ නොහැකිය',
invalid: '${label} වලංගු නොවන දිනයකි',
},
types: {
string: typeTemplate,
method: typeTemplate,
array: typeTemplate,
object: typeTemplate,
number: typeTemplate,
date: typeTemplate,
boolean: typeTemplate,
integer: typeTemplate,
float: typeTemplate,
regexp: typeTemplate,
email: typeTemplate,
url: typeTemplate,
hex: typeTemplate,
},
string: {
len: '${label} අකුරු ${len}ක් විය යුතුය',
min: '${label} අවමය අකුරු ${min}ක් විය යුතුය',
max: '${label} අකුරු ${max}ක් දක්වා විය යුතුය',
range: '${label} අකුරු ${min}-${max}ක් අතර විය යුතුය',
},
number: {
len: '${label} නිසැකව ${len} සමාන විය යුතුය',
min: '${label} අවමය ${min} විය යුතුය',
max: '${label} උපරිමය ${max} විය යුතුය',
range: '${label} නිසැකව ${min}-${max} අතර විය යුතුය',
},
array: {
len: '${len} ${label} විය යුතුය',
min: 'අවම වශයෙන් ${min} ${label}',
max: 'උපරිම වශයෙන් ${max} ${label}',
range: '${label} ගණන ${min}-${max} අතර විය යුතුය',
},
pattern: {
mismatch: '${pattern} රටාවට ${label} නොගැළපේ',
},
},
},
Image: {
preview: 'පෙරදසුන',
},
};
export default localeValues;

View File

@ -43,7 +43,10 @@ exports[`renders ./components/mentions/demo/form.md extend context correctly 1`]
class="ant-form ant-form-horizontal" class="ant-form ant-form-horizontal"
> >
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-col-6 ant-form-item-label" class="ant-col ant-col-6 ant-form-item-label"
@ -78,8 +81,12 @@ exports[`renders ./components/mentions/demo/form.md extend context correctly 1`]
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-col-6 ant-form-item-label" class="ant-col ant-col-6 ant-form-item-label"
@ -115,8 +122,12 @@ exports[`renders ./components/mentions/demo/form.md extend context correctly 1`]
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-col-14 ant-col-offset-6 ant-form-item-control" class="ant-col ant-col-14 ant-col-offset-6 ant-form-item-control"
@ -148,6 +159,7 @@ exports[`renders ./components/mentions/demo/form.md extend context correctly 1`]
</div> </div>
</div> </div>
</div> </div>
</div>
</form> </form>
`; `;

View File

@ -43,7 +43,10 @@ exports[`renders ./components/mentions/demo/form.md correctly 1`] = `
class="ant-form ant-form-horizontal" class="ant-form ant-form-horizontal"
> >
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-col-6 ant-form-item-label" class="ant-col ant-col-6 ant-form-item-label"
@ -78,8 +81,12 @@ exports[`renders ./components/mentions/demo/form.md correctly 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-col-6 ant-form-item-label" class="ant-col ant-col-6 ant-form-item-label"
@ -115,8 +122,12 @@ exports[`renders ./components/mentions/demo/form.md correctly 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-col-14 ant-col-offset-6 ant-form-item-control" class="ant-col ant-col-14 ant-col-offset-6 ant-form-item-control"
@ -148,6 +159,7 @@ exports[`renders ./components/mentions/demo/form.md correctly 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
</form> </form>
`; `;

View File

@ -0,0 +1,136 @@
@dialog-prefix-cls: ~'@{ant-prefix}-modal';
.@{dialog-prefix-cls} {
.reset-component();
.modal-mask();
position: relative;
top: 100px;
width: auto;
max-width: calc(100vw - 32px);
margin: 0 auto;
padding-bottom: 24px;
&-wrap {
z-index: @zindex-modal;
}
&-title {
margin: 0;
color: @modal-heading-color;
font-weight: 500;
font-size: @modal-header-title-font-size;
line-height: @modal-header-title-line-height;
word-wrap: break-word;
}
&-content {
position: relative;
background-color: @modal-content-bg;
background-clip: padding-box;
border: 0;
border-radius: @modal-border-radius;
box-shadow: @shadow-2;
pointer-events: auto;
}
&-close {
position: absolute;
top: 0;
right: 0;
z-index: @zindex-popup-close;
padding: 0;
color: @modal-close-color;
font-weight: 700;
line-height: 1;
text-decoration: none;
background: transparent;
border: 0;
outline: 0;
cursor: pointer;
transition: color 0.3s;
&-x {
display: block;
width: @modal-header-close-size;
height: @modal-header-close-size;
font-size: @font-size-lg;
font-style: normal;
line-height: @modal-header-close-size;
text-align: center;
text-transform: none;
text-rendering: auto;
}
&:focus,
&:hover {
color: @icon-color-hover;
text-decoration: none;
}
}
&-header {
padding: @modal-header-padding;
color: @text-color;
background: @modal-header-bg;
border-bottom: @modal-header-border-width @modal-header-border-style
@modal-header-border-color-split;
border-radius: @modal-border-radius @modal-border-radius 0 0;
}
&-body {
padding: @modal-body-padding;
font-size: @font-size-base;
line-height: @line-height-base;
word-wrap: break-word;
}
&-footer {
padding: @modal-footer-padding-vertical @modal-footer-padding-horizontal;
text-align: right;
background: @modal-footer-bg;
border-top: @modal-footer-border-width @modal-footer-border-style
@modal-footer-border-color-split;
border-radius: 0 0 @modal-border-radius @modal-border-radius;
.@{ant-prefix}-btn + .@{ant-prefix}-btn:not(.@{ant-prefix}-dropdown-trigger) {
margin-bottom: 0;
margin-left: 8px;
}
}
&-open {
overflow: hidden;
}
}
.@{dialog-prefix-cls}-centered {
text-align: center;
&::before {
display: inline-block;
width: 0;
height: 100%;
vertical-align: middle;
content: '';
}
.@{dialog-prefix-cls} {
top: 0;
display: inline-block;
padding-bottom: 0;
text-align: left;
vertical-align: middle;
}
}
@media (max-width: @screen-sm-max) {
.@{dialog-prefix-cls} {
max-width: calc(100vw - 16px);
margin: 8px auto;
}
.@{dialog-prefix-cls}-centered {
.@{dialog-prefix-cls} {
flex: 1;
}
}
}

View File

@ -1,6 +1,6 @@
import { render as testLibRender } from '@testing-library/react';
import { mount, render } from 'enzyme';
import React from 'react'; import React from 'react';
import { mount, render } from 'enzyme';
import { fireEvent, render as testLibRender } from '@testing-library/react';
import Radio from '..'; import Radio from '..';
describe('Radio Group', () => { describe('Radio Group', () => {
@ -224,4 +224,16 @@ describe('Radio Group', () => {
}); });
}); });
}); });
it('onBlur & onFocus should work', () => {
const handleBlur = jest.fn();
const handleFocus = jest.fn();
const { container } = testLibRender(
<Radio.Group options={['1', '2', '3']} onBlur={handleBlur} onFocus={handleFocus} />,
);
fireEvent.focus(container.firstChild);
expect(handleFocus).toHaveBeenCalledTimes(1);
fireEvent.blur(container.firstChild);
expect(handleBlur).toHaveBeenCalledTimes(1);
});
}); });

View File

@ -18,13 +18,6 @@ const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>((props, ref
value: props.value, value: props.value,
}); });
const { prefixCls: customizePrefixCls } = props;
const prefixCls = getPrefixCls('radio', customizePrefixCls);
const groupPrefixCls = `${prefixCls}-group`;
// Style
const [wrapSSR, hashId] = useStyle(prefixCls);
const onRadioChange = (ev: RadioChangeEvent) => { const onRadioChange = (ev: RadioChangeEvent) => {
const lastValue = value; const lastValue = value;
const val = ev.target.value; const val = ev.target.value;
@ -37,8 +30,8 @@ const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>((props, ref
} }
}; };
const renderGroup = () => {
const { const {
prefixCls: customizePrefixCls,
className = '', className = '',
options, options,
buttonStyle = 'outline' as RadioGroupButtonStyle, buttonStyle = 'outline' as RadioGroupButtonStyle,
@ -49,7 +42,15 @@ const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>((props, ref
id, id,
onMouseEnter, onMouseEnter,
onMouseLeave, onMouseLeave,
onFocus,
onBlur,
} = props; } = props;
const prefixCls = getPrefixCls('radio', customizePrefixCls);
const groupPrefixCls = `${prefixCls}-group`;
// Style
const [wrapSSR, hashId] = useStyle(prefixCls);
let childrenToRender = children; let childrenToRender = children;
// 如果存在 options, 优先使用 // 如果存在 options, 优先使用
if (options && options.length > 0) { if (options && options.length > 0) {
@ -102,15 +103,11 @@ const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>((props, ref
style={style} style={style}
onMouseEnter={onMouseEnter} onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave} onMouseLeave={onMouseLeave}
onFocus={onFocus}
onBlur={onBlur}
id={id} id={id}
ref={ref} ref={ref}
> >
{childrenToRender}
</div>,
);
};
return (
<RadioGroupContextProvider <RadioGroupContextProvider
value={{ value={{
onChange: onRadioChange, onChange: onRadioChange,
@ -120,8 +117,9 @@ const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>((props, ref
optionType: props.optionType, optionType: props.optionType,
}} }}
> >
{renderGroup()} {childrenToRender}
</RadioGroupContextProvider> </RadioGroupContextProvider>
</div>,
); );
}); });

View File

@ -20,6 +20,8 @@ export interface RadioGroupProps extends AbstractCheckboxGroupProps {
id?: string; id?: string;
optionType?: RadioGroupOptionType; optionType?: RadioGroupOptionType;
buttonStyle?: RadioGroupButtonStyle; buttonStyle?: RadioGroupButtonStyle;
onFocus?: React.FocusEventHandler<HTMLDivElement>;
onBlur?: React.FocusEventHandler<HTMLDivElement>;
} }
export interface RadioGroupContextProps { export interface RadioGroupContextProps {

View File

@ -6,7 +6,7 @@ export interface SkeletonElementProps {
className?: string; className?: string;
style?: React.CSSProperties; style?: React.CSSProperties;
size?: 'large' | 'small' | 'default' | number; size?: 'large' | 'small' | 'default' | number;
shape?: 'circle' | 'square' | 'round'; shape?: 'circle' | 'square' | 'round' | 'default';
active?: boolean; active?: boolean;
} }

View File

@ -114,7 +114,7 @@ Array [
class="ant-skeleton ant-skeleton-element" class="ant-skeleton ant-skeleton-element"
> >
<span <span
class="ant-skeleton-button ant-skeleton-button-square" class="ant-skeleton-button"
/> />
</div> </div>
</div> </div>
@ -148,7 +148,7 @@ Array [
class="ant-skeleton ant-skeleton-element" class="ant-skeleton ant-skeleton-element"
> >
<span <span
class="ant-skeleton-button ant-skeleton-button-square" class="ant-skeleton-button"
/> />
</div>, </div>,
<br />, <br />,
@ -230,7 +230,10 @@ Array [
style="margin:16px 0" style="margin:16px 0"
> >
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -268,8 +271,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -307,8 +314,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -395,8 +406,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -428,6 +443,25 @@ Array [
> >
<input <input
checked="" checked=""
class="ant-radio-button-input"
type="radio"
value="default"
/>
<span
class="ant-radio-button-inner"
/>
</span>
<span>
Default
</span>
</label>
<label
class="ant-radio-button-wrapper ant-radio-button-wrapper-in-form-item"
>
<span
class="ant-radio-button"
>
<input
class="ant-radio-button-input" class="ant-radio-button-input"
type="radio" type="radio"
value="square" value="square"
@ -483,8 +517,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -552,6 +590,7 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
</form>, </form>,
] ]
`; `;

View File

@ -114,7 +114,7 @@ Array [
class="ant-skeleton ant-skeleton-element" class="ant-skeleton ant-skeleton-element"
> >
<span <span
class="ant-skeleton-button ant-skeleton-button-square" class="ant-skeleton-button"
/> />
</div> </div>
</div> </div>
@ -148,7 +148,7 @@ Array [
class="ant-skeleton ant-skeleton-element" class="ant-skeleton ant-skeleton-element"
> >
<span <span
class="ant-skeleton-button ant-skeleton-button-square" class="ant-skeleton-button"
/> />
</div>, </div>,
<br />, <br />,
@ -230,7 +230,10 @@ Array [
style="margin:16px 0" style="margin:16px 0"
> >
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -268,8 +271,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -307,8 +314,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -395,8 +406,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -428,6 +443,25 @@ Array [
> >
<input <input
checked="" checked=""
class="ant-radio-button-input"
type="radio"
value="default"
/>
<span
class="ant-radio-button-inner"
/>
</span>
<span>
Default
</span>
</label>
<label
class="ant-radio-button-wrapper ant-radio-button-wrapper-in-form-item"
>
<span
class="ant-radio-button"
>
<input
class="ant-radio-button-input" class="ant-radio-button-input"
type="radio" type="radio"
value="square" value="square"
@ -483,8 +517,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -552,6 +590,7 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
</form>, </form>,
] ]
`; `;

View File

@ -20,14 +20,14 @@ import { DotChartOutlined } from '@ant-design/icons';
import React, { useState } from 'react'; import React, { useState } from 'react';
type SizeType = 'default' | 'small' | 'large'; type SizeType = 'default' | 'small' | 'large';
type ButtonShapeType = 'circle' | 'square' | 'round'; type ButtonShapeType = 'circle' | 'square' | 'round' | 'default';
type AvatarShapeType = 'circle' | 'square'; type AvatarShapeType = 'circle' | 'square';
const App: React.FC = () => { const App: React.FC = () => {
const [active, setActive] = useState(false); const [active, setActive] = useState(false);
const [block, setBlock] = useState(false); const [block, setBlock] = useState(false);
const [size, setSize] = useState<SizeType>('default'); const [size, setSize] = useState<SizeType>('default');
const [buttonShape, setButtonShape] = useState<ButtonShapeType>('square'); const [buttonShape, setButtonShape] = useState<ButtonShapeType>('default');
const [avatarShape, setAvatarShape] = useState<AvatarShapeType>('circle'); const [avatarShape, setAvatarShape] = useState<AvatarShapeType>('circle');
const handleActiveChange = (checked: boolean) => { const handleActiveChange = (checked: boolean) => {
@ -88,6 +88,7 @@ const App: React.FC = () => {
</Form.Item> </Form.Item>
<Form.Item label="Button Shape"> <Form.Item label="Button Shape">
<Radio.Group value={buttonShape} onChange={handleShapeButton}> <Radio.Group value={buttonShape} onChange={handleShapeButton}>
<Radio.Button value="default">Default</Radio.Button>
<Radio.Button value="square">Square</Radio.Button> <Radio.Button value="square">Square</Radio.Button>
<Radio.Button value="round">Round</Radio.Button> <Radio.Button value="round">Round</Radio.Button>
<Radio.Button value="circle">Circle</Radio.Button> <Radio.Button value="circle">Circle</Radio.Button>

View File

@ -55,7 +55,7 @@ Provide a placeholder while you wait for content to load, or to visualise conten
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| active | Show animation effect | boolean | false | | | active | Show animation effect | boolean | false | |
| block | Option to fit button width to its parent width | boolean | false | 4.17.0 | | block | Option to fit button width to its parent width | boolean | false | 4.17.0 |
| shape | Set the shape of button | `circle` \| `round` \| `default` | - | | | shape | Set the shape of button | `circle` \| `round` \| `square` \| `default` | - | |
| size | Set the size of button | `large` \| `small` \| `default` | - | | | size | Set the size of button | `large` \| `small` \| `default` | - | |
### SkeletonInputProps ### SkeletonInputProps

View File

@ -53,10 +53,10 @@ cover: https://gw.alipayobjects.com/zos/alicdn/KpcciCJgv/Skeleton.svg
### SkeletonButtonProps ### SkeletonButtonProps
| 属性 | 说明 | 类型 | 默认值 | 版本 | | 属性 | 说明 | 类型 | 默认值 | 版本 |
| ------ | ------------------------------ | -------------------------------- | ------ | ------ | | --- | --- | --- | --- | --- |
| active | 是否展示动画效果 | boolean | false | | | active | 是否展示动画效果 | boolean | false | |
| block | 将按钮宽度调整为其父宽度的选项 | boolean | false | 4.17.0 | | block | 将按钮宽度调整为其父宽度的选项 | boolean | false | 4.17.0 |
| shape | 指定按钮的形状 | `circle` \| `round` \| `default` | - | | | shape | 指定按钮的形状 | `circle` \| `round` \| `square` \| `default` | - | |
| size | 设置按钮的大小 | `large` \| `small` \| `default` | - | | | size | 设置按钮的大小 | `large` \| `small` \| `default` | - | |
### SkeletonInputProps ### SkeletonInputProps

View File

@ -148,6 +148,42 @@ exports[`renders ./components/space/demo/align.md extend context correctly 1`] =
</div> </div>
</div> </div>
</div> </div>
<div
class="space-align-block"
>
<div
class="ant-space ant-space-horizontal ant-space-align-stretch"
>
<div
class="ant-space-item"
style="margin-right:8px"
>
stretch
</div>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
</div>
<div
class="ant-space-item"
>
<span
class="mock-block"
>
Block
</span>
</div>
</div>
</div>
</div> </div>
`; `;

View File

@ -148,6 +148,42 @@ exports[`renders ./components/space/demo/align.md correctly 1`] = `
</div> </div>
</div> </div>
</div> </div>
<div
class="space-align-block"
>
<div
class="ant-space ant-space-horizontal ant-space-align-stretch"
>
<div
class="ant-space-item"
style="margin-right:8px"
>
stretch
</div>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
</div>
<div
class="ant-space-item"
>
<span
class="mock-block"
>
Block
</span>
</div>
</div>
</div>
</div> </div>
`; `;

View File

@ -47,6 +47,13 @@ const App: React.FC = () => (
<span className="mock-block">Block</span> <span className="mock-block">Block</span>
</Space> </Space>
</div> </div>
<div className="space-align-block">
<Space align="stretch">
stretch
<Button type="primary">Primary</Button>
<span className="mock-block">Block</span>
</Space>
</div>
</div> </div>
); );

View File

@ -16,7 +16,7 @@ Avoid components clinging together and set a unified space.
| Property | Description | Type | Default | Version | | Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| align | Align items | `start` \| `end` \|`center` \|`baseline` | - | 4.2.0 | | align | Align items | `start` \| `end` \|`center` \|`baseline` \|`stretch` | - | 4.2.0 |
| direction | The space direction | `vertical` \| `horizontal` | `horizontal` | 4.1.0 | | direction | The space direction | `vertical` \| `horizontal` | `horizontal` | 4.1.0 |
| size | The space size | [Size](#Size) \| [Size\[\]](#Size) | `small` | 4.1.0 \| Array: 4.9.0 | | size | The space size | [Size](#Size) \| [Size\[\]](#Size) | `small` | 4.1.0 \| Array: 4.9.0 |
| split | Set split | ReactNode | - | 4.7.0 | | split | Set split | ReactNode | - | 4.7.0 |

View File

@ -24,7 +24,7 @@ export interface SpaceProps extends React.HTMLAttributes<HTMLDivElement> {
size?: SpaceSize | [SpaceSize, SpaceSize]; size?: SpaceSize | [SpaceSize, SpaceSize];
direction?: 'horizontal' | 'vertical'; direction?: 'horizontal' | 'vertical';
// No `stretch` since many components do not support that. // No `stretch` since many components do not support that.
align?: 'start' | 'end' | 'center' | 'baseline'; align?: 'start' | 'end' | 'center' | 'baseline' | 'stretch';
split?: React.ReactNode; split?: React.ReactNode;
wrap?: boolean; wrap?: boolean;
} }

View File

@ -20,7 +20,7 @@ cover: https://gw.alipayobjects.com/zos/antfincdn/wc6%263gJ0Y8/Space.svg
| 参数 | 说明 | 类型 | 默认值 | 版本 | | 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| align | 对齐方式 | `start` \| `end` \|`center` \|`baseline` | - | 4.2.0 | | align | 对齐方式 | `start` \| `end` \|`center` \|`baseline` \|`stretch` | - | 4.2.0 |
| direction | 间距方向 | `vertical` \| `horizontal` | `horizontal` | 4.1.0 | | direction | 间距方向 | `vertical` \| `horizontal` | `horizontal` | 4.1.0 |
| size | 间距大小 | [Size](#Size) \| [Size\[\]](#Size) | `small` | 4.1.0 \| Array: 4.9.0 | | size | 间距大小 | [Size](#Size) \| [Size\[\]](#Size) | `small` | 4.1.0 \| Array: 4.9.0 |
| split | 设置拆分 | ReactNode | - | 4.7.0 | | split | 设置拆分 | ReactNode | - | 4.7.0 |

View File

@ -36,6 +36,9 @@ const genSpaceStyle: GenerateStyle<SpaceToken> = token => {
'&-baseline': { '&-baseline': {
alignItems: 'flex-baseline', alignItems: 'flex-baseline',
}, },
'&-stretch': {
alignItems: 'stretch',
},
}, },
[`${componentCls}-space-item`]: { [`${componentCls}-space-item`]: {
'&:empty': { '&:empty': {

View File

@ -2293,7 +2293,10 @@ Array [
style="margin-bottom:16px" style="margin-bottom:16px"
> >
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2331,8 +2334,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2370,8 +2377,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2409,8 +2420,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2448,8 +2463,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2487,8 +2506,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2526,8 +2549,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2565,8 +2592,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2604,8 +2635,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2643,8 +2678,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2682,8 +2721,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2770,8 +2813,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2857,8 +2904,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2925,8 +2976,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -3032,8 +3087,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -3139,6 +3198,7 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
</form>, </form>,
<div <div
class="ant-table-wrapper" class="ant-table-wrapper"
@ -15719,7 +15779,10 @@ Array [
style="margin-bottom:16px" style="margin-bottom:16px"
> >
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -15757,8 +15820,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -15796,6 +15863,7 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
</form>, </form>,
<div <div
class="ant-table-wrapper" class="ant-table-wrapper"

View File

@ -1802,7 +1802,10 @@ Array [
style="margin-bottom:16px" style="margin-bottom:16px"
> >
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -1840,8 +1843,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -1879,8 +1886,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -1918,8 +1929,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -1957,8 +1972,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -1996,8 +2015,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2035,8 +2058,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2074,8 +2101,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2113,8 +2144,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2152,8 +2187,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2191,8 +2230,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2279,8 +2322,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2366,8 +2413,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2434,8 +2485,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2541,8 +2596,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -2648,6 +2707,7 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
</form>, </form>,
<div <div
class="ant-table-wrapper" class="ant-table-wrapper"
@ -11941,7 +12001,10 @@ Array [
style="margin-bottom:16px" style="margin-bottom:16px"
> >
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -11979,8 +12042,12 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-form-item-label" class="ant-col ant-form-item-label"
@ -12018,6 +12085,7 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div>
</form>, </form>,
<div <div
class="ant-table-wrapper" class="ant-table-wrapper"

View File

@ -0,0 +1,8 @@
import type { TimePickerLocale } from '../index';
const locale: TimePickerLocale = {
placeholder: 'වේලාව තෝරන්න',
rangePlaceholder: ['ආරම්භක වේලාව', 'නිමවන වේලාව'],
};
export default locale;

View File

@ -4,6 +4,7 @@ import RcUpload from 'rc-upload';
import useMergedState from 'rc-util/lib/hooks/useMergedState'; import useMergedState from 'rc-util/lib/hooks/useMergedState';
import * as React from 'react'; import * as React from 'react';
import { ConfigContext } from '../config-provider'; import { ConfigContext } from '../config-provider';
import DisabledContext from '../config-provider/DisabledContext';
import LocaleReceiver from '../locale-provider/LocaleReceiver'; import LocaleReceiver from '../locale-provider/LocaleReceiver';
import defaultLocale from '../locale/default'; import defaultLocale from '../locale/default';
import warning from '../_util/warning'; import warning from '../_util/warning';
@ -38,7 +39,7 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
onChange, onChange,
onDrop, onDrop,
previewFile, previewFile,
disabled, disabled: customDisabled,
locale: propLocale, locale: propLocale,
iconRender, iconRender,
isImageUrl, isImageUrl,
@ -52,6 +53,10 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
maxCount, maxCount,
} = props; } = props;
// ===================== Disabled =====================
const disabled = React.useContext(DisabledContext);
const mergedDisabled = customDisabled || disabled;
const [mergedFileList, setMergedFileList] = useMergedState(defaultFileList || [], { const [mergedFileList, setMergedFileList] = useMergedState(defaultFileList || [], {
value: fileList, value: fileList,
postState: list => list ?? [], postState: list => list ?? [],
@ -300,6 +305,7 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
onSuccess, onSuccess,
...(props as RcUploadProps), ...(props as RcUploadProps),
prefixCls, prefixCls,
disabled: mergedDisabled,
beforeUpload: mergedBeforeUpload, beforeUpload: mergedBeforeUpload,
onChange: undefined, onChange: undefined,
}; };
@ -311,7 +317,7 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
// !children: https://github.com/ant-design/ant-design/issues/14298 // !children: https://github.com/ant-design/ant-design/issues/14298
// disabled: https://github.com/ant-design/ant-design/issues/16478 // disabled: https://github.com/ant-design/ant-design/issues/16478
// https://github.com/ant-design/ant-design/issues/24197 // https://github.com/ant-design/ant-design/issues/24197
if (!children || disabled) { if (!children || mergedDisabled) {
delete rcUploadProps.id; delete rcUploadProps.id;
} }
@ -339,7 +345,7 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
onPreview={onPreview} onPreview={onPreview}
onDownload={onDownload} onDownload={onDownload}
onRemove={handleRemove} onRemove={handleRemove}
showRemoveIcon={!disabled && showRemoveIcon} showRemoveIcon={!mergedDisabled && showRemoveIcon}
showPreviewIcon={showPreviewIcon} showPreviewIcon={showPreviewIcon}
showDownloadIcon={showDownloadIcon} showDownloadIcon={showDownloadIcon}
removeIcon={removeIcon} removeIcon={removeIcon}
@ -371,7 +377,7 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
[`${prefixCls}-drag`]: true, [`${prefixCls}-drag`]: true,
[`${prefixCls}-drag-uploading`]: mergedFileList.some(file => file.status === 'uploading'), [`${prefixCls}-drag-uploading`]: mergedFileList.some(file => file.status === 'uploading'),
[`${prefixCls}-drag-hover`]: dragState === 'dragover', [`${prefixCls}-drag-hover`]: dragState === 'dragover',
[`${prefixCls}-disabled`]: disabled, [`${prefixCls}-disabled`]: mergedDisabled,
[`${prefixCls}-rtl`]: direction === 'rtl', [`${prefixCls}-rtl`]: direction === 'rtl',
}, },
hashId, hashId,
@ -395,7 +401,7 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
} }
const uploadButtonCls = classNames(prefixCls, `${prefixCls}-select`, { const uploadButtonCls = classNames(prefixCls, `${prefixCls}-select`, {
[`${prefixCls}-disabled`]: disabled, [`${prefixCls}-disabled`]: mergedDisabled,
}); });
const renderUploadButton = (uploadButtonStyle?: React.CSSProperties) => ( const renderUploadButton = (uploadButtonStyle?: React.CSSProperties) => (

View File

@ -3692,7 +3692,10 @@ exports[`renders ./components/upload/demo/upload-with-aliyun-oss.md extend conte
class="ant-form ant-form-horizontal" class="ant-form ant-form-horizontal"
> >
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-col-4 ant-form-item-label" class="ant-col ant-col-4 ant-form-item-label"
@ -3767,5 +3770,6 @@ exports[`renders ./components/upload/demo/upload-with-aliyun-oss.md extend conte
</div> </div>
</div> </div>
</div> </div>
</div>
</form> </form>
`; `;

View File

@ -3476,7 +3476,10 @@ exports[`renders ./components/upload/demo/upload-with-aliyun-oss.md correctly 1`
class="ant-form ant-form-horizontal" class="ant-form ant-form-horizontal"
> >
<div <div
class="ant-row ant-form-item" class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
> >
<div <div
class="ant-col ant-col-4 ant-form-item-label" class="ant-col ant-col-4 ant-form-item-label"
@ -3551,5 +3554,6 @@ exports[`renders ./components/upload/demo/upload-with-aliyun-oss.md correctly 1`
</div> </div>
</div> </div>
</div> </div>
</div>
</form> </form>
`; `;

View File

@ -16,7 +16,7 @@ export interface UploadToken extends FullToken<'Upload'> {
} }
const genBaseStyle: GenerateStyle<UploadToken> = token => { const genBaseStyle: GenerateStyle<UploadToken> = token => {
const { componentCls } = token; const { componentCls, colorTextDisabled } = token;
return { return {
[`${componentCls}-wrapper`]: { [`${componentCls}-wrapper`]: {
@ -34,6 +34,7 @@ const genBaseStyle: GenerateStyle<UploadToken> = token => {
}, },
[`${componentCls}-disabled`]: { [`${componentCls}-disabled`]: {
color: colorTextDisabled,
cursor: 'not-allowed', cursor: 'not-allowed',
}, },
}, },

View File

@ -81,6 +81,7 @@ The following languages are currently supported:
| Portuguese | pt_PT | | Portuguese | pt_PT |
| Romanian | ro_RO | | Romanian | ro_RO |
| Russian | ru_RU | | Russian | ru_RU |
| Sinhalese / Sinhala | si_LK |
| Slovak | sk_SK | | Slovak | sk_SK |
| Serbian | sr_RS | | Serbian | sr_RS |
| Slovenian | sl_SI | | Slovenian | sl_SI |

View File

@ -78,6 +78,7 @@ return (
| 葡萄牙语 | pt_PT | | 葡萄牙语 | pt_PT |
| 罗马尼亚语 | ro_RO | | 罗马尼亚语 | ro_RO |
| 俄罗斯语 | ru_RU | | 俄罗斯语 | ru_RU |
| 僧伽罗语 | si_LK |
| 斯洛伐克语 | sk_SK | | 斯洛伐克语 | sk_SK |
| 塞尔维亚语 | sr_RS | | 塞尔维亚语 | sr_RS |
| 斯洛文尼亚语 | sl_SI | | 斯洛文尼亚语 | sl_SI |

View File

@ -125,16 +125,16 @@
"rc-dialog": "~8.9.0", "rc-dialog": "~8.9.0",
"rc-drawer": "~4.4.2", "rc-drawer": "~4.4.2",
"rc-dropdown": "~4.0.0", "rc-dropdown": "~4.0.0",
"rc-field-form": "~1.26.1", "rc-field-form": "~1.27.0",
"rc-image": "~5.7.0", "rc-image": "~5.7.0",
"rc-input": "~0.0.1-alpha.5", "rc-input": "~0.0.1-alpha.5",
"rc-input-number": "~7.3.0", "rc-input-number": "~7.3.0",
"rc-mentions": "~1.9.0", "rc-mentions": "~1.9.0",
"rc-menu": "~9.6.0", "rc-menu": "~9.6.0",
"rc-motion": "^2.5.1", "rc-motion": "^2.6.1",
"rc-notification": "~5.0.0-alpha.9", "rc-notification": "~5.0.0-alpha.9",
"rc-pagination": "~3.1.16", "rc-pagination": "~3.1.17",
"rc-picker": "~2.6.8", "rc-picker": "~2.6.10",
"rc-progress": "~3.3.2", "rc-progress": "~3.3.2",
"rc-rate": "~2.9.0", "rc-rate": "~2.9.0",
"rc-resize-observer": "^1.2.0", "rc-resize-observer": "^1.2.0",
@ -215,7 +215,7 @@
"eslint-plugin-import": "^2.21.1", "eslint-plugin-import": "^2.21.1",
"eslint-plugin-jest": "^26.4.0", "eslint-plugin-jest": "^26.4.0",
"eslint-plugin-jsx-a11y": "^6.2.1", "eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-markdown": "^2.0.0", "eslint-plugin-markdown": "^3.0.0",
"eslint-plugin-react": "^7.28.0", "eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-hooks": "^4.1.2", "eslint-plugin-react-hooks": "^4.1.2",
"eslint-plugin-unicorn": "^43.0.0", "eslint-plugin-unicorn": "^43.0.0",

View File

@ -574,6 +574,7 @@
@modal-mask-bg: fade(@black, 45%); @modal-mask-bg: fade(@black, 45%);
@modal-confirm-body-padding: 32px 32px 24px; @modal-confirm-body-padding: 32px 32px 24px;
@modal-confirm-title-font-size: @font-size-lg; @modal-confirm-title-font-size: @font-size-lg;
@modal-border-radius: @border-radius-base;
// Progress // Progress
// -- // --

View File

@ -629,6 +629,7 @@
@modal-mask-bg: fade(@black, 45%); @modal-mask-bg: fade(@black, 45%);
@modal-confirm-body-padding: 32px 32px 24px; @modal-confirm-body-padding: 32px 32px 24px;
@modal-confirm-title-font-size: @font-size-lg; @modal-confirm-title-font-size: @font-size-lg;
@modal-border-radius: @border-radius-base;
// Progress // Progress
// -- // --