diff --git a/components/badge/__tests__/__snapshots__/demo.test.js.snap b/components/badge/__tests__/__snapshots__/demo.test.js.snap index 4467d46442..578500ea1e 100644 --- a/components/badge/__tests__/__snapshots__/demo.test.js.snap +++ b/components/badge/__tests__/__snapshots__/demo.test.js.snap @@ -2211,6 +2211,349 @@ exports[`renders ./components/badge/demo/ribbon-debug.md correctly 1`] = ` `; +exports[`renders ./components/badge/demo/size.md correctly 1`] = ` +Array [ + + + + +

+ 0 +

+

+ 1 +

+

+ 2 +

+

+ 3 +

+

+ 4 +

+

+ 5 +

+

+ 6 +

+

+ 7 +

+

+ 8 +

+

+ 9 +

+

+ 0 +

+

+ 1 +

+

+ 2 +

+

+ 3 +

+

+ 4 +

+

+ 5 +

+

+ 6 +

+

+ 7 +

+

+ 8 +

+

+ 9 +

+

+ 0 +

+

+ 1 +

+

+ 2 +

+

+ 3 +

+

+ 4 +

+

+ 5 +

+

+ 6 +

+

+ 7 +

+

+ 8 +

+

+ 9 +

+
+
+
, + + + + +

+ 0 +

+

+ 1 +

+

+ 2 +

+

+ 3 +

+

+ 4 +

+

+ 5 +

+

+ 6 +

+

+ 7 +

+

+ 8 +

+

+ 9 +

+

+ 0 +

+

+ 1 +

+

+ 2 +

+

+ 3 +

+

+ 4 +

+

+ 5 +

+

+ 6 +

+

+ 7 +

+

+ 8 +

+

+ 9 +

+

+ 0 +

+

+ 1 +

+

+ 2 +

+

+ 3 +

+

+ 4 +

+

+ 5 +

+

+ 6 +

+

+ 7 +

+

+ 8 +

+

+ 9 +

+
+
+ , +] +`; + exports[`renders ./components/badge/demo/status.md correctly 1`] = `
`; +exports[`Badge render Badge size when contains children 1`] = ` +Array [ + + + + +

+ 0 +

+

+ 1 +

+

+ 2 +

+

+ 3 +

+

+ 4 +

+

+ 5 +

+

+ 6 +

+

+ 7 +

+

+ 8 +

+

+ 9 +

+

+ 0 +

+

+ 1 +

+

+ 2 +

+

+ 3 +

+

+ 4 +

+

+ 5 +

+

+ 6 +

+

+ 7 +

+

+ 8 +

+

+ 9 +

+

+ 0 +

+

+ 1 +

+

+ 2 +

+

+ 3 +

+

+ 4 +

+

+ 5 +

+

+ 6 +

+

+ 7 +

+

+ 8 +

+

+ 9 +

+
+
+ , + +
+ + +

+ 0 +

+

+ 1 +

+

+ 2 +

+

+ 3 +

+

+ 4 +

+

+ 5 +

+

+ 6 +

+

+ 7 +

+

+ 8 +

+

+ 9 +

+

+ 0 +

+

+ 1 +

+

+ 2 +

+

+ 3 +

+

+ 4 +

+

+ 5 +

+

+ 6 +

+

+ 7 +

+

+ 8 +

+

+ 9 +

+

+ 0 +

+

+ 1 +

+

+ 2 +

+

+ 3 +

+

+ 4 +

+

+ 5 +

+

+ 6 +

+

+ 7 +

+

+ 8 +

+

+ 9 +

+
+
+ , +] +`; + exports[`Badge render Badge status/color when contains children 1`] = ` Array [ { ); expect(wrapper).toMatchSnapshot(); }); + + it('render Badge size when contains children', () => { + const wrapper = render( + <> + +
+ + + + + , + ); + expect(wrapper).toMatchSnapshot(); + }); }); describe('Ribbon', () => { diff --git a/components/badge/demo/size.md b/components/badge/demo/size.md new file mode 100644 index 0000000000..e2d7af41c9 --- /dev/null +++ b/components/badge/demo/size.md @@ -0,0 +1,30 @@ +--- +order: 9 +title: + zh-CN: 大小 + en-US: Size +--- + +## zh-CN + +可以设置有数字徽标的大小。 + +## en-US + +Set size of numeral Badge. + +```jsx +import { Badge } from 'antd'; + +ReactDOM.render( + <> + + + + + + + , + mountNode, +); +``` diff --git a/components/badge/index.en-US.md b/components/badge/index.en-US.md index 1b85af8bf8..8035dcaa15 100644 --- a/components/badge/index.en-US.md +++ b/components/badge/index.en-US.md @@ -24,6 +24,7 @@ Badge normally appears in proximity to notifications or user avatars with eye-ca | overflowCount | Max count to show | number | 99 | | | showZero | Whether to show badge when `count` is zero | boolean | false | | | status | Set Badge as a status dot | `success` \| `processing` \| `default` \| `error` \| `warning` | - | | +| size | If `count` is set, `size` sets the size of badge | `default` \| `small` | - | 4.6.0 | | text | If `status` is set, `text` sets the display text of the status `dot` | ReactNode | - | | | title | Text to show when hovering over the badge | string | - | | diff --git a/components/badge/index.tsx b/components/badge/index.tsx index 9309b626e6..98f902b3b0 100644 --- a/components/badge/index.tsx +++ b/components/badge/index.tsx @@ -30,6 +30,7 @@ export interface BadgeProps { status?: PresetStatusColorType; color?: LiteralUnion; text?: React.ReactNode; + size?: 'default' | 'small'; offset?: [number | string, number | string]; title?: string; } @@ -44,6 +45,7 @@ const Badge: CompoundedComponent = ({ count = null, overflowCount = 99, dot = false, + size = 'default', title, offset, style, @@ -140,6 +142,7 @@ const Badge: CompoundedComponent = ({ const scrollNumberCls = classNames({ [`${prefixCls}-dot`]: bDot, [`${prefixCls}-count`]: !bDot, + [`${prefixCls}-count-sm`]: size === 'small', [`${prefixCls}-multiple-words`]: !bDot && count && count.toString && count.toString().length > 1, [`${prefixCls}-status-${status}`]: !!status, diff --git a/components/badge/index.zh-CN.md b/components/badge/index.zh-CN.md index 91d21cdd57..3aa267c533 100644 --- a/components/badge/index.zh-CN.md +++ b/components/badge/index.zh-CN.md @@ -25,6 +25,7 @@ cover: https://gw.alipayobjects.com/zos/antfincdn/6%26GF9WHwvY/Badge.svg | overflowCount | 展示封顶的数字值 | number | 99 | | | showZero | 当数值为 0 时,是否展示 Badge | boolean | false | | | status | 设置 Badge 为状态点 | `success` \| `processing` \| `default` \| `error` \| `warning` | - | | +| size | 在设置了 `count` 的前提下有效,设置小圆点的大小 | `default` \| `small` | - | 4.6.0 | | text | 在设置了 `status` 的前提下有效,设置状态点的文本 | ReactNode | - | | | title | 设置鼠标放在状态点上时显示的文字 | string | - | | diff --git a/components/badge/style/index.less b/components/badge/style/index.less index d86c22ec0e..ff91df5101 100644 --- a/components/badge/style/index.less +++ b/components/badge/style/index.less @@ -32,6 +32,15 @@ } } + &-count-sm { + min-width: @badge-height-sm; + height: @badge-height-sm; + padding: 0; + font-size: @badge-font-size-sm; + line-height: @badge-height-sm; + border-radius: @badge-height-sm / 2; + } + &-multiple-words { padding: 0 8px; } diff --git a/components/button/__tests__/__image_snapshots__/image-test-js-button-image-image-test-component-image-screenshot-should-correct-1-snap.png b/components/button/__tests__/__image_snapshots__/image-test-js-button-image-image-test-component-image-screenshot-should-correct-1-snap.png index 74dc898ee4..9254f31b0a 100644 Binary files a/components/button/__tests__/__image_snapshots__/image-test-js-button-image-image-test-component-image-screenshot-should-correct-1-snap.png and b/components/button/__tests__/__image_snapshots__/image-test-js-button-image-image-test-component-image-screenshot-should-correct-1-snap.png differ diff --git a/components/form/Form.tsx b/components/form/Form.tsx index 2f884d403c..2f35f82e72 100644 --- a/components/form/Form.tsx +++ b/components/form/Form.tsx @@ -12,7 +12,7 @@ import SizeContext, { SizeType, SizeContextProvider } from '../config-provider/S export type FormLayout = 'horizontal' | 'inline' | 'vertical'; -export interface FormProps extends Omit { +export interface FormProps extends Omit, 'form'> { prefixCls?: string; hideRequiredMark?: boolean; colon?: boolean; @@ -21,7 +21,7 @@ export interface FormProps extends Omit { labelAlign?: FormLabelAlign; labelCol?: ColProps; wrapperCol?: ColProps; - form?: FormInstance; + form?: FormInstance; size?: SizeType; scrollToFirstError?: boolean; } @@ -105,7 +105,9 @@ const InternalForm: React.ForwardRefRenderFunction = (props, ); }; -const Form = React.forwardRef(InternalForm); +const Form = React.forwardRef(InternalForm) as ( + props: React.PropsWithChildren> & { ref?: React.Ref> }, +) => React.ReactElement; export { useForm, List, FormInstance }; diff --git a/components/form/__tests__/type.test.tsx b/components/form/__tests__/type.test.tsx index 70806212c5..2f6c86c3f6 100644 --- a/components/form/__tests__/type.test.tsx +++ b/components/form/__tests__/type.test.tsx @@ -1,7 +1,12 @@ import * as React from 'react'; -import Form from '..'; +import Form, { FormInstance } from '..'; import Input from '../../input'; +interface FormValues { + username?: string; + path1?: { path2?: number }; +} + describe('Form.typescript', () => { it('Form.Item', () => { const form = ( @@ -14,4 +19,52 @@ describe('Form.typescript', () => { expect(form).toBeTruthy(); }); + + describe('generic', () => { + it('hooks', () => { + const Demo = () => { + const [form] = Form.useForm(); + + form.setFieldsValue({ path1: { path2: 2333 } }); + + return ( +
{ + expect(values).toBeTruthy(); + expect(values.username).toBeTruthy(); + expect(values.path1?.path2).toBeTruthy(); + }} + /> + ); + }; + + expect(Demo).toBeTruthy(); + }); + + it('ref', () => { + class Demo extends React.Component { + formRef = React.createRef>(); + + componentDidMount() { + this.formRef.current?.setFieldsValue({ path1: { path2: 233 } }); + } + + render() { + return ( + { + expect(values).toBeTruthy(); + expect(values.username).toBeTruthy(); + expect(values.path1?.path2).toBeTruthy(); + }} + /> + ); + } + } + + expect(Demo).toBeTruthy(); + }); + }); }); diff --git a/components/form/hooks/useForm.ts b/components/form/hooks/useForm.ts index 521d16eacd..a3ad814781 100644 --- a/components/form/hooks/useForm.ts +++ b/components/form/hooks/useForm.ts @@ -4,7 +4,7 @@ import scrollIntoView from 'scroll-into-view-if-needed'; import { ScrollOptions, NamePath, InternalNamePath } from '../interface'; import { toArray, getFieldId } from '../util'; -export interface FormInstance extends RcFormInstance { +export interface FormInstance extends RcFormInstance { scrollToField: (name: NamePath, options?: ScrollOptions) => void; /** This is an internal usage. Do not use in your prod */ __INTERNAL__: { @@ -21,11 +21,11 @@ function toNamePathStr(name: NamePath) { return namePath.join('_'); } -export default function useForm(form?: FormInstance): [FormInstance] { +export default function useForm(form?: FormInstance): [FormInstance] { const [rcForm] = useRcForm(); const itemsRef = useRef>({}); - const wrapForm: FormInstance = useMemo( + const wrapForm: FormInstance = useMemo( () => form || { ...rcForm, diff --git a/components/grid/__tests__/__image_snapshots__/image-test-js-grid-image-image-test-component-image-screenshot-should-correct-1-snap.png b/components/grid/__tests__/__image_snapshots__/image-test-js-grid-image-image-test-component-image-screenshot-should-correct-1-snap.png index db6056e214..fbb723ee81 100644 Binary files a/components/grid/__tests__/__image_snapshots__/image-test-js-grid-image-image-test-component-image-screenshot-should-correct-1-snap.png and b/components/grid/__tests__/__image_snapshots__/image-test-js-grid-image-image-test-component-image-screenshot-should-correct-1-snap.png differ diff --git a/components/input-number/index.en-US.md b/components/input-number/index.en-US.md index 6791dfa7d7..8d83b88c8f 100644 --- a/components/input-number/index.en-US.md +++ b/components/input-number/index.en-US.md @@ -18,6 +18,7 @@ When a numeric value needs to be provided. | autoFocus | If get focus when component mounted | boolean | false | | defaultValue | The initial value | number | - | | disabled | If disable the input | boolean | false | +| readOnly | If readonly the input | boolean | false | | formatter | Specifies the format of the value presented | function(value: number \| string): string | - | | max | The max value | number | [Number.MAX_SAFE_INTEGER](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER) | | min | The min value | number | [Number.MIN_SAFE_INTEGER](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_SAFE_INTEGER) | diff --git a/components/input-number/index.tsx b/components/input-number/index.tsx index 66cdcad85d..f54bd8c315 100644 --- a/components/input-number/index.tsx +++ b/components/input-number/index.tsx @@ -22,6 +22,7 @@ export interface InputNumberProps tabIndex?: number; onChange?: (value: number | string | undefined) => void; disabled?: boolean; + readOnly?: boolean; size?: SizeType; formatter?: (value: number | string | undefined) => string; parser?: (displayValue: string | undefined) => number | string; @@ -37,7 +38,13 @@ export interface InputNumberProps const InputNumber = React.forwardRef((props, ref) => { const renderInputNumber = ({ getPrefixCls, direction }: ConfigConsumerProps) => { - const { className, size: customizeSize, prefixCls: customizePrefixCls, ...others } = props; + const { + className, + size: customizeSize, + prefixCls: customizePrefixCls, + readOnly, + ...others + } = props; const prefixCls = getPrefixCls('input-number', customizePrefixCls); const upIcon = ; const downIcon = ; @@ -51,6 +58,7 @@ const InputNumber = React.forwardRef((props, ref) => [`${prefixCls}-lg`]: mergeSize === 'large', [`${prefixCls}-sm`]: mergeSize === 'small', [`${prefixCls}-rtl`]: direction === 'rtl', + [`${prefixCls}-readonly`]: readOnly, }, className, ); @@ -62,6 +70,7 @@ const InputNumber = React.forwardRef((props, ref) => upHandler={upIcon} downHandler={downIcon} prefixCls={prefixCls} + readOnly={readOnly} {...others} /> ); diff --git a/components/input-number/index.zh-CN.md b/components/input-number/index.zh-CN.md index d629489fa9..269c4a4a1e 100644 --- a/components/input-number/index.zh-CN.md +++ b/components/input-number/index.zh-CN.md @@ -21,6 +21,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/XOS8qZ0kU/InputNumber.svg | autoFocus | 自动获取焦点 | boolean | false | | defaultValue | 初始值 | number | - | | disabled | 禁用 | boolean | false | +| readOnly | 只读 | boolean | false | | formatter | 指定输入框展示值的格式 | function(value: number \| string): string | - | | max | 最大值 | number | [Number.MAX_SAFE_INTEGER](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER) | | min | 最小值 | number | [Number.MIN_SAFE_INTEGER](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_SAFE_INTEGER) | diff --git a/components/input-number/style/index.less b/components/input-number/style/index.less index 65fa198ae6..bc39acc2f9 100644 --- a/components/input-number/style/index.less +++ b/components/input-number/style/index.less @@ -72,6 +72,12 @@ } } + &-readonly { + .@{input-number-prefix-cls}-handler-wrap { + display: none; + } + } + &-input { width: 100%; height: @input-height-base - 2px; diff --git a/components/select/index.en-US.md b/components/select/index.en-US.md index 49cb1ba505..b302b7b915 100644 --- a/components/select/index.en-US.md +++ b/components/select/index.en-US.md @@ -60,6 +60,7 @@ Select component to select value from options. | virtual | Disable virtual scroll when set to false | boolean | true | 4.1.0 | | onBlur | Called when blur | function | - | | | onChange | Called when select an option or input value change | function(value, option:Option \| Array<Option>) | - | | +| onClear | Called when clear | function | - | 4.6.0 | | onDeselect | Called when a option is deselected, param is the selected option's value. Only called for multiple or tags, effective in multiple or tags mode only | function(string \| number \| LabeledValue) | - | | | onFocus | Called when focus | function | - | | | onInputKeyDown | Called when key pressed | function | - | | diff --git a/components/select/index.zh-CN.md b/components/select/index.zh-CN.md index 5ec6d9749b..9fb0f100cf 100644 --- a/components/select/index.zh-CN.md +++ b/components/select/index.zh-CN.md @@ -61,6 +61,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/_0XzgOis7/Select.svg | virtual | 设置 false 时关闭虚拟滚动 | boolean | true | 4.1.0 | | onBlur | 失去焦点时回调 | function | - | | | onChange | 选中 option,或 input 的 value 变化时,调用此函数 | function(value, option:Option \| Array<Option>) | - | | +| onClear | 清除内容时回调 | function | - | 4.6.0 | | onDeselect | 取消选中时调用,参数为选中项的 value (或 key) 值,仅在 multiple 或 tags 模式下生效 | function(string \| number \| LabeledValue) | - | | | onFocus | 获得焦点时回调 | function | - | | | onInputKeyDown | 按键按下时回调 | function | - | | diff --git a/components/style/mixins/typography.less b/components/style/mixins/typography.less index 8c74a3f0c1..71a4d39b06 100644 --- a/components/style/mixins/typography.less +++ b/components/style/mixins/typography.less @@ -47,3 +47,12 @@ @typography-title-margin-bottom ); } +.typography-title-5() { + .typography-title( + @heading-5-size, + @typography-title-font-weight, + 1.5, + @heading-color, + @typography-title-margin-bottom + ); +} diff --git a/components/style/themes/dark.less b/components/style/themes/dark.less index 55c59ec50d..271c5d213d 100644 --- a/components/style/themes/dark.less +++ b/components/style/themes/dark.less @@ -164,7 +164,7 @@ @body-background: @black; @component-background: #141414; -@text-color: fade(@white, 65%); +@text-color: fade(@white, 85%); @text-color-secondary: fade(@white, 45%); @text-color-inverse: @white; @icon-color-hover: fade(@white, 75%); diff --git a/components/style/themes/default.less b/components/style/themes/default.less index deca4764e5..b175ddda6c 100644 --- a/components/style/themes/default.less +++ b/components/style/themes/default.less @@ -50,7 +50,7 @@ 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; @code-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; -@text-color: fade(@black, 65%); +@text-color: fade(@black, 85%); @text-color-secondary: fade(@black, 45%); @text-color-inverse: @white; @icon-color: inherit; @@ -69,6 +69,7 @@ @heading-2-size: ceil(@font-size-base * 2.14); @heading-3-size: ceil(@font-size-base * 1.71); @heading-4-size: ceil(@font-size-base * 1.42); +@heading-5-size: ceil(@font-size-base * 1.14); // https://github.com/ant-design/ant-design/issues/20210 @line-height-base: 1.5715; @border-radius-base: 2px; @@ -651,8 +652,10 @@ // Badge // --- @badge-height: 20px; +@badge-height-sm: 14px; @badge-dot-size: 6px; @badge-font-size: @font-size-sm; +@badge-font-size-sm: @font-size-sm; @badge-font-weight: normal; @badge-status-size: 6px; @badge-text-color: @component-background; diff --git a/components/typography/Base.tsx b/components/typography/Base.tsx index 3c40b512a4..f65f49be85 100644 --- a/components/typography/Base.tsx +++ b/components/typography/Base.tsx @@ -8,6 +8,7 @@ import EditOutlined from '@ant-design/icons/EditOutlined'; import CheckOutlined from '@ant-design/icons/CheckOutlined'; import CopyOutlined from '@ant-design/icons/CopyOutlined'; import ResizeObserver from 'rc-resize-observer'; +import { AutoSizeType } from 'rc-textarea/lib/ResizableTextArea'; import { ConfigConsumerProps, configConsumerProps, ConfigContext } from '../config-provider'; import LocaleReceiver from '../locale-provider/LocaleReceiver'; import devWarning from '../_util/devWarning'; @@ -35,6 +36,8 @@ interface EditConfig { editing?: boolean; onStart?: () => void; onChange?: (value: string) => void; + maxLength?: number; + autoSize?: boolean | AutoSizeType; } interface EllipsisConfig { @@ -402,6 +405,7 @@ class Base extends React.Component { renderEditInput() { const { children, className, style } = this.props; const { direction } = this.context; + const { maxLength, autoSize } = this.getEditable(); return ( { className={className} style={style} direction={direction} + maxLength={maxLength} + autoSize={autoSize} /> ); } diff --git a/components/typography/Editable.tsx b/components/typography/Editable.tsx index a3dd70eaa8..c3af07bbde 100644 --- a/components/typography/Editable.tsx +++ b/components/typography/Editable.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import classNames from 'classnames'; import KeyCode from 'rc-util/lib/KeyCode'; import EnterOutlined from '@ant-design/icons/EnterOutlined'; +import { AutoSizeType } from 'rc-textarea/lib/ResizableTextArea'; import TextArea from '../input/TextArea'; interface EditableProps { @@ -13,6 +14,8 @@ interface EditableProps { className?: string; style?: React.CSSProperties; direction?: 'ltr' | 'rtl'; + maxLength?: number; + autoSize?: boolean | AutoSizeType; } interface EditableState { @@ -115,8 +118,15 @@ class Editable extends React.Component { render() { const { current } = this.state; - const { prefixCls, 'aria-label': ariaLabel, className, style, direction } = this.props; - + const { + prefixCls, + 'aria-label': ariaLabel, + className, + style, + direction, + maxLength, + autoSize, + } = this.props; const textAreaClassName = classNames(prefixCls, className, `${prefixCls}-edit-content`, { [`${prefixCls}-rtl`]: direction === 'rtl', }); @@ -124,6 +134,7 @@ class Editable extends React.Component {