(
src="https://mdn.alipayobjects.com/huamei_iwk9zp/afts/file/A*uYT7SZwhJnUAAAAAAAAAAAAADgCCAQ"
/>
),
- toolbarRender: (_, { icons: { closeIcon } }) => (
-
- ),
}}
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
/>
diff --git a/components/image/demo/toolbarRender.md b/components/image/demo/toolbarRender.md
index 5794885071..5b570567fc 100644
--- a/components/image/demo/toolbarRender.md
+++ b/components/image/demo/toolbarRender.md
@@ -1,7 +1,35 @@
## zh-CN
-可以自定义工具栏。
+可以自定义工具栏并添加下载图片按钮。
## en-US
-You can customize the toolbar.
+You can customize the toolbar and add a button for downloading the image.
+
+```css
+.toolbar-wrapper {
+ position: fixed;
+ bottom: 32px;
+ left: 50%;
+ padding: 0px 24px;
+ color: #fff;
+ font-size: 20px;
+ background-color: rgba(0, 0, 0, 0.1);
+ border-radius: 100px;
+ transform: translateX(-50%);
+}
+
+.toolbar-wrapper .anticon {
+ padding: 12px;
+ cursor: pointer;
+}
+
+.toolbar-wrapper .anticon[disabled] {
+ cursor: not-allowed;
+ opacity: 0.3;
+}
+
+.toolbar-wrapper .anticon:hover {
+ opacity: 0.3;
+}
+```
diff --git a/components/image/demo/toolbarRender.tsx b/components/image/demo/toolbarRender.tsx
index 92e968a6d4..5701b35841 100644
--- a/components/image/demo/toolbarRender.tsx
+++ b/components/image/demo/toolbarRender.tsx
@@ -1,5 +1,12 @@
-import { DownloadOutlined } from '@ant-design/icons';
-import { Image } from 'antd';
+import {
+ DownloadOutlined,
+ RotateLeftOutlined,
+ RotateRightOutlined,
+ SwapOutlined,
+ ZoomInOutlined,
+ ZoomOutOutlined,
+} from '@ant-design/icons';
+import { Image, Space } from 'antd';
import React from 'react';
const src = 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png';
@@ -12,7 +19,7 @@ const App: React.FC = () => {
const url = URL.createObjectURL(new Blob([blob]));
const link = document.createElement('a');
link.href = url;
- link.download = 'image.jpg';
+ link.download = 'image.png';
document.body.appendChild(link);
link.click();
URL.revokeObjectURL(url);
@@ -23,36 +30,26 @@ const App: React.FC = () => {
return (
(
-
- -
-
-
- {flipYIcon}
- {flipXIcon}
- {rotateLeftIcon}
- {rotateRightIcon}
- {zoomOutIcon}
- {zoomInIcon}
- {closeIcon}
-
+
+
+
+
+
+
+
+
+
),
}}
- src={src}
/>
);
};
diff --git a/components/image/index.en-US.md b/components/image/index.en-US.md
index 34b9d89221..4d0c412a41 100644
--- a/components/image/index.en-US.md
+++ b/components/image/index.en-US.md
@@ -40,7 +40,7 @@ Previewable image.
| fallback | Load failure fault-tolerant src | string | - | 4.6.0 |
| height | Image height | string \| number | - | 4.6.0 |
| placeholder | Load placeholder, use default placeholder when set `true` | ReactNode | - | 4.6.0 |
-| preview | preview config, disabled when `false` | boolean \| [PreviewType](#PreviewType) | true | 4.6.0 [PreviewType](#PreviewType):4.7.0 |
+| preview | preview config, disabled when `false` | boolean \| [PreviewType](#previewtype) | true | 4.6.0 [PreviewType](#previewtype):4.7.0 |
| src | Image path | string | - | 4.6.0 |
| width | Image width | string \| number | - | 4.6.0 |
| onError | Load failed callback | (event: Event) => void | - | 4.12.0 |
@@ -61,18 +61,20 @@ Other attributes [<img>](https://developer.mozilla.org/en-US/docs/Web/HTML/El
| scaleStep | `1 + scaleStep` is the step to increase or decrease the scale | number | 0.5 | - |
| minScale | Min scale | number | 1 | 5.7.0 |
| maxScale | Max scale | number | 50 | 5.7.0 |
+| closeIcon | Custom close icon | React.ReactNode | - | 5.7.0 |
| forceRender | Force render preview dialog | boolean | - | - |
-| toolbarRender | Custom toolbar render | (originalNode: React.ReactNode, info: Omit<[ToolbarRenderInfoType](#ToolbarRenderInfoType), 'current' \| 'total'>) => React.ReactNode | - | 5.7.0 |
-| imageRender | Custom preview content | (originalNode: React.ReactNode, info: { transform: [TransformType](#TransformType) }) => React.ReactNode | - | 5.7.0 |
-| onTransform | Callback when the transform of image changed | { transform: [TransformType](#TransformType), action: [TransformAction](#TransformAction) } | - | 5.7.0 |
+| toolbarRender | Custom toolbar render | (originalNode: React.ReactNode, info: Omit<[ToolbarRenderInfoType](#toolbarrenderinfotype), 'current' \| 'total'>) => React.ReactNode | - | 5.7.0 |
+| imageRender | Custom preview content | (originalNode: React.ReactNode, info: { transform: [TransformType](#transformtype) }) => React.ReactNode | - | 5.7.0 |
+| onTransform | Callback when the transform of image changed | { transform: [TransformType](#transformtype), action: [TransformAction](#transformaction) } | - | 5.7.0 |
| onVisibleChange | Callback when `visible` changed | (visible: boolean, prevVisible: boolean) => void | - | - |
## PreviewGroup
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
-| preview | Preview config, `disabled` when false | boolean \| [PreviewGroupType](#PreviewGroupType) | true | 4.6.0 [PreviewGroupType](#PreviewGroupType):4.7.0 |
+| preview | Preview config, `disabled` when false | boolean \| [PreviewGroupType](#previewgrouptype) | true | 4.6.0 [PreviewGroupType](#previewgrouptype):4.7.0 |
| items | Preview items | string[] \| { src: string, crossOrigin: string, ... }[] | - | 5.7.0 |
+| fallback | Load failure fault-tolerant src | string | - | 5.7.0 |
### PreviewGroupType
@@ -87,11 +89,12 @@ Other attributes [<img>](https://developer.mozilla.org/en-US/docs/Web/HTML/El
| scaleStep | `1 + scaleStep` is the step to increase or decrease the scale | number | 0.5 | - |
| minScale | Min scale | number | 1 | 5.7.0 |
| maxScale | Max scale | number | 50 | 5.7.0 |
+| closeIcon | Custom close icon | React.ReactNode | - | 5.7.0 |
| forceRender | Force render preview dialog | boolean | - | - |
-| countRender | Custom preview count content | (current: number, total: number) => string | - | 4.20.0 |
-| toolbarRender | Custom toolbar render | (originalNode: React.ReactNode, info: [ToolbarRenderInfoType](#ToolbarRenderInfoType)) => React.ReactNode | - | 5.7.0 |
-| imageRender | Custom preview content | (originalNode: React.ReactNode, info: { transform: [TransformType](#TransformType), current: number }) => React.ReactNode | - | 5.7.0 |
-| onTransform | Callback when the transform of image changed | { transform: [TransformType](#TransformType), action: [TransformAction](#TransformAction) } | - | 5.7.0 |
+| countRender | Custom preview count content | (current: number, total: number) => React.ReactNode | - | 4.20.0 |
+| toolbarRender | Custom toolbar render | (originalNode: React.ReactNode, info: [ToolbarRenderInfoType](#toolbarrenderinfotype)) => React.ReactNode | - | 5.7.0 |
+| imageRender | Custom preview content | (originalNode: React.ReactNode, info: { transform: [TransformType](#transformtype), current: number }) => React.ReactNode | - | 5.7.0 |
+| onTransform | Callback when the transform of image changed | { transform: [TransformType](#transformtype), action: [TransformAction](#transformaction) } | - | 5.7.0 |
| onChange | Callback when switch preview image | (current: number, prevCurrent: number) => void | - | 5.3.0 |
| onVisibleChange | Callback when `visible` changed | (visible: boolean, prevVisible: boolean, current: number) => void | - | current 参数 5.3.0 |
@@ -140,7 +143,6 @@ type TransformAction =
rotateRightIcon: React.ReactNode;
zoomOutIcon: React.ReactNode;
zoomInIcon: React.ReactNode;
- closeIcon: React.ReactNode;
};
actions: {
onFlipY: () => void;
@@ -149,7 +151,6 @@ type TransformAction =
onRotateRight: () => void;
onZoomOut: () => void;
onZoomIn: () => void;
- onClose: () => void;
};
transform: TransformType,
current: number;
diff --git a/components/image/index.zh-CN.md b/components/image/index.zh-CN.md
index 84236c5bfe..dd4111d240 100644
--- a/components/image/index.zh-CN.md
+++ b/components/image/index.zh-CN.md
@@ -41,7 +41,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*LVQ3R5JjjJEAAA
| fallback | 加载失败容错地址 | string | - | 4.6.0 |
| height | 图像高度 | string \| number | - | 4.6.0 |
| placeholder | 加载占位, 为 `true` 时使用默认占位 | ReactNode | - | 4.6.0 |
-| preview | 预览参数,为 `false` 时禁用 | boolean \| [PreviewType](#PreviewType) | true | 4.6.0 [PreviewType](#PreviewType):4.7.0 |
+| preview | 预览参数,为 `false` 时禁用 | boolean \| [PreviewType](#previewtype) | true | 4.6.0 [PreviewType](#previewyype):4.7.0 |
| src | 图片地址 | string | - | 4.6.0 |
| width | 图像宽度 | string \| number | - | 4.6.0 |
| onError | 加载错误回调 | (event: Event) => void | - | 4.12.0 |
@@ -62,18 +62,20 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*LVQ3R5JjjJEAAA
| scaleStep | `1 + scaleStep` 为缩放放大的每步倍数 | number | 0.5 | - |
| minScale | 最小缩放倍数 | number | 1 | 5.7.0 |
| maxScale | 最大放大倍数 | number | 50 | 5.7.0 |
+| closeIcon | 自定义关闭 Icon | React.ReactNode | - | 5.7.0 |
| forceRender | 强制渲染预览图 | boolean | - | - |
-| toolbarRender | 自定义工具栏 | (originalNode: React.ReactNode, info: Omit<[ToolbarRenderInfoType](#ToolbarRenderInfoType), 'current' \| 'total'>) => React.ReactNode | - | 5.7.0 |
-| imageRender | 自定义预览内容 | (originalNode: React.ReactNode, info: { transform: [TransformType](#TransformType) }) => React.ReactNode | - | 5.7.0 |
-| onTransform | 预览图 transform 变化的回调 | { transform: [TransformType](#TransformType), action: [TransformAction](#TransformAction) } | - | 5.7.0 |
+| toolbarRender | 自定义工具栏 | (originalNode: React.ReactNode, info: Omit<[ToolbarRenderInfoType](#toolbarrenderinfotype), 'current' \| 'total'>) => React.ReactNode | - | 5.7.0 |
+| imageRender | 自定义预览内容 | (originalNode: React.ReactNode, info: { transform: [TransformType](#transformtype) }) => React.ReactNode | - | 5.7.0 |
+| onTransform | 预览图 transform 变化的回调 | { transform: [TransformType](#transformtype), action: [TransformAction](#transformaction) } | - | 5.7.0 |
| onVisibleChange | 当 `visible` 发生改变时的回调 | (visible: boolean, prevVisible: boolean) => void | - | - |
## PreviewGroup
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
-| preview | 预览参数,为 `false` 时禁用 | boolean \| [PreviewGroupType](#PreviewGroupType) | true | 4.6.0 [PreviewGroupType](#PreviewGroupType):4.7.0 |
+| preview | 预览参数,为 `false` 时禁用 | boolean \| [PreviewGroupType](#previewgrouptype) | true | 4.6.0 [PreviewGroupType](#previewgrouptype):4.7.0 |
| items | 预览数组 | string[] \| { src: string, crossOrigin: string, ... }[] | - | 5.7.0 |
+| fallback | 加载失败容错地址 | string | - | 5.7.0 |
### PreviewGroupType
@@ -88,11 +90,12 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*LVQ3R5JjjJEAAA
| scaleStep | `1 + scaleStep` 为缩放放大的每步倍数 | number | 0.5 | - |
| minScale | 最小缩放倍数 | number | 1 | 5.7.0 |
| maxScale | 最大放大倍数 | number | 50 | 5.7.0 |
+| closeIcon | 自定义关闭 Icon | React.ReactNode | - | 5.7.0 |
| forceRender | 强制渲染预览图 | boolean | - | - |
-| countRender | 自定义预览计数内容 | (current: number, total: number) => string | - | 4.20.0 |
-| toolbarRender | 自定义工具栏 | (originalNode: React.ReactNode, info: [ToolbarRenderInfoType](#ToolbarRenderInfoType)) => React.ReactNode | - | 5.7.0 |
-| imageRender | 自定义预览内容 | (originalNode: React.ReactNode, info: { transform: [TransformType](#TransformType), current: number }) => React.ReactNode | - | 5.7.0 |
-| onTransform | 预览图 transform 变化的回调 | { transform: [TransformType](#TransformType), action: [TransformAction](#TransformAction) } | - | 5.7.0 |
+| countRender | 自定义预览计数内容 | (current: number, total: number) => React.ReactNode | - | 4.20.0 |
+| toolbarRender | 自定义工具栏 | (originalNode: React.ReactNode, info: [ToolbarRenderInfoType](#toolbarrenderinfotype)) => React.ReactNode | - | 5.7.0 |
+| imageRender | 自定义预览内容 | (originalNode: React.ReactNode, info: { transform: [TransformType](#transformtype), current: number }) => React.ReactNode | - | 5.7.0 |
+| onTransform | 预览图 transform 变化的回调 | { transform: [TransformType](#transformtype), action: [TransformAction](#transformaction) } | - | 5.7.0 |
| onChange | 切换预览图的回调 | (current: number, prevCurrent: number) => void | - | 5.3.0 |
| onVisibleChange | 当 `visible` 发生改变时的回调 | (visible: boolean, prevVisible: boolean, current: number) => void | - | current 参数 5.3.0 |
@@ -141,7 +144,6 @@ type TransformAction =
rotateRightIcon: React.ReactNode;
zoomOutIcon: React.ReactNode;
zoomInIcon: React.ReactNode;
- closeIcon: React.ReactNode;
};
actions: {
onFlipY: () => void;
@@ -150,7 +152,6 @@ type TransformAction =
onRotateRight: () => void;
onZoomOut: () => void;
onZoomIn: () => void;
- onClose: () => void;
};
transform: TransformType,
current: number;
diff --git a/components/image/style/index.ts b/components/image/style/index.ts
index 932a9a6585..16d3e9b6cb 100644
--- a/components/image/style/index.ts
+++ b/components/image/style/index.ts
@@ -1,7 +1,7 @@
import type { CSSObject } from '@ant-design/cssinjs';
import { TinyColor } from '@ctrl/tinycolor';
import { genModalMaskStyle } from '../../modal/style';
-import { resetComponent, textEllipsis } from '../../style';
+import { textEllipsis } from '../../style';
import { initFadeMotion, initZoomMotion } from '../../style/motion';
import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
@@ -43,14 +43,15 @@ export const genBoxStyle = (position?: PositionType): CSSObject => ({
});
export const genImageMaskStyle = (token: ImageToken): CSSObject => {
- const { iconCls, motionDurationSlow, paddingXXS, marginXXS, prefixCls } = token;
+ const { iconCls, motionDurationSlow, paddingXXS, marginXXS, prefixCls, colorTextLightSolid } =
+ token;
return {
position: 'absolute',
inset: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
- color: '#fff',
+ color: colorTextLightSolid,
background: new TinyColor('#000').setAlpha(0.5).toRgbString(),
cursor: 'pointer',
opacity: 0,
@@ -70,22 +71,70 @@ export const genImageMaskStyle = (token: ImageToken): CSSObject => {
};
export const genPreviewOperationsStyle = (token: ImageToken): CSSObject => {
- const { previewCls, modalMaskBg, paddingSM, previewOperationColorDisabled, motionDurationSlow } =
- token;
+ const {
+ previewCls,
+ modalMaskBg,
+ paddingSM,
+ marginXL,
+ margin,
+ paddingLG,
+ previewOperationColorDisabled,
+ motionDurationSlow,
+ iconCls,
+ colorTextLightSolid,
+ } = token;
const operationBg = new TinyColor(modalMaskBg).setAlpha(0.1);
const operationBgHover = operationBg.clone().setAlpha(0.2);
return {
- [`${previewCls}-operations`]: {
- ...resetComponent(token),
+ [`${previewCls}-footer`]: {
+ position: 'fixed',
+ bottom: marginXL,
+ left: {
+ _skip_check_: true,
+ value: 0,
+ },
+ width: '100%',
display: 'flex',
- justifyContent: 'flex-end',
+ flexDirection: 'column',
alignItems: 'center',
color: token.previewOperationColor,
- listStyle: 'none',
- background: operationBg.toRgbString(),
- pointerEvents: 'auto',
+ },
+ [`${previewCls}-progress`]: {
+ marginBottom: margin,
+ },
+ [`${previewCls}-close`]: {
+ position: 'fixed',
+ top: marginXL,
+ right: {
+ _skip_check_: true,
+ value: marginXL,
+ },
+ display: 'flex',
+ color: colorTextLightSolid,
+ backgroundColor: operationBg.toRgbString(),
+ borderRadius: '50%',
+ padding: paddingSM,
+ outline: 0,
+ border: 0,
+ cursor: 'pointer',
+ transition: `all ${motionDurationSlow}`,
+
+ '&:hover': {
+ backgroundColor: operationBgHover.toRgbString(),
+ },
+
+ [`& > ${iconCls}`]: {
+ fontSize: token.previewOperationSize,
+ },
+ },
+ [`${previewCls}-operations`]: {
+ display: 'flex',
+ alignItems: 'center',
+ padding: `0 ${paddingLG}px`,
+ backgroundColor: operationBg.toRgbString(),
+ borderRadius: 100,
'&-operation': {
marginInlineStart: paddingSM,
@@ -94,28 +143,22 @@ export const genPreviewOperationsStyle = (token: ImageToken): CSSObject => {
transition: `all ${motionDurationSlow}`,
userSelect: 'none',
- '&:hover': {
- background: operationBgHover.toRgbString(),
+ [`&:not(${previewCls}-operations-operation-disabled):hover > ${iconCls}`]: {
+ opacity: 0.3,
},
'&-disabled': {
color: previewOperationColorDisabled,
- pointerEvents: 'none',
+ cursor: 'not-allowed',
},
'&:first-of-type': {
marginInlineStart: 0,
},
- },
- '&-progress': {
- position: 'absolute',
- left: { _skip_check_: true, value: '50%' },
- transform: 'translateX(-50%)',
- },
-
- '&-icon': {
- fontSize: token.previewOperationSize,
+ [`& > ${iconCls}`]: {
+ fontSize: token.previewOperationSize,
+ },
},
},
};
@@ -151,7 +194,6 @@ export const genPreviewSwitchStyle = (token: ImageToken): CSSObject => {
transform: `translateY(-50%)`,
cursor: 'pointer',
transition: `all ${motionDurationSlow}`,
- pointerEvents: 'auto',
userSelect: 'none',
'&:hover': {
@@ -202,7 +244,7 @@ export const genImagePreviewStyle: GenerateStyle = (token: ImageToke
[`${previewCls}-img`]: {
maxWidth: '100%',
- maxHeight: '100%',
+ maxHeight: '70%',
verticalAlign: 'middle',
transform: 'scale3d(1, 1, 1)',
cursor: 'grab',
@@ -258,10 +300,7 @@ export const genImagePreviewStyle: GenerateStyle = (token: ImageToke
{
[`${componentCls}-preview-operations-wrapper`]: {
position: 'fixed',
- insetBlockStart: 0,
- insetInlineEnd: 0,
zIndex: token.zIndexPopup + 1,
- width: '100%',
},
'&': [genPreviewOperationsStyle(token), genPreviewSwitchStyle(token)],
},
diff --git a/package.json b/package.json
index a9f51dd7cb..8bcd89af9b 100644
--- a/package.json
+++ b/package.json
@@ -129,7 +129,7 @@
"rc-drawer": "~6.2.0",
"rc-dropdown": "~4.1.0",
"rc-field-form": "~1.34.0",
- "rc-image": "~6.1.0",
+ "rc-image": "~7.0.0-2",
"rc-input": "~1.1.0",
"rc-input-number": "~8.0.0",
"rc-mentions": "~2.5.0",
From 890316fc355ad9be451c34387504395c71691101 Mon Sep 17 00:00:00 2001
From: lijianan <574980606@qq.com>
Date: Fri, 7 Jul 2023 13:34:51 +0800
Subject: [PATCH 30/80] type: replace unknown with CheckboxRef (#43424)
---
components/checkbox/Checkbox.tsx | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/components/checkbox/Checkbox.tsx b/components/checkbox/Checkbox.tsx
index f35c41b25a..adbe5a2b32 100644
--- a/components/checkbox/Checkbox.tsx
+++ b/components/checkbox/Checkbox.tsx
@@ -151,7 +151,8 @@ const InternalCheckbox: React.ForwardRefRenderFunction(InternalCheckbox);
+const Checkbox = React.forwardRef(InternalCheckbox);
+
if (process.env.NODE_ENV !== 'production') {
Checkbox.displayName = 'Checkbox';
}
From a4d01a5a170b5f57a3cd896827c765e71721f0af Mon Sep 17 00:00:00 2001
From: thinkasany <117748716+thinkasany@users.noreply.github.com>
Date: Fri, 7 Jul 2023 15:00:15 +0800
Subject: [PATCH 31/80] type(select): supplementary mode type (#43413)
---
components/select/index.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/components/select/index.tsx b/components/select/index.tsx
index 9eed691872..40b21c0490 100755
--- a/components/select/index.tsx
+++ b/components/select/index.tsx
@@ -43,7 +43,7 @@ export interface InternalSelectProps<
suffixIcon?: React.ReactNode;
size?: SizeType;
disabled?: boolean;
- mode?: 'multiple' | 'tags' | 'SECRET_COMBOBOX_MODE_DO_NOT_USE';
+ mode?: 'multiple' | 'tags' | 'SECRET_COMBOBOX_MODE_DO_NOT_USE' | 'combobox';
bordered?: boolean;
}
@@ -118,7 +118,7 @@ const InternalSelect = <
const mode = React.useMemo(() => {
const { mode: m } = props as InternalSelectProps;
- if ((m as any) === 'combobox') {
+ if (m === 'combobox') {
return undefined;
}
From e36d50f379b11bbed8e6df4ffcdad66c1c10191a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E7=88=B1=E5=90=83=E7=99=BD=E8=90=9D?=
=?UTF-8?q?=E5=8D=9C?=
Date: Fri, 7 Jul 2023 17:21:54 +0800
Subject: [PATCH 32/80] docs: blog of suspense breaks styles (#43421)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* docs: Suspense breaks styles
* Update docs/blog/suspense.en-US.md
Co-authored-by: lijianan <574980606@qq.com>
Signed-off-by: 二货爱吃白萝卜
* Update docs/blog/suspense.zh-CN.md
Co-authored-by: lijianan <574980606@qq.com>
Signed-off-by: 二货爱吃白萝卜
* docs: clean up
---------
Signed-off-by: 二货爱吃白萝卜
Co-authored-by: lijianan <574980606@qq.com>
---
docs/blog/suspense.en-US.md | 182 ++++++++++++++++++++++++++++++++++++
docs/blog/suspense.zh-CN.md | 180 +++++++++++++++++++++++++++++++++++
2 files changed, 362 insertions(+)
create mode 100644 docs/blog/suspense.en-US.md
create mode 100644 docs/blog/suspense.zh-CN.md
diff --git a/docs/blog/suspense.en-US.md b/docs/blog/suspense.en-US.md
new file mode 100644
index 0000000000..26b9d96ce0
--- /dev/null
+++ b/docs/blog/suspense.en-US.md
@@ -0,0 +1,182 @@
+---
+title: Suspense breaks styles
+date: 2023-07-07
+author: zombieJ
+---
+
+We know that React 18 provides a `useInsertionEffect` hooks specifically for CSS-IN-JS, which has a faster timing priority than `useLayoutEffect`, so that the order of calls will not be affected by the order of writing:
+
+```tsx
+useLayoutEffect(() => {
+ console.log('layout effect');
+}, []);
+
+useInsertionEffect(() => {
+ console.log('insertion effect');
+}, []);
+
+// Console:
+// - insertion effect
+// - layout effect
+```
+
+In early `@ant-design/cssinjs` implementation, we did not choose `useInsertionEffect` because we needed to be compatible with React 17 version, but simulated the effect of inserting in advance by adding styles in the render phase:
+
+```tsx
+// pseudocode. Not used in real world
+function useStyleInsertion(hash: string, counter: Record) {
+ useMemo(() => {
+ if (!counter[hash]) {
+ // Insert only when current style not inserted
+ }
+
+ counter[hash] += 1;
+ }, [hash]);
+
+ useEffect(
+ () => () => {
+ counter[hash] -= 1;
+
+ if (!counter[hash]) {
+ // Remove if set to clear on destroy
+ }
+ },
+ [hash],
+ );
+}
+```
+
+Above code will count the usage of styles, if the current style has not been inserted, it will insert the style in the render phase. Similarly, if the current style is configured to be unloaded when it is not in use, it will be cleared after the effect confirms the count. In addition, there is a similar logic that listens for changes in tokens, and when there are multiple tokens, it will clear all styles `` corresponding to tokens that are no longer in use to avoid memory leaks caused by too many theme switches.
+
+These code can run perfectly in React 17, and also run very well in React 18's StrictMode. `counter` always appears and disappears in pairs. But under Suspense, it may have problems.
+
+## StrictMode
+
+The StrictMode of React 18 is different from [React 17](https://17.reactjs.org/docs/strict-mode.html) in that it will be called multiple times in each phase to ensure that developers clean up the Effect:
+
+````tsx
+
+```tsx
+const My = () => {
+ console.log('render');
+
+ useMemo(() => {
+ console.log('memo');
+ }, []);
+
+ useEffect(() => {
+ console.log('effect');
+
+ return () => {
+ console.log('effect cleanup');
+ };
+ }, []);
+};
+
+
+
+;
+
+// Console:
+// - render
+// - memo
+// - render
+// - memo
+// - effect
+// - effect cleanup
+// - effect
+````
+
+With above sample, we can know that `counter` in StrictMode will be accumulated, but the final value will be correct (that is, each component will only be counted once):
+
+- memo: 1
+- memo: 2
+- effect cleanup: 1
+
+But StrictMode is just a simulation of Suspense. In the real scenario, the number of executions is not guaranteed to appear in pairs.
+
+## Suspense
+
+We use [umi](https://github.com/umijs/umi) for site development, which is split by page and loaded on demand by default. Display the loading state during the loading process through Suspense:
+
+```tsx
+
+
+ } />
+
+
+```
+
+When switching pages, there is a chance that some styles will be lost when the page is switched back and forth:
+
+
+
+Part of the style lost in Page 1 is the style unique to Page 1 (some tokens are customized through ConfigProvider), and the style of Page 2 is the style common to Page 1 and Page 2.
+
+With the style management logic we introduced at the beginning, Page 1 will be cleared all styles `` corresponding to the token when Page 2 is rendered because it has styles corresponding to the independent token. This looks as expected, so the problem is that the style is not re-inserted when switching back to Page 1.
+
+### Wrong Counter
+
+With a series of breakpoints, we found that this problem is caused by the asynchronous nature of Suspense. It will call the component multiple times during the loading process, so the timing of the component style registration will also be called multiple times. And since our counter is in the render phase, the counter will be called multiple times under Suspense, which will cause the value of the counter to be incorrect:
+
+- render: 0
+- useMemo: 1
+- render: 1
+- useMemo: 2
+- effect: 2
+- Not like StrictMode, effect is not executed again, so effect cleanup will not be executed
+
+Counter is not synchronized, so the token manager thinks that the style is no longer in use, so it performs batch cleaning, while the component style manager thinks that other components are still in use, so when re-entering Page 1, the style will not be re-inserted.
+
+## useInsertionEffect
+
+Obviously, due to its characteristics, we cannot use `useMemo` as a counter, it will not appear in pairs with `useEffect`. So we consider using `useInsertionEffect` to insert styles:
+
+```tsx
+// pseudocode. Not used in real world
+useInsertionEffect(() => {
+ if (!counter[hash]) {
+ // Insert only when current style not inserted
+ }
+ counter[hash] += 1;
+
+ return () => {
+ counter[hash] -= 1;
+
+ if (!counter[hash]) {
+ // Remove if set to clear on destroy
+ }
+ };
+}, [hash]);
+```
+
+And for React 17 version, it is downgraded to `useLayoutEffect`:
+
+```tsx
+const useMergedInsertionEffect = useInsertionEffect || useLayoutEffect;
+
+useMergedInsertionEffect(() => {
+ // Same as above
+}, [hash]);
+```
+
+With this modification, we found that React 17's CI was failed. After checking, we found that `useLayoutEffect` will have a timing problem:
+
+```tsx
+// Some logic measure dom size
+useLayoutEffect(() => {
+ // This is not correct since style is not applied
+ const { clientHeight } = nodeRef.current;
+}, []);
+
+// Inject style
+useLayoutEffect(() => {
+ // ...
+}, [hash]);
+```
+
+Measure logic in `useLayoutEffect` is executed before injecting style, resulting in incorrect size information. It can also be predicted that this will have an impact on developers. So we have to compromise, and in React 17 version, it will be downgraded to the original `useMemo` insertion.
+
+## Summary
+
+Suspense brings rendering performance improvements, but it also makes timing very important. It is not the best way to only 'work on' StrictMode. Different logic is used for different React versions is not good choice since it will have timing problem. `render` will trigger from parent node to child node in turn, while `useInsertionEffect` is the opposite. However, from the perspective of antd, the component styles are independent of each other, so this problem will not affect us.
diff --git a/docs/blog/suspense.zh-CN.md b/docs/blog/suspense.zh-CN.md
new file mode 100644
index 0000000000..27d84129fb
--- /dev/null
+++ b/docs/blog/suspense.zh-CN.md
@@ -0,0 +1,180 @@
+---
+title: Suspense 引发的样式丢失问题
+date: 2023-07-07
+author: zombieJ
+---
+
+我们知道,React 18 提供了一个专门为 CSS-IN-JS 使用的 `useInsertionEffect` hooks,它会比 `useLayoutEffect` 拥有更快的时序优先级,从而保证不会因为书写顺序而影响调用顺序的问题:
+
+```tsx
+useLayoutEffect(() => {
+ console.log('layout effect');
+}, []);
+
+useInsertionEffect(() => {
+ console.log('insertion effect');
+}, []);
+
+// Console:
+// - insertion effect
+// - layout effect
+```
+
+在早期 `@ant-design/cssinjs` 实现中,由于需要兼容 React 17 版本,我们并没有选择 `useInsertionEffect`,而是通过在 render 阶段添加样式的方式来模拟提前插入的效果:
+
+```tsx
+// pseudocode. Not used in real world
+function useStyleInsertion(hash: string, counter: Record) {
+ useMemo(() => {
+ if (!counter[hash]) {
+ // Insert only when current style not inserted
+ }
+
+ counter[hash] += 1;
+ }, [hash]);
+
+ useEffect(
+ () => () => {
+ counter[hash] -= 1;
+
+ if (!counter[hash]) {
+ // Remove if set to clear on destroy
+ }
+ },
+ [hash],
+ );
+}
+```
+
+以上代码会对使用样式进行统计,如果发现当前样式没有被插入过,就会在 render 阶段插入样式,否则就不会插入。同样的,如果发现当前样式配置了未使用时卸载,则会在 effect 确认计数后清除。此外,还有一套类似的代码会监听 token 的变化,当存在多份 token 时会对不再使用的 token 对应的所有样式 `` 进行清理,以避免过多的主题切换导致的内存泄漏。
+
+这段代码在 React 17 可以完美运行,在 React 18 的 StrictMode 下也运行的十分正常。`counter` 总是成对出现与消失。但是它在 Suspense 下,就会有概率出现问题了。
+
+## StrictMode
+
+React 18 的 StrictMode 和 [React 17](https://17.reactjs.org/docs/strict-mode.html)不同的是,它会在各个阶段进行多次调用,从而确保开发者对 Effect 进行了清理:
+
+```tsx
+const My = () => {
+ console.log('render');
+
+ useMemo(() => {
+ console.log('memo');
+ }, []);
+
+ useEffect(() => {
+ console.log('effect');
+
+ return () => {
+ console.log('effect cleanup');
+ };
+ }, []);
+};
+
+
+
+;
+
+// Console:
+// - render
+// - memo
+// - render
+// - memo
+// - effect
+// - effect cleanup
+// - effect
+```
+
+从上面的例子可以知道,`counter` 在 StrictMode 虽然会累加,但是最终会是正确的值(即每个组件只计算 1 次统计):
+
+- memo: 1
+- memo: 2
+- effect cleanup: 1
+
+但是 StrictMode 只是对 Suspense 的模拟。在真实场景下,执行次数并不会保证成对出现。
+
+## Suspense
+
+我们使用 [umi](https://github.com/umijs/umi) 进行站点开发,它默认按页拆包、按需加载。通过 Suspense 的方式在加载过程中显示 loading 状态:
+
+```tsx
+
+
+ } />
+
+
+```
+
+在页面切换时,偶发出现页面往复切换时部分样式丢失的情况:
+
+
+
+其中 Page 1 丢失部分的样式为 Page 1 独有的样式(通过 ConfigProvider 定制了一些 token),而 Page 2 的样式则为 Page 1 与 Page 2 通用的样式。
+
+在我们最初介绍的样式管理逻辑可以明白,Page 1 由于为独立 token 对应的样式,因而在 Page 2 渲染时会被清理掉所有的对应 token 的样式 ``。这看起来是符合预期的,那么问题就出在了切回 Page 1 时样式没有被重新插入。
+
+### 计数器错误
+
+在经过一系列断点后,我们发现这个问题出现在计数器不同步之上。由于 Suspense 的特性,它会在加载过程中多次调用组件,所以组件样式注册的时机也会被调用多次。而由于我们的计数器是在 render 阶段进行的,所以在 Suspense 下,计数器会被多次调用,从而导致计数器的值不正确:
+
+- render: 0
+- useMemo: 1
+- render: 1
+- useMemo: 2
+- effect: 2
+- 不像 StrictMode,effect 并没有再次执行,所以 effect cleanup 也不会执行
+
+计数器不同步导致 token 层面已经认为样式已经没有再使用所以进行了批量清理,而在组件样式层面则认为还有其他组件在使用,所以当重新进入 Page 1 时并不会重新插入样式。
+
+## useInsertionEffect
+
+显而易见,Suspense 由于其特性,我们不能通过 `useMemo` 来做计数器,它不会和 `useEffect` 成对出现。所以我们考虑需要使用 `useInsertionEffect` 来进行样式的插入:
+
+```tsx
+// pseudocode. Not used in real world
+useInsertionEffect(() => {
+ if (!counter[hash]) {
+ // Insert only when current style not inserted
+ }
+ counter[hash] += 1;
+
+ return () => {
+ counter[hash] -= 1;
+
+ if (!counter[hash]) {
+ // Remove if set to clear on destroy
+ }
+ };
+}, [hash]);
+```
+
+而对于 React 17 版本,则降级为 `useLayoutEffect`:
+
+```tsx
+const useMergedInsertionEffect = useInsertionEffect || useLayoutEffect;
+
+useMergedInsertionEffect(() => {
+ // Same as above
+}, [hash]);
+```
+
+经过这样的修改后,我们发现 React 17 的 CI 挂了。在检查后,发现 `useLayoutEffect` 就会出现时序问题:
+
+```tsx
+// Some logic measure dom size
+useLayoutEffect(() => {
+ // This is not correct since style is not applied
+ const { clientHeight } = nodeRef.current;
+}, []);
+
+// Inject style
+useLayoutEffect(() => {
+ // ...
+}, [hash]);
+```
+
+测量的 `useLayoutEffect` 先于注入样式执行,导致获取了错误的尺寸信息。也可以预测到这会对开发者产生影响。因而我们退而求其次,在 React 17 版本时会降级为原先的 `useMemo` 插入。
+
+## 总结
+
+Suspense 在带来渲染能力提升的同时也让时序变得十分重要,仅仅对 StrictMode 进行处理并不是一个最优的方式。针对不同的 React 版本使用不同的逻辑其实会存在不同版本之间的时序问题,`render` 会从父节点到子节点依次触发,而 `useInsertionEffect` 则相反。不过从 antd 角度来说,组件样式之间相互独立,所以这种时序问题并不会对我们产生影响。
From 266e60aa9533885673e182dfef56df1ba1f04257 Mon Sep 17 00:00:00 2001
From: daisy <47104575+linxianxi@users.noreply.github.com>
Date: Fri, 7 Jul 2023 17:29:01 +0800
Subject: [PATCH 33/80] fix(Card): should show tab when tabList is empty
(#43416)
* fix(Card): should show tab when tabList is empty
* test: add case
* test: adjust case
---
components/card/Card.tsx | 25 ++++++++++++------------
components/card/__tests__/index.test.tsx | 11 +++++++++++
2 files changed, 23 insertions(+), 13 deletions(-)
diff --git a/components/card/Card.tsx b/components/card/Card.tsx
index fd0a63ee06..2d088f5e01 100644
--- a/components/card/Card.tsx
+++ b/components/card/Card.tsx
@@ -118,19 +118,18 @@ const Card = React.forwardRef((props, ref) => {
let head: React.ReactNode;
const mergedSize = useSize(customizeSize);
const tabSize = !mergedSize || mergedSize === 'default' ? 'large' : mergedSize;
- const tabs =
- tabList && tabList.length ? (
- ({
- label: tab,
- ...item,
- }))}
- />
- ) : null;
+ const tabs = tabList ? (
+ ({
+ label: tab,
+ ...item,
+ }))}
+ />
+ ) : null;
if (title || extra || tabs) {
head = (
diff --git a/components/card/__tests__/index.test.tsx b/components/card/__tests__/index.test.tsx
index 9508eb909f..461503955e 100644
--- a/components/card/__tests__/index.test.tsx
+++ b/components/card/__tests__/index.test.tsx
@@ -132,6 +132,17 @@ describe('Card', () => {
expect(cardRef.current).toHaveClass('ant-card');
});
+ it('should show tab when tabList is empty', () => {
+ const { container } = render(
+
+ Card content
+ ,
+ );
+
+ expect(container.querySelector('.ant-tabs')).toBeTruthy();
+ expect(container.querySelector('.ant-tabs-nav-add')).toBeTruthy();
+ });
+
it('correct pass tabList props', () => {
const { container } = render(
Date: Fri, 7 Jul 2023 18:28:13 +0800
Subject: [PATCH 34/80] site: optimization type & props (#43433)
* chore: optimization type & props
* fix: remove useless type
---
.dumi/theme/builtins/Previewer/index.tsx | 5 ++---
.dumi/theme/common/ThemeSwitch/index.tsx | 2 +-
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/.dumi/theme/builtins/Previewer/index.tsx b/.dumi/theme/builtins/Previewer/index.tsx
index b3785af952..a37144956f 100644
--- a/.dumi/theme/builtins/Previewer/index.tsx
+++ b/.dumi/theme/builtins/Previewer/index.tsx
@@ -1,7 +1,6 @@
-import type { FC } from 'react';
-import React from 'react';
import type { IPreviewerProps } from 'dumi';
import { useTabMeta } from 'dumi';
+import React from 'react';
import CodePreviewer from './CodePreviewer';
import DesignPreviewer from './DesignPreviewer';
@@ -9,7 +8,7 @@ export interface AntdPreviewerProps extends IPreviewerProps {
originDebug?: IPreviewerProps['debug'];
}
-const Previewer: FC = ({ ...props }) => {
+const Previewer: React.FC = (props) => {
const tab = useTabMeta();
if (tab?.frontmatter.title === 'Design') {
diff --git a/.dumi/theme/common/ThemeSwitch/index.tsx b/.dumi/theme/common/ThemeSwitch/index.tsx
index 69895928f8..e17d2b4ebd 100644
--- a/.dumi/theme/common/ThemeSwitch/index.tsx
+++ b/.dumi/theme/common/ThemeSwitch/index.tsx
@@ -14,7 +14,7 @@ export type ThemeSwitchProps = {
onChange: (value: ThemeName[]) => void;
};
-const ThemeSwitch: React.FC = (props: ThemeSwitchProps) => {
+const ThemeSwitch: React.FC = (props) => {
const { value = ['light'], onChange } = props;
const { token } = useSiteToken();
const { pathname, search } = useLocation();
From 7d9429ba759f13cedca7aee6bd9328aaf5fd0ab9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?ZhuoYang=20Wu=28=E9=98=BF=E7=A6=BB=29?=
Date: Fri, 7 Jul 2023 18:58:20 +0800
Subject: [PATCH 35/80] fix: TreeSelect selected item not fit dropdown width
(#43413)
Co-authored-by: thinkasany <117748716+thinkasany@users.noreply.github.com>
---
components/tree-select/style/index.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/components/tree-select/style/index.ts b/components/tree-select/style/index.ts
index 61f15b0e8b..ab1319ebd6 100644
--- a/components/tree-select/style/index.ts
+++ b/components/tree-select/style/index.ts
@@ -30,7 +30,7 @@ const genBaseStyle: GenerateStyle = (token) => {
{
[treeCls]: {
borderRadius: 0,
- '&-list-holder-inner': {
+ [`${treeCls}-list-holder-inner`]: {
alignItems: 'stretch',
[`${treeCls}-treenode`]: {
From 2f0c82f59d439c8ebef517c0c422097b08e23152 Mon Sep 17 00:00:00 2001
From: Jomorx <95688556+Jomorx@users.noreply.github.com>
Date: Fri, 7 Jul 2023 18:58:54 +0800
Subject: [PATCH 36/80] fix: Slider tooltip text not align center (#43430)
* style: support centering text in tooltip in slider
* fix: formatting code
---
components/slider/style/index.tsx | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/components/slider/style/index.tsx b/components/slider/style/index.tsx
index d166ff3673..d75dc28257 100644
--- a/components/slider/style/index.tsx
+++ b/components/slider/style/index.tsx
@@ -57,7 +57,7 @@ interface SliderToken extends FullToken<'Slider'> {
// =============================== Base ===============================
const genBaseStyle: GenerateStyle = (token) => {
- const { componentCls, controlSize, dotSize, marginFull, marginPart, colorFillContentHover } =
+ const { componentCls, antCls, controlSize, dotSize, marginFull, marginPart, colorFillContentHover } =
token;
return {
@@ -256,6 +256,10 @@ const genBaseStyle: GenerateStyle = (token) => {
cursor: `not-allowed !important`,
},
},
+
+ [`&-tooltip ${antCls}-tooltip-inner`]:{
+ minWidth: "unset",
+ },
},
};
};
From e86e5e500003d1d182aa1d76607fed0ce21fd6a7 Mon Sep 17 00:00:00 2001
From: JiaQi <112228030+Yuiai01@users.noreply.github.com>
Date: Fri, 7 Jul 2023 19:02:55 +0800
Subject: [PATCH 37/80] feat: ConfigProvider support DatePicker & TimePicker
className and style properties (#43418)
---
.../config-provider/__tests__/style.test.tsx | 58 +++++++++++++++++++
components/config-provider/context.ts | 2 +
components/config-provider/index.en-US.md | 2 +
components/config-provider/index.tsx | 6 ++
components/config-provider/index.zh-CN.md | 2 +
.../generatePicker/generateSinglePicker.tsx | 13 ++++-
6 files changed, 82 insertions(+), 1 deletion(-)
diff --git a/components/config-provider/__tests__/style.test.tsx b/components/config-provider/__tests__/style.test.tsx
index 0c0408a26d..622a9ed40d 100644
--- a/components/config-provider/__tests__/style.test.tsx
+++ b/components/config-provider/__tests__/style.test.tsx
@@ -13,6 +13,7 @@ import Cascader from '../../cascader';
import Checkbox from '../../checkbox';
import Collapse from '../../collapse';
import ColorPicker from '../../color-picker';
+import DatePicker from '../../date-picker';
import Descriptions from '../../descriptions';
import Divider from '../../divider';
import Drawer from '../../drawer';
@@ -44,6 +45,7 @@ import Switch from '../../switch';
import Table from '../../table';
import Tabs from '../../tabs';
import Tag from '../../tag';
+import TimePicker from '../../time-picker';
import Timeline from '../../timeline';
import Transfer from '../../transfer';
import Tree from '../../tree';
@@ -1010,6 +1012,34 @@ describe('ConfigProvider support style and className props', () => {
expect(element).toHaveStyle({ backgroundColor: 'red' });
});
+ it('Should TimePicker className works', () => {
+ const { container } = render(
+
+
+ ,
+ );
+
+ expect(container.querySelector('.ant-picker')).toHaveClass('test-class');
+ });
+
+ it('Should TimePicker style works', () => {
+ const { container } = render(
+
+
+ ,
+ );
+
+ expect(container.querySelector('.ant-picker')).toHaveStyle('color: red; font-size: 16px;');
+ });
+
it('Should message className & style works', () => {
const Demo: React.FC = () => {
const [messageApi, contextHolder] = message.useMessage();
@@ -1205,4 +1235,32 @@ describe('ConfigProvider support style and className props', () => {
expect(element).toHaveClass('cp-colorPicker');
expect(element).toHaveStyle({ backgroundColor: 'red' });
});
+
+ it('Should DatePicker className works', () => {
+ const { container } = render(
+
+
+ ,
+ );
+
+ expect(container.querySelector('.ant-picker')).toHaveClass('test-class');
+ });
+
+ it('Should DatePicker style works', () => {
+ const { container } = render(
+
+
+ ,
+ );
+
+ expect(container.querySelector('.ant-picker')).toHaveStyle('color: red; font-size: 16px;');
+ });
});
diff --git a/components/config-provider/context.ts b/components/config-provider/context.ts
index 5717b45a3b..b13611610d 100644
--- a/components/config-provider/context.ts
+++ b/components/config-provider/context.ts
@@ -136,10 +136,12 @@ export interface ConfigConsumerProps {
card?: ComponentStyleConfig;
tabs?: ComponentStyleConfig;
timeline?: ComponentStyleConfig;
+ timePicker?: ComponentStyleConfig;
upload?: ComponentStyleConfig;
notification?: ComponentStyleConfig;
tree?: ComponentStyleConfig;
colorPicker?: ComponentStyleConfig;
+ datePicker?: ComponentStyleConfig;
}
const defaultGetPrefixCls = (suffixCls?: string, customizePrefixCls?: string) => {
diff --git a/components/config-provider/index.en-US.md b/components/config-provider/index.en-US.md
index 52e95f9c71..09e0f9dfc5 100644
--- a/components/config-provider/index.en-US.md
+++ b/components/config-provider/index.en-US.md
@@ -114,6 +114,7 @@ const {
| checkbox | Set Checkbox common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| collapse | Set Collapse common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| colorPicker | Set ColorPicker common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
+| datePicker | Set datePicker common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| descriptions | Set Descriptions common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| divider | Set Divider common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| drawer | Set Drawer common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
@@ -146,6 +147,7 @@ const {
| tabs | Set Tabs common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| tag | Set Tag common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| timeline | Set Timeline common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
+| timePicker | Set TimePicker common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| transfer | Set Transfer common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| tree | Set Tree common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| typography | Set Typography common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
diff --git a/components/config-provider/index.tsx b/components/config-provider/index.tsx
index ec1a0eebde..56c5602670 100644
--- a/components/config-provider/index.tsx
+++ b/components/config-provider/index.tsx
@@ -178,10 +178,12 @@ export interface ConfigProviderProps {
card?: ComponentStyleConfig;
tabs?: ComponentStyleConfig;
timeline?: ComponentStyleConfig;
+ timePicker?: ComponentStyleConfig;
upload?: ComponentStyleConfig;
notification?: ComponentStyleConfig;
tree?: ComponentStyleConfig;
colorPicker?: ComponentStyleConfig;
+ datePicker?: ComponentStyleConfig;
}
interface ProviderChildrenProps extends ConfigProviderProps {
@@ -312,10 +314,12 @@ const ProviderChildren: React.FC = (props) => {
card,
tabs,
timeline,
+ timePicker,
upload,
notification,
tree,
colorPicker,
+ datePicker,
} = props;
// =================================== Warning ===================================
@@ -408,10 +412,12 @@ const ProviderChildren: React.FC = (props) => {
card,
tabs,
timeline,
+ timePicker,
upload,
notification,
tree,
colorPicker,
+ datePicker,
};
const config = {
diff --git a/components/config-provider/index.zh-CN.md b/components/config-provider/index.zh-CN.md
index d8c7f0cbe3..9bb937e72a 100644
--- a/components/config-provider/index.zh-CN.md
+++ b/components/config-provider/index.zh-CN.md
@@ -116,6 +116,7 @@ const {
| checkbox | 设置 Checkbox 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| collapse | 设置 Collapse 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| colorPicker | 设置 ColorPicker 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
+| datePicker | 设置 DatePicker 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| descriptions | 设置 Descriptions 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| divider | 设置 Divider 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| drawer | 设置 Drawer 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
@@ -148,6 +149,7 @@ const {
| tabs | 设置 Tabs 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| tag | 设置 Tag 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| timeline | 设置 Timeline 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
+| timePicker | 设置 TimePicker 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| transfer | 设置 Transfer 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| tree | 设置 Tree 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| typography | 设置 Typography 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
diff --git a/components/date-picker/generatePicker/generateSinglePicker.tsx b/components/date-picker/generatePicker/generateSinglePicker.tsx
index a886b58fc2..205d5b829e 100644
--- a/components/date-picker/generatePicker/generateSinglePicker.tsx
+++ b/components/date-picker/generatePicker/generateSinglePicker.tsx
@@ -37,11 +37,13 @@ export default function generatePicker(generateConfig: GenerateConfig<
picker?: PickerMode,
displayName?: string,
) {
+ const consumerName = displayName === 'TimePicker' ? 'timePicker' : 'datePicker';
const Picker = forwardRef | CommonPickerMethods, InnerPickerProps>(
(props, ref) => {
const {
prefixCls: customizePrefixCls,
getPopupContainer: customizeGetPopupContainer,
+ style,
className,
rootClassName,
size: customizeSize,
@@ -55,7 +57,14 @@ export default function generatePicker(generateConfig: GenerateConfig<
...restProps
} = props;
- const { getPrefixCls, direction, getPopupContainer } = useContext(ConfigContext);
+ const {
+ getPrefixCls,
+ direction,
+ getPopupContainer,
+ // Consume different styles according to different names
+ [consumerName]: consumerStyle,
+ } = useContext(ConfigContext);
+
const prefixCls = getPrefixCls('picker', customizePrefixCls);
const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction);
const innerRef = React.useRef>(null);
@@ -153,9 +162,11 @@ export default function generatePicker(generateConfig: GenerateConfig<
),
hashId,
compactItemClassnames,
+ consumerStyle?.className,
className,
rootClassName,
)}
+ style={{ ...consumerStyle?.style, ...style }}
prefixCls={prefixCls}
getPopupContainer={customizeGetPopupContainer || getPopupContainer}
generateConfig={generateConfig}
From 9b03ce476da059a92d66f93072b71c2644d43cb9 Mon Sep 17 00:00:00 2001
From: thinkasany <117748716+thinkasany@users.noreply.github.com>
Date: Fri, 7 Jul 2023 21:42:26 +0800
Subject: [PATCH 38/80] type: remove unnecessary code (#43439)
---
components/button/button-group.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/components/button/button-group.tsx b/components/button/button-group.tsx
index d38df65ab8..f9644f40f2 100644
--- a/components/button/button-group.tsx
+++ b/components/button/button-group.tsx
@@ -1,9 +1,9 @@
import classNames from 'classnames';
import * as React from 'react';
+import warning from '../_util/warning';
import { ConfigContext } from '../config-provider';
import type { SizeType } from '../config-provider/SizeContext';
import { useToken } from '../theme/internal';
-import warning from '../_util/warning';
export interface ButtonGroupProps {
size?: SizeType;
@@ -13,7 +13,7 @@ export interface ButtonGroupProps {
children?: React.ReactNode;
}
-export const GroupSizeContext = React.createContext(undefined);
+export const GroupSizeContext = React.createContext(undefined);
const ButtonGroup: React.FC = (props) => {
const { getPrefixCls, direction } = React.useContext(ConfigContext);
From a998569adb9617c7a47b04b15ab0e1ed639b2f53 Mon Sep 17 00:00:00 2001
From: Fly me to the moon <138013431+Negentropy247@users.noreply.github.com>
Date: Sat, 8 Jul 2023 15:45:28 +0800
Subject: [PATCH 39/80] test: optimization type in test case (#43449)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* test: optimization type in test case
* fix lint
---------
Co-authored-by: 我们去月球漫步 <13105694+flymetothemoon-wj@user.noreply.gitee.com>
---
.../date-picker/__tests__/DatePicker.test.tsx | 2 +-
components/dropdown/__tests__/index.test.tsx | 2 +-
tests/__mocks__/@rc-component/trigger.tsx | 13 ++++++-------
3 files changed, 8 insertions(+), 9 deletions(-)
diff --git a/components/date-picker/__tests__/DatePicker.test.tsx b/components/date-picker/__tests__/DatePicker.test.tsx
index 6df4622b3e..c7b7533ba1 100644
--- a/components/date-picker/__tests__/DatePicker.test.tsx
+++ b/components/date-picker/__tests__/DatePicker.test.tsx
@@ -21,7 +21,7 @@ jest.mock('@rc-component/trigger', () => {
const h: typeof React = jest.requireActual('react');
return {
- default: h.forwardRef((props, ref) => {
+ default: h.forwardRef((props, ref) => {
triggerProps = props;
return h.createElement(Trigger, { ref, ...props });
}),
diff --git a/components/dropdown/__tests__/index.test.tsx b/components/dropdown/__tests__/index.test.tsx
index fa3f15b081..0af4b9fcac 100644
--- a/components/dropdown/__tests__/index.test.tsx
+++ b/components/dropdown/__tests__/index.test.tsx
@@ -15,7 +15,7 @@ jest.mock('@rc-component/trigger', () => {
const h: typeof React = jest.requireActual('react');
return {
- default: h.forwardRef((props, ref) => {
+ default: h.forwardRef((props, ref) => {
triggerProps = props;
return h.createElement(Trigger, { ref, ...props });
}),
diff --git a/tests/__mocks__/@rc-component/trigger.tsx b/tests/__mocks__/@rc-component/trigger.tsx
index b9c9d5c88d..1c331e7774 100644
--- a/tests/__mocks__/@rc-component/trigger.tsx
+++ b/tests/__mocks__/@rc-component/trigger.tsx
@@ -1,4 +1,4 @@
-import type { TriggerProps } from '@rc-component/trigger';
+import type { TriggerProps, TriggerRef } from '@rc-component/trigger';
import MockTrigger from '@rc-component/trigger/lib/mock';
import * as React from 'react';
import { TriggerMockContext } from '../../shared/demoTestContext';
@@ -6,22 +6,21 @@ import { TriggerMockContext } from '../../shared/demoTestContext';
let OriginTrigger = jest.requireActual('@rc-component/trigger');
OriginTrigger = OriginTrigger.default ?? OriginTrigger;
-const ForwardTrigger = React.forwardRef((props, ref) => {
+const ForwardTrigger = React.forwardRef((props, ref) => {
const context = React.useContext(TriggerMockContext);
const mergedPopupVisible = context?.popupVisible ?? props.popupVisible;
(global as any).triggerProps = props;
- const mergedProps = {
+ const mergedProps: TriggerProps = {
...props,
- ref,
- popupVisible: mergedPopupVisible as boolean,
+ popupVisible: mergedPopupVisible,
};
if (context?.mock === false) {
- return ;
+ return ;
}
- return ;
+ return ;
});
export default ForwardTrigger;
From dcb4611e256bd16bdb1ad54285e168125891fd01 Mon Sep 17 00:00:00 2001
From: Fly me to the moon <138013431+Negentropy247@users.noreply.github.com>
Date: Sat, 8 Jul 2023 17:39:59 +0800
Subject: [PATCH 40/80] type: replace unknown with TooltipRef (#43450)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: 我们去月球漫步 <13105694+flymetothemoon-wj@user.noreply.gitee.com>
---
components/popover/index.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/components/popover/index.tsx b/components/popover/index.tsx
index 23db59123e..42a7ddf9f6 100644
--- a/components/popover/index.tsx
+++ b/components/popover/index.tsx
@@ -4,7 +4,7 @@ import type { RenderFunction } from '../_util/getRenderPropValue';
import { getRenderPropValue } from '../_util/getRenderPropValue';
import { getTransitionName } from '../_util/motion';
import { ConfigContext } from '../config-provider';
-import type { AbstractTooltipProps } from '../tooltip';
+import type { AbstractTooltipProps, TooltipRef } from '../tooltip';
import Tooltip from '../tooltip';
import PurePanel from './PurePanel';
// CSSINJS
@@ -28,7 +28,7 @@ const Overlay: React.FC = ({ title, content, prefixCls }) => (
>
);
-const Popover = React.forwardRef((props, ref) => {
+const Popover = React.forwardRef((props, ref) => {
const {
prefixCls: customizePrefixCls,
title,
From 2cb72a7fc073a273726587dc7161942aeb9e11a7 Mon Sep 17 00:00:00 2001
From: liyunhe
Date: Sat, 8 Jul 2023 18:55:26 +0800
Subject: [PATCH 41/80] demo: Update for Resolving deprecation of findDOMNode
in StrictMode (#43451)
Resolving deprecation of findDOMNode in StrictMode
References: https://github.com/react-grid-layout/react-draggable/blob/master/CHANGELOG.md#440-may-12-2020
Signed-off-by: kookieaz
---
components/modal/demo/modal-render.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/components/modal/demo/modal-render.tsx b/components/modal/demo/modal-render.tsx
index f6d98c7f84..1ea4d01ade 100644
--- a/components/modal/demo/modal-render.tsx
+++ b/components/modal/demo/modal-render.tsx
@@ -71,6 +71,7 @@ const App: React.FC = () => {
onStart(event, uiData)}
>
{modal}
From 56e610252327e8a2ead1f2a09efe14166bfaf3dd Mon Sep 17 00:00:00 2001
From: thinkasany <117748716+thinkasany@users.noreply.github.com>
Date: Sat, 8 Jul 2023 19:47:54 +0800
Subject: [PATCH 42/80] type: replace unknown with TooltipRef (#43452)
---
components/popconfirm/index.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/components/popconfirm/index.tsx b/components/popconfirm/index.tsx
index 9263c11357..5e3e8b5661 100644
--- a/components/popconfirm/index.tsx
+++ b/components/popconfirm/index.tsx
@@ -9,7 +9,7 @@ import { cloneElement } from '../_util/reactNode';
import type { ButtonProps, LegacyButtonType } from '../button/button';
import { ConfigContext } from '../config-provider';
import Popover from '../popover';
-import type { AbstractTooltipProps } from '../tooltip';
+import type { AbstractTooltipProps, TooltipRef } from '../tooltip';
import PurePanel, { Overlay } from './PurePanel';
import usePopconfirmStyle from './style';
@@ -37,7 +37,7 @@ export interface PopconfirmState {
open?: boolean;
}
-const Popconfirm = React.forwardRef((props, ref) => {
+const Popconfirm = React.forwardRef((props, ref) => {
const {
prefixCls: customizePrefixCls,
placement = 'top',
From cea3c72a09047416acfb6a82e3f271b02d197d29 Mon Sep 17 00:00:00 2001
From: thinkasany <117748716+thinkasany@users.noreply.github.com>
Date: Sat, 8 Jul 2023 21:48:52 +0800
Subject: [PATCH 43/80] type: add RawPurePanelProps interface (#43453)
* type(popover): add RawPurePanelProps interface
* fix: optimize
---
components/popover/PurePanel.tsx | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/components/popover/PurePanel.tsx b/components/popover/PurePanel.tsx
index 29259c7f15..218583505f 100644
--- a/components/popover/PurePanel.tsx
+++ b/components/popover/PurePanel.tsx
@@ -25,7 +25,11 @@ export interface PurePanelProps extends Omit {
children?: React.ReactNode;
}
-export function RawPurePanel(props: any) {
+interface RawPurePanelProps extends PopoverProps {
+ hashId: string;
+}
+
+export const RawPurePanel: React.FC = (props) => {
const {
hashId,
prefixCls,
@@ -50,13 +54,13 @@ export function RawPurePanel(props: any) {
>
- {children || getOverlay(prefixCls, title, content)}
+ {children || getOverlay(prefixCls!, title, content)}
);
-}
+};
-export default function PurePanel(props: any) {
+const PurePanel: React.FC = (props) => {
const { prefixCls: customizePrefixCls, ...restProps } = props;
const { getPrefixCls } = React.useContext(ConfigContext);
@@ -64,4 +68,6 @@ export default function PurePanel(props: any) {
const [wrapSSR, hashId] = useStyle(prefixCls);
return wrapSSR();
-}
+};
+
+export default PurePanel;
From b139dec6635df4c0cdc168077569e21b602572f0 Mon Sep 17 00:00:00 2001
From: thinkasany <117748716+thinkasany@users.noreply.github.com>
Date: Sun, 9 Jul 2023 13:34:59 +0800
Subject: [PATCH 44/80] chore: code style optimization (#43457)
* chore: Code style optimization
* style: code optimize
---
components/descriptions/Cell.tsx | 27 ++++++++++----------
components/descriptions/index.tsx | 41 +++++++++++++++++--------------
components/tooltip/index.tsx | 3 +--
3 files changed, 38 insertions(+), 33 deletions(-)
diff --git a/components/descriptions/Cell.tsx b/components/descriptions/Cell.tsx
index fe7bd3ca2a..55256c8407 100644
--- a/components/descriptions/Cell.tsx
+++ b/components/descriptions/Cell.tsx
@@ -19,19 +19,20 @@ export interface CellProps {
colon?: boolean;
}
-const Cell: React.FC = ({
- itemPrefixCls,
- component,
- span,
- className,
- style,
- labelStyle,
- contentStyle,
- bordered,
- label,
- content,
- colon,
-}) => {
+const Cell: React.FC = (props) => {
+ const {
+ itemPrefixCls,
+ component,
+ span,
+ className,
+ style,
+ labelStyle,
+ contentStyle,
+ bordered,
+ label,
+ content,
+ colon,
+ } = props;
const Component = component as any;
if (bordered) {
diff --git a/components/descriptions/index.tsx b/components/descriptions/index.tsx
index 2b5445e8db..0f012c2cf6 100644
--- a/components/descriptions/index.tsx
+++ b/components/descriptions/index.tsx
@@ -115,23 +115,28 @@ export interface DescriptionsProps {
contentStyle?: React.CSSProperties;
}
-function Descriptions({
- prefixCls: customizePrefixCls,
- title,
- extra,
- column = DEFAULT_COLUMN_MAP,
- colon = true,
- bordered,
- layout,
- children,
- className,
- rootClassName,
- style,
- size: customizeSize,
- labelStyle,
- contentStyle,
- ...restProps
-}: DescriptionsProps) {
+type CompoundedComponent = React.FC & {
+ Item: typeof DescriptionsItem;
+};
+
+const Descriptions: CompoundedComponent = (props) => {
+ const {
+ prefixCls: customizePrefixCls,
+ title,
+ extra,
+ column = DEFAULT_COLUMN_MAP,
+ colon = true,
+ bordered,
+ layout,
+ children,
+ className,
+ rootClassName,
+ style,
+ size: customizeSize,
+ labelStyle,
+ contentStyle,
+ ...restProps
+ } = props;
const { getPrefixCls, direction } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('descriptions', customizePrefixCls);
const [screens, setScreens] = React.useState({});
@@ -207,7 +212,7 @@ function Descriptions({
,
);
-}
+};
if (process.env.NODE_ENV !== 'production') {
Descriptions.displayName = 'Descriptions';
diff --git a/components/tooltip/index.tsx b/components/tooltip/index.tsx
index 4b7432b32d..4d3b402837 100644
--- a/components/tooltip/index.tsx
+++ b/components/tooltip/index.tsx
@@ -10,6 +10,7 @@ import useMergedState from 'rc-util/lib/hooks/useMergedState';
import type { CSSProperties } from 'react';
import * as React from 'react';
import type { PresetColorType } from '../_util/colors';
+import type { RenderFunction } from '../_util/getRenderPropValue';
import { getTransitionName } from '../_util/motion';
import type { AdjustOverflow, PlacementsConfig } from '../_util/placements';
import getPlacements from '../_util/placements';
@@ -110,8 +111,6 @@ export interface AbstractTooltipProps extends LegacyTooltipProps {
destroyTooltipOnHide?: boolean | { keepParent?: boolean };
}
-export type RenderFunction = () => React.ReactNode;
-
export interface TooltipPropsWithOverlay extends AbstractTooltipProps {
title?: React.ReactNode | RenderFunction;
overlay?: React.ReactNode | RenderFunction;
From a5bb59c76c9cefc970b313349b66a9bde5f61f5a Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sun, 9 Jul 2023 13:35:57 +0800
Subject: [PATCH 45/80] chore(deps): update dependency stylelint-prettier from
v3 to v4 (#43456)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 9069268f0b..574c8c93c3 100644
--- a/package.json
+++ b/package.json
@@ -290,7 +290,7 @@
"stylelint": "^15.1.0",
"stylelint-config-rational-order": "^0.1.2",
"stylelint-config-standard": "^34.0.0",
- "stylelint-prettier": "^3.0.0",
+ "stylelint-prettier": "^4.0.0",
"sylvanas": "^0.6.1",
"terser": "^5.16.1",
"ts-node": "^10.8.2",
From efa427072aaf2c57a543d67b219ed01317f1d40a Mon Sep 17 00:00:00 2001
From: thinkasany <117748716+thinkasany@users.noreply.github.com>
Date: Sun, 9 Jul 2023 16:47:31 +0800
Subject: [PATCH 46/80] chore: code style optimization (#43458)
---
components/badge/Ribbon.tsx | 23 ++--
components/badge/ScrollNumber.tsx | 124 +++++++++----------
components/button/LoadingIcon.tsx | 11 +-
components/input/TextArea.tsx | 172 +++++++++++++--------------
components/typography/Typography.tsx | 80 ++++++-------
5 files changed, 194 insertions(+), 216 deletions(-)
diff --git a/components/badge/Ribbon.tsx b/components/badge/Ribbon.tsx
index 05760f6e93..453c071d0c 100644
--- a/components/badge/Ribbon.tsx
+++ b/components/badge/Ribbon.tsx
@@ -1,10 +1,10 @@
import classNames from 'classnames';
import * as React from 'react';
-import { ConfigContext } from '../config-provider';
import type { PresetColorType } from '../_util/colors';
+import { isPresetColor } from '../_util/colors';
import type { LiteralUnion } from '../_util/type';
+import { ConfigContext } from '../config-provider';
import useStyle from './style';
-import { isPresetColor } from '../_util/colors';
type RibbonPlacement = 'start' | 'end';
@@ -18,15 +18,16 @@ export interface RibbonProps {
placement?: RibbonPlacement;
}
-const Ribbon: React.FC
= ({
- className,
- prefixCls: customizePrefixCls,
- style,
- color,
- children,
- text,
- placement = 'end',
-}) => {
+const Ribbon: React.FC = (props) => {
+ const {
+ className,
+ prefixCls: customizePrefixCls,
+ style,
+ color,
+ children,
+ text,
+ placement = 'end',
+ } = props;
const { getPrefixCls, direction } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('ribbon', customizePrefixCls);
const colorInPreset = isPresetColor(color, false);
diff --git a/components/badge/ScrollNumber.tsx b/components/badge/ScrollNumber.tsx
index 83eaafbde5..a71762ac2c 100644
--- a/components/badge/ScrollNumber.tsx
+++ b/components/badge/ScrollNumber.tsx
@@ -1,7 +1,7 @@
import classNames from 'classnames';
import * as React from 'react';
-import { ConfigContext } from '../config-provider';
import { cloneElement } from '../_util/reactNode';
+import { ConfigContext } from '../config-provider';
import SingleNumber from './SingleNumber';
export interface ScrollNumberProps {
@@ -21,75 +21,67 @@ export interface ScrollNumberState {
count?: string | number | null;
}
-const ScrollNumber = React.forwardRef(
- (
- {
- prefixCls: customizePrefixCls,
- count,
- className,
- motionClassName,
- style,
- title,
- show,
- component: Component = 'sup',
- children,
- ...restProps
- },
- ref,
- ) => {
- const { getPrefixCls } = React.useContext(ConfigContext);
- const prefixCls = getPrefixCls('scroll-number', customizePrefixCls);
+const ScrollNumber = React.forwardRef((props, ref) => {
+ const {
+ prefixCls: customizePrefixCls,
+ count,
+ className,
+ motionClassName,
+ style,
+ title,
+ show,
+ component: Component = 'sup',
+ children,
+ ...restProps
+ } = props;
+ const { getPrefixCls } = React.useContext(ConfigContext);
+ const prefixCls = getPrefixCls('scroll-number', customizePrefixCls);
- // ============================ Render ============================
- const newProps = {
- ...restProps,
- 'data-show': show,
- style,
- className: classNames(prefixCls, className, motionClassName),
- title: title as string,
- };
+ // ============================ Render ============================
+ const newProps = {
+ ...restProps,
+ 'data-show': show,
+ style,
+ className: classNames(prefixCls, className, motionClassName),
+ title: title as string,
+ };
- // Only integer need motion
- let numberNodes: React.ReactNode = count;
- if (count && Number(count) % 1 === 0) {
- const numberList = String(count).split('');
+ // Only integer need motion
+ let numberNodes: React.ReactNode = count;
+ if (count && Number(count) % 1 === 0) {
+ const numberList = String(count).split('');
- numberNodes = numberList.map((num, i) => (
-
- ));
- }
+ numberNodes = numberList.map((num, i) => (
+
+ ));
+ }
- // allow specify the border
- // mock border-color by box-shadow for compatible with old usage:
- //
- if (style && style.borderColor) {
- newProps.style = {
- ...style,
- boxShadow: `0 0 0 1px ${style.borderColor} inset`,
- };
- }
- if (children) {
- return cloneElement(children, (oriProps) => ({
- className: classNames(
- `${prefixCls}-custom-component`,
- oriProps?.className,
- motionClassName,
- ),
- }));
- }
+ // allow specify the border
+ // mock border-color by box-shadow for compatible with old usage:
+ //
+ if (style && style.borderColor) {
+ newProps.style = {
+ ...style,
+ boxShadow: `0 0 0 1px ${style.borderColor} inset`,
+ };
+ }
+ if (children) {
+ return cloneElement(children, (oriProps) => ({
+ className: classNames(`${prefixCls}-custom-component`, oriProps?.className, motionClassName),
+ }));
+ }
- return (
-
- {numberNodes}
-
- );
- },
-);
+ return (
+
+ {numberNodes}
+
+ );
+});
export default ScrollNumber;
diff --git a/components/button/LoadingIcon.tsx b/components/button/LoadingIcon.tsx
index 8b4cc16a4f..8ab9d6d291 100644
--- a/components/button/LoadingIcon.tsx
+++ b/components/button/LoadingIcon.tsx
@@ -1,7 +1,7 @@
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
+import classNames from 'classnames';
import CSSMotion from 'rc-motion';
import React, { forwardRef } from 'react';
-import classNames from 'classnames';
import IconWrapper from './IconWrapper';
type InnerLoadingIconProps = {
@@ -43,13 +43,8 @@ const getRealWidth = (node: HTMLElement): React.CSSProperties => ({
transform: 'scale(1)',
});
-const LoadingIcon: React.FC = ({
- prefixCls,
- loading,
- existIcon,
- className,
- style,
-}) => {
+const LoadingIcon: React.FC = (props) => {
+ const { prefixCls, loading, existIcon, className, style } = props;
const visible = !!loading;
if (existIcon) {
diff --git a/components/input/TextArea.tsx b/components/input/TextArea.tsx
index 5104fbcf76..e4d79a303f 100644
--- a/components/input/TextArea.tsx
+++ b/components/input/TextArea.tsx
@@ -29,103 +29,97 @@ export interface TextAreaRef {
resizableTextArea?: RcTextAreaRef['resizableTextArea'];
}
-const TextArea = forwardRef(
- (
- {
- prefixCls: customizePrefixCls,
- bordered = true,
- size: customizeSize,
- disabled: customDisabled,
- status: customStatus,
- allowClear,
- showCount,
- classNames: classes,
- ...rest
- },
- ref,
- ) => {
- const { getPrefixCls, direction } = React.useContext(ConfigContext);
+const TextArea = forwardRef((props, ref) => {
+ const {
+ prefixCls: customizePrefixCls,
+ bordered = true,
+ size: customizeSize,
+ disabled: customDisabled,
+ status: customStatus,
+ allowClear,
+ showCount,
+ classNames: classes,
+ ...rest
+ } = props;
+ const { getPrefixCls, direction } = React.useContext(ConfigContext);
- // ===================== Size =====================
- const mergedSize = useSize(customizeSize);
+ // ===================== Size =====================
+ const mergedSize = useSize(customizeSize);
- // ===================== Disabled =====================
- const disabled = React.useContext(DisabledContext);
- const mergedDisabled = customDisabled ?? disabled;
+ // ===================== Disabled =====================
+ const disabled = React.useContext(DisabledContext);
+ const mergedDisabled = customDisabled ?? disabled;
- // ===================== Status =====================
- const {
- status: contextStatus,
- hasFeedback,
- feedbackIcon,
- } = React.useContext(FormItemInputContext);
- const mergedStatus = getMergedStatus(contextStatus, customStatus);
+ // ===================== Status =====================
+ const {
+ status: contextStatus,
+ hasFeedback,
+ feedbackIcon,
+ } = React.useContext(FormItemInputContext);
+ const mergedStatus = getMergedStatus(contextStatus, customStatus);
- // ===================== Ref =====================
- const innerRef = React.useRef(null);
+ // ===================== Ref =====================
+ const innerRef = React.useRef(null);
- React.useImperativeHandle(ref, () => ({
- resizableTextArea: innerRef.current?.resizableTextArea,
- focus: (option?: InputFocusOptions) => {
- triggerFocus(innerRef.current?.resizableTextArea?.textArea, option);
- },
- blur: () => innerRef.current?.blur(),
- }));
+ React.useImperativeHandle(ref, () => ({
+ resizableTextArea: innerRef.current?.resizableTextArea,
+ focus: (option?: InputFocusOptions) => {
+ triggerFocus(innerRef.current?.resizableTextArea?.textArea, option);
+ },
+ blur: () => innerRef.current?.blur(),
+ }));
- const prefixCls = getPrefixCls('input', customizePrefixCls);
+ const prefixCls = getPrefixCls('input', customizePrefixCls);
- // Allow clear
- let mergedAllowClear: BaseInputProps['allowClear'];
- if (typeof allowClear === 'object' && allowClear?.clearIcon) {
- mergedAllowClear = allowClear;
- } else if (allowClear) {
- mergedAllowClear = { clearIcon: };
- }
+ // Allow clear
+ let mergedAllowClear: BaseInputProps['allowClear'];
+ if (typeof allowClear === 'object' && allowClear?.clearIcon) {
+ mergedAllowClear = allowClear;
+ } else if (allowClear) {
+ mergedAllowClear = { clearIcon: };
+ }
- // ===================== Style =====================
- const [wrapSSR, hashId] = useStyle(prefixCls);
+ // ===================== Style =====================
+ const [wrapSSR, hashId] = useStyle(prefixCls);
- return wrapSSR(
- {feedbackIcon}
- }
- showCount={showCount}
- ref={innerRef}
- />,
- );
- },
-);
+ return wrapSSR(
+ {feedbackIcon}}
+ showCount={showCount}
+ ref={innerRef}
+ />,
+ );
+});
export default TextArea;
diff --git a/components/typography/Typography.tsx b/components/typography/Typography.tsx
index 9b78a906a1..9f46022494 100644
--- a/components/typography/Typography.tsx
+++ b/components/typography/Typography.tsx
@@ -1,9 +1,9 @@
import classNames from 'classnames';
import { composeRef } from 'rc-util/lib/ref';
import * as React from 'react';
+import warning from '../_util/warning';
import type { DirectionType } from '../config-provider';
import { ConfigContext } from '../config-provider';
-import warning from '../_util/warning';
import useStyle from './style';
export interface TypographyProps
@@ -29,53 +29,49 @@ interface InternalTypographyProps
const Typography = React.forwardRef<
HTMLElement,
InternalTypographyProps
->(
- (
- {
- prefixCls: customizePrefixCls,
- component: Component = 'article',
- className,
- rootClassName,
- setContentRef,
- children,
- direction: typographyDirection,
- ...restProps
- },
- ref,
- ) => {
- const { getPrefixCls, direction: contextDirection } = React.useContext(ConfigContext);
+>((props, ref) => {
+ const {
+ prefixCls: customizePrefixCls,
+ component: Component = 'article',
+ className,
+ rootClassName,
+ setContentRef,
+ children,
+ direction: typographyDirection,
+ ...restProps
+ } = props;
+ const { getPrefixCls, direction: contextDirection } = React.useContext(ConfigContext);
- const direction = typographyDirection ?? contextDirection;
+ const direction = typographyDirection ?? contextDirection;
- let mergedRef = ref;
- if (setContentRef) {
- warning(false, 'Typography', '`setContentRef` is deprecated. Please use `ref` instead.');
- mergedRef = composeRef(ref, setContentRef);
- }
+ let mergedRef = ref;
+ if (setContentRef) {
+ warning(false, 'Typography', '`setContentRef` is deprecated. Please use `ref` instead.');
+ mergedRef = composeRef(ref, setContentRef);
+ }
- const prefixCls = getPrefixCls('typography', customizePrefixCls);
+ const prefixCls = getPrefixCls('typography', customizePrefixCls);
- // Style
- const [wrapSSR, hashId] = useStyle(prefixCls);
+ // Style
+ const [wrapSSR, hashId] = useStyle(prefixCls);
- const componentClassName = classNames(
- prefixCls,
- {
- [`${prefixCls}-rtl`]: direction === 'rtl',
- },
- className,
- rootClassName,
- hashId,
- );
+ const componentClassName = classNames(
+ prefixCls,
+ {
+ [`${prefixCls}-rtl`]: direction === 'rtl',
+ },
+ className,
+ rootClassName,
+ hashId,
+ );
- return wrapSSR(
- // @ts-expect-error: Expression produces a union type that is too complex to represent.
-
- {children}
- ,
- );
- },
-);
+ return wrapSSR(
+ // @ts-expect-error: Expression produces a union type that is too complex to represent.
+
+ {children}
+ ,
+ );
+});
if (process.env.NODE_ENV !== 'production') {
Typography.displayName = 'Typography';
From 4154dafaa10e07454bf2729c279161abb9ec207c Mon Sep 17 00:00:00 2001
From: EMpersonal
Date: Mon, 10 Jul 2023 10:06:52 +0800
Subject: [PATCH 47/80] fix: some browser may not repaint when remove nodes
(#43358)
Co-authored-by: sunliangmu
Co-authored-by: lijianan <574980606@qq.com>
---
components/_util/styleChecker.ts | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/components/_util/styleChecker.ts b/components/_util/styleChecker.ts
index 80f01afc79..c39ca2510f 100644
--- a/components/_util/styleChecker.ts
+++ b/components/_util/styleChecker.ts
@@ -25,10 +25,17 @@ export const detectFlexGapSupported = () => {
flex.appendChild(document.createElement('div'));
flex.appendChild(document.createElement('div'));
+ // some browser may not repaint when remove nodes, so we need create a new layer to detect.
+ const container = document.createElement('div');
+ container.style.position = 'absolute';
+ container.style.zIndex = '-9999';
+ container.appendChild(flex);
+
+
// append to the DOM (needed to obtain scrollHeight)
- document.body.appendChild(flex);
+ document.body.appendChild(container);
flexGapSupported = flex.scrollHeight === 1; // flex container should be 1px high from the row-gap
- document.body.removeChild(flex);
+ document.body.removeChild(container);
return flexGapSupported;
};
From 9494f39c68efcdde25f38bdd0ea39260ed189fc8 Mon Sep 17 00:00:00 2001
From: lijianan <574980606@qq.com>
Date: Mon, 10 Jul 2023 10:08:28 +0800
Subject: [PATCH 48/80] site: update Next.js docs (#43448)
* site: update nextjs docs
* fix
* add
---
docs/react/contributing.en-US.md | 1 +
docs/react/contributing.zh-CN.md | 1 +
docs/react/getting-started.en-US.md | 2 +-
docs/react/getting-started.zh-CN.md | 2 +-
docs/react/use-with-next.en-US.md | 8 ++++----
docs/react/use-with-next.zh-CN.md | 8 ++++----
docs/react/use-with-vite.en-US.md | 6 ++----
docs/react/use-with-vite.zh-CN.md | 6 ++----
8 files changed, 16 insertions(+), 18 deletions(-)
diff --git a/docs/react/contributing.en-US.md b/docs/react/contributing.en-US.md
index 7d121fd205..07da9dc689 100644
--- a/docs/react/contributing.en-US.md
+++ b/docs/react/contributing.en-US.md
@@ -55,6 +55,7 @@ The core team is monitoring for pull requests. We will review your pull request
5. Run `npm test -- -u` to update the [jest snapshots](https://jestjs.io/docs/snapshot-testing) and commit these changes as well (if there are any updates).
6. Ensure the UI change passes `npm run test-image`,Run `npm run test-image -- -u` to update UI snapshots and commit these changes as well (if there are any updates), **UI test base on [Docker](https://docs.docker.com/get-docker/), need download the corresponding installation according to the platform**
7. Make sure your code lints (npm run lint). Tip: Lint runs automatically when you `git commit` (Use [Git Hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks)).
+8. Finally, please make sure that all GitHub CI checks pass, if they fail, you can click `detail` to enter the details to view the reason.
Sending a Pull Request to [react-component](https://github.com/react-component/):
diff --git a/docs/react/contributing.zh-CN.md b/docs/react/contributing.zh-CN.md
index 0c6541ae60..8be7b52d05 100644
--- a/docs/react/contributing.zh-CN.md
+++ b/docs/react/contributing.zh-CN.md
@@ -55,6 +55,7 @@ Ant Design 团队会关注所有的 pull request,我们会 review 以及合并
5. 运行 `npm test -- -u` 来更新 [jest snapshot](https://jestjs.io/zh-Hans/docs/snapshot-testing) 并且把这些更新也提交上来(如果有的话)。
6. 确认所有的 UI 改动通过 `npm run test-image`,可以运行 `npm run test-image -- -u` 更新 UI 快照并且把这些更新也提交上来(如果有的话),**UI 测试基于 [Docker](https://docs.docker.com/get-docker/),根据平台下载对应的安装程序。**
7. 确保你的代码通过了 lint 检查 `npm run lint`. 小贴士: Lint 会在你 `git commit` 的时候自动运行(通过[Git Hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks))。
+8. 最后请确保所有 GitHub CI 检查通过,如果失败,可点击 `detail` 进入详情查看原因。
给 [react-component](https://github.com/react-component/) 发送 pull request:
diff --git a/docs/react/getting-started.en-US.md b/docs/react/getting-started.en-US.md
index 72f227d32d..755f914274 100755
--- a/docs/react/getting-started.en-US.md
+++ b/docs/react/getting-started.en-US.md
@@ -113,6 +113,6 @@ Jest does not support `esm` modules, and Ant Design uses them. In order to test
## Customize your Workflow
-If you want to customize your workflow, we recommend using [webpack](https://webpack.js.org) to build and debug code. You can try out plenty of [boilerplates](https://github.com/enaqx/awesome-react#react-tools) available in the React ecosystem.
+If you want to customize your workflow, we recommend using [webpack](https://webpack.js.org) or [vite](https://vitejs.dev/) to build and debug code. You can try out plenty of [boilerplates](https://github.com/enaqx/awesome-react#react-tools) available in the React ecosystem.
There are also some [scaffolds](https://scaffold.ant.design/) which have already been integrated into antd, so you can try and start with one of these and even contribute.
diff --git a/docs/react/getting-started.zh-CN.md b/docs/react/getting-started.zh-CN.md
index 95c736db2c..0622ec5fca 100755
--- a/docs/react/getting-started.zh-CN.md
+++ b/docs/react/getting-started.zh-CN.md
@@ -105,6 +105,6 @@ createRoot(document.getElementById('root')).render();
## 自行构建
-如果想自己维护工作流,我们推荐使用 [webpack](https://webpack.js.org) 进行构建和调试,可以使用 React 生态圈中的 [各种脚手架](https://github.com/enaqx/awesome-react#react-tools) 进行开发。
+如果想自己维护工作流,我们推荐使用 [webpack](https://webpack.js.org) 或者 [vite](https://cn.vitejs.dev/) 进行构建和调试,可以使用 React 生态圈中的 [各种脚手架](https://github.com/enaqx/awesome-react#react-tools) 进行开发。
目前社区也有很多基于 antd 定制的 [React 脚手架](https://scaffold.ant.design/),欢迎进行试用和贡献。
diff --git a/docs/react/use-with-next.en-US.md b/docs/react/use-with-next.en-US.md
index daef119fa4..5a8bfa2b04 100644
--- a/docs/react/use-with-next.en-US.md
+++ b/docs/react/use-with-next.en-US.md
@@ -24,15 +24,15 @@ Open the browser at http://localhost:3000/. if you see the NEXT logo, it is cons
## Import antd
-Now we install `antd` from yarn or npm.
+Now we install `antd` from yarn or npm or pnpm.
-```bash
-$ npm install antd --save
-```
+
Modify `src/app/page.tsx`, import Button component from `antd`.
```jsx
+'use client';
+
import React from 'react';
import { Button } from 'antd';
diff --git a/docs/react/use-with-next.zh-CN.md b/docs/react/use-with-next.zh-CN.md
index 1a1af14c05..cd8f8479a4 100644
--- a/docs/react/use-with-next.zh-CN.md
+++ b/docs/react/use-with-next.zh-CN.md
@@ -24,15 +24,15 @@ $ npm run dev
## 引入 antd
-现在从 yarn 或 npm 安装并引入 antd。
+现在从 yarn 或 npm 或 pnpm 安装并引入 antd。
-```bash
-$ npm install antd --save
-```
+
修改 `src/app/page.tsx`,引入 antd 的按钮组件。
```jsx
+'use client';
+
import React from 'react';
import { Button } from 'antd';
diff --git a/docs/react/use-with-vite.en-US.md b/docs/react/use-with-vite.en-US.md
index f5618153f0..fb3b906e17 100644
--- a/docs/react/use-with-vite.en-US.md
+++ b/docs/react/use-with-vite.en-US.md
@@ -43,11 +43,9 @@ Below is the default directory structure.
└── vite.config.ts
```
-Now we install `antd` from yarn or npm.
+Now we install `antd` from yarn or npm or pnpm.
-```bash
-$ npm install antd --save
-```
+
Modify `src/App.js`, import Button component from `antd`.
diff --git a/docs/react/use-with-vite.zh-CN.md b/docs/react/use-with-vite.zh-CN.md
index 4a0b8d8e1f..67c0fd5c5f 100644
--- a/docs/react/use-with-vite.zh-CN.md
+++ b/docs/react/use-with-vite.zh-CN.md
@@ -43,11 +43,9 @@ $ npm run dev
└── vite.config.js
```
-现在从 yarn 或 npm 安装并引入 antd。
+现在从 yarn 或 npm 或 pnpm 安装并引入 antd。
-```bash
-$ npm install antd --save
-```
+
修改 `src/App.js`,引入 antd 的按钮组件。
From 5094710a2e98bed524b908dd9fe4122ea63e827c Mon Sep 17 00:00:00 2001
From: MadCcc <1075746765@qq.com>
Date: Mon, 10 Jul 2023 10:36:35 +0800
Subject: [PATCH 49/80] fix: add deprecated token warning (#43461)
---
components/menu/style/index.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/components/menu/style/index.tsx b/components/menu/style/index.tsx
index c95cd21644..aa90bea75c 100644
--- a/components/menu/style/index.tsx
+++ b/components/menu/style/index.tsx
@@ -859,6 +859,7 @@ export default (prefixCls: string, injectStyle: boolean): UseComponentStyleResul
['colorActiveBarWidth', 'activeBarWidth'],
['colorActiveBarHeight', 'activeBarHeight'],
['colorActiveBarBorderSize', 'activeBarBorderWidth'],
+ ['colorItemBgSelected', 'itemSelectedBg'],
],
},
);
From e72a7836f48efd15e307af39cd9fa5a112d858ee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=BA=A2=E6=9E=9C=E6=B1=81?=
Date: Mon, 10 Jul 2023 13:37:43 +0800
Subject: [PATCH 50/80] feat: ColorPicker Implement onChangeComplete API
(#43370)
* feat: implement onChangeComplete api
* docs: add change completed demo
* perf: optimization code
---
components/color-picker/ColorPicker.tsx | 22 +-
components/color-picker/ColorPickerPanel.tsx | 13 +-
.../__snapshots__/demo-extend.test.ts.snap | 384 ++++++++++++++++++
.../__tests__/__snapshots__/demo.test.ts.snap | 19 +
.../color-picker/__tests__/index.test.tsx | 15 +
.../color-picker/components/PanelPicker.tsx | 6 +-
.../color-picker/demo/change-completed.md | 7 +
.../color-picker/demo/change-completed.tsx | 21 +
components/color-picker/index.en-US.md | 2 +
components/color-picker/index.zh-CN.md | 2 +
components/color-picker/interface.ts | 1 +
package.json | 4 +-
12 files changed, 468 insertions(+), 28 deletions(-)
create mode 100644 components/color-picker/demo/change-completed.md
create mode 100644 components/color-picker/demo/change-completed.tsx
diff --git a/components/color-picker/ColorPicker.tsx b/components/color-picker/ColorPicker.tsx
index 0003af4adc..11a3071429 100644
--- a/components/color-picker/ColorPicker.tsx
+++ b/components/color-picker/ColorPicker.tsx
@@ -30,7 +30,7 @@ import { customizePrefixCls, generateColor } from './util';
export type ColorPickerProps = Omit<
RcColorPickerProps,
- 'onChange' | 'value' | 'defaultValue' | 'panelRender'
+ 'onChange' | 'value' | 'defaultValue' | 'panelRender' | 'onChangeComplete'
> & {
value?: Color | string;
defaultValue?: Color | string;
@@ -55,6 +55,7 @@ export type ColorPickerProps = Omit<
onFormatChange?: (format: ColorFormat) => void;
onChange?: (value: Color, hex: string) => void;
onClear?: () => void;
+ onChangeComplete?: (value: Color) => void;
} & Pick;
type CompoundedComponent = React.FC & {
@@ -85,6 +86,7 @@ const ColorPicker: CompoundedComponent = (props) => {
onChange,
onClear,
onOpenChange,
+ onChangeComplete,
getPopupContainer,
autoAdjustOverflow = true,
destroyTooltipOnHide,
@@ -142,13 +144,12 @@ const ColorPicker: CompoundedComponent = (props) => {
color = generateColor(hsba);
}
}
- if (!value) {
- setColorValue(color);
- }
// Only for drag-and-drop color picking
if (pickColor) {
popupAllowCloseRef.current = false;
}
+
+ setColorValue(color);
onChange?.(color, color.toHexString());
};
@@ -157,8 +158,9 @@ const ColorPicker: CompoundedComponent = (props) => {
onClear?.();
};
- const handleChangeComplete = () => {
+ const handleChangeComplete: ColorPickerProps['onChangeComplete'] = (color) => {
popupAllowCloseRef.current = true;
+ onChangeComplete?.(generateColor(color));
};
const popoverProps: PopoverProps = {
@@ -182,6 +184,7 @@ const ColorPicker: CompoundedComponent = (props) => {
panelRender,
format: formatValue,
onFormatChange: setFormatValue,
+ onChangeComplete: handleChangeComplete,
};
const mergedStyle: React.CSSProperties = { ...colorPicker?.style, ...style };
@@ -196,12 +199,7 @@ const ColorPicker: CompoundedComponent = (props) => {
}
}}
content={
-
+
}
overlayClassName={mergePopupCls}
{...popoverProps}
@@ -211,7 +209,7 @@ const ColorPicker: CompoundedComponent = (props) => {
open={popupOpen}
className={mergeCls}
style={mergedStyle}
- color={colorValue}
+ color={value ? generateColor(value) : colorValue}
prefixCls={prefixCls}
disabled={disabled}
colorCleared={colorCleared}
diff --git a/components/color-picker/ColorPickerPanel.tsx b/components/color-picker/ColorPickerPanel.tsx
index 45c2153915..8f3ab63d6b 100644
--- a/components/color-picker/ColorPickerPanel.tsx
+++ b/components/color-picker/ColorPickerPanel.tsx
@@ -10,21 +10,11 @@ import type { ColorPickerBaseProps } from './interface';
interface ColorPickerPanelProps extends ColorPickerBaseProps {
onChange?: (value?: Color, type?: HsbaColorType, pickColor?: boolean) => void;
- onChangeComplete?: (type?: HsbaColorType) => void;
onClear?: () => void;
}
const ColorPickerPanel: FC = (props) => {
- const {
- prefixCls,
- presets,
- panelRender,
- color,
- onChange,
- onClear,
- onChangeComplete,
- ...injectProps
- } = props;
+ const { prefixCls, presets, panelRender, color, onChange, onClear, ...injectProps } = props;
const colorPickerPanelPrefixCls = `${prefixCls}-inner-content`;
// ==== Inject props ===
@@ -33,7 +23,6 @@ const ColorPickerPanel: FC = (props) => {
value: color,
onChange,
onClear,
- onChangeComplete,
...injectProps,
};
diff --git a/components/color-picker/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/color-picker/__tests__/__snapshots__/demo-extend.test.ts.snap
index 5a659f54c1..a99bc38c68 100644
--- a/components/color-picker/__tests__/__snapshots__/demo-extend.test.ts.snap
+++ b/components/color-picker/__tests__/__snapshots__/demo-extend.test.ts.snap
@@ -767,6 +767,390 @@ Array [
]
`;
+exports[`renders components/color-picker/demo/change-completed.tsx extend context correctly 1`] = `
+
+`;
+
exports[`renders components/color-picker/demo/controlled.tsx extend context correctly 1`] = `
Array [
`;
+exports[`renders components/color-picker/demo/change-completed.tsx correctly 1`] = `
+
+`;
+
exports[`renders components/color-picker/demo/controlled.tsx correctly 1`] = `
{
expect(componentContainer.querySelector('.ant-color-picker-inner-content')).toBeTruthy();
expect(componentContainer).toMatchSnapshot();
});
+
+ it('Should onChangeComplete work', async () => {
+ spyElementPrototypes(HTMLElement, {
+ getBoundingClientRect: () => ({
+ x: 0,
+ y: 100,
+ width: 100,
+ height: 100,
+ }),
+ });
+ const handleChangeComplete = jest.fn();
+ const { container } = render(
);
+ doMouseMove(container, 0, 999);
+ expect(handleChangeComplete).toHaveBeenCalledTimes(1);
+ });
});
diff --git a/components/color-picker/components/PanelPicker.tsx b/components/color-picker/components/PanelPicker.tsx
index fe850cb4f4..4e8fbc25bb 100644
--- a/components/color-picker/components/PanelPicker.tsx
+++ b/components/color-picker/components/PanelPicker.tsx
@@ -9,10 +9,12 @@ import ColorClear from './ColorClear';
import ColorInput from './ColorInput';
export interface PanelPickerProps
- extends Pick
{
+ extends Pick<
+ ColorPickerBaseProps,
+ 'prefixCls' | 'colorCleared' | 'allowClear' | 'onChangeComplete'
+ > {
value?: Color;
onChange?: (value?: Color, type?: HsbaColorType, pickColor?: boolean) => void;
- onChangeComplete?: (type?: HsbaColorType) => void;
onClear?: () => void;
}
diff --git a/components/color-picker/demo/change-completed.md b/components/color-picker/demo/change-completed.md
new file mode 100644
index 0000000000..b3cb4a1ec6
--- /dev/null
+++ b/components/color-picker/demo/change-completed.md
@@ -0,0 +1,7 @@
+## zh-CN
+
+颜色选择完成才会触发回调
+
+## en-US
+
+The callback will be called only when the color selection is completed.
diff --git a/components/color-picker/demo/change-completed.tsx b/components/color-picker/demo/change-completed.tsx
new file mode 100644
index 0000000000..eaf7f210e8
--- /dev/null
+++ b/components/color-picker/demo/change-completed.tsx
@@ -0,0 +1,21 @@
+import { App, ColorPicker } from 'antd';
+import type { ColorPickerProps } from 'antd/es/color-picker';
+import React, { useState } from 'react';
+
+const Demo = () => {
+ const { message } = App.useApp();
+ const [value, setValue] = useState('#1677ff');
+ return (
+
+ {
+ setValue(color);
+ message.success(`The selected color is ${color.toHexString()}`);
+ }}
+ />
+
+ );
+};
+
+export default Demo;
diff --git a/components/color-picker/index.en-US.md b/components/color-picker/index.en-US.md
index 70f126ed47..1f976b1b54 100644
--- a/components/color-picker/index.en-US.md
+++ b/components/color-picker/index.en-US.md
@@ -22,6 +22,7 @@ Used when the user needs to customize the color selection.
Basic Usage
Trigger size
controlled mode
+Color change completed
Rendering Trigger Text
Disable
Clear Color
@@ -55,6 +56,7 @@ Used when the user needs to customize the color selection.
| trigger | ColorPicker trigger mode | `hover` \| `click` | `click` | |
| value | Value of color | string \| `Color` | - | |
| onChange | Callback when `value` is changed | `(value: Color, hex: string) => void` | - | |
+| onChangeComplete | Called when color pick ends | `(value: Color) => void` | - | 5.7.0 |
| onFormatChange | Callback when `format` is changed | `(format: 'hex' \| 'rgb' \| 'hsb') => void` | - | |
| onOpenChange | Callback when `open` is changed | `(open: boolean) => void` | - | |
| onClear | Called when clear | `() => void` | - | 5.6.0 |
diff --git a/components/color-picker/index.zh-CN.md b/components/color-picker/index.zh-CN.md
index 1779c16fcf..83e2f944fe 100644
--- a/components/color-picker/index.zh-CN.md
+++ b/components/color-picker/index.zh-CN.md
@@ -23,6 +23,7 @@ group:
基本使用
触发器尺寸大小
受控模式
+颜色完成选择
渲染触发器文本
禁用
清除颜色
@@ -56,6 +57,7 @@ group:
| trigger | 颜色选择器的触发模式 | `hover` \| `click` | `click` | |
| value | 颜色的值 | string \| `Color` | - | |
| onChange | 颜色变化的回调 | `(value: Color, hex: string) => void` | - | |
+| onChangeComplete | 颜色选择完成的回调 | `(value: Color) => void` | - | 5.7.0 |
| onFormatChange | 颜色格式变化的回调 | `(format: 'hex' \| 'rgb' \| 'hsb') => void` | - | |
| onOpenChange | 当 `open` 被改变时的回调 | `(open: boolean) => void` | - | |
| onClear | 清除的回调 | `() => void` | - | 5.6.0 |
diff --git a/components/color-picker/interface.ts b/components/color-picker/interface.ts
index 4a4d911edb..fd66038821 100644
--- a/components/color-picker/interface.ts
+++ b/components/color-picker/interface.ts
@@ -31,4 +31,5 @@ export interface ColorPickerBaseProps {
presets?: PresetsItem[];
panelRender?: ColorPickerProps['panelRender'];
onFormatChange?: ColorPickerProps['onFormatChange'];
+ onChangeComplete?: ColorPickerProps['onChangeComplete'];
}
diff --git a/package.json b/package.json
index e51970ba11..0257255096 100644
--- a/package.json
+++ b/package.json
@@ -114,7 +114,7 @@
"@ant-design/react-slick": "~1.0.0",
"@babel/runtime": "^7.18.3",
"@ctrl/tinycolor": "^3.6.0",
- "@rc-component/color-picker": "~1.3.0",
+ "@rc-component/color-picker": "~1.4.0",
"@rc-component/mutate-observer": "^1.0.0",
"@rc-component/tour": "~1.8.0",
"@rc-component/trigger": "^1.13.0",
@@ -325,4 +325,4 @@
"*.{ts,tsx,js,jsx}": "prettier --ignore-unknown --write",
"*.{json,less,md}": "prettier --ignore-unknown --write"
}
-}
+}
\ No newline at end of file
From c3e56bc94b57a040dc457c766038eea0099eb332 Mon Sep 17 00:00:00 2001
From: MadCcc <1075746765@qq.com>
Date: Mon, 10 Jul 2023 14:25:07 +0800
Subject: [PATCH 51/80] docs: update examples link (#43463)
* docs: update examples link
* chore: code clean
---
docs/blog/extract-ssr.en-US.md | 2 +-
docs/blog/extract-ssr.zh-CN.md | 2 +-
docs/blog/mock-project-build.en-US.md | 2 +-
docs/blog/mock-project-build.zh-CN.md | 2 +-
docs/react/customize-theme.en-US.md | 4 ++--
docs/react/customize-theme.zh-CN.md | 4 ++--
docs/react/getting-started.en-US.md | 2 +-
docs/react/getting-started.zh-CN.md | 2 +-
scripts/ci-mock-project-build.sh | 4 ++--
9 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/docs/blog/extract-ssr.en-US.md b/docs/blog/extract-ssr.en-US.md
index 541d6d2358..c3076ee616 100644
--- a/docs/blog/extract-ssr.en-US.md
+++ b/docs/blog/extract-ssr.en-US.md
@@ -150,7 +150,7 @@ Then add the corresponding CSS file on the HTML template side: