chore: merge master into feature

This commit is contained in:
栗嘉男 2024-09-14 11:50:34 +08:00
commit 78c1fca2f0
56 changed files with 2624 additions and 537 deletions

View File

@ -81,3 +81,11 @@ jobs:
Hello @${{ github.event.issue.user.login }}, your issue has been closed because it does not conform to our issue requirements. Please use the [Issue Helper](http://new-issue.ant.design) to create an issue, thank you!
你好 @${{ github.event.issue.user.login }},为了能够进行高效沟通,我们对 issue 有一定的格式要求,你的 issue 因为不符合要求而被自动关闭。你可以通过 [issue 助手](http://new-issue.ant.design) 来创建 issue 以方便我们定位错误。谢谢配合!
- name: remove unconfirmed label
if: github.event.label.name != 'unconfirmed'
uses: actions-cool/issues-helper@v3
with:
actions: 'remove-labels'
issue-number: ${{ github.event.issue.number }}
labels: 'unconfirmed'

View File

@ -16,6 +16,19 @@ tag: vVERSION
---
## 5.20.6
`2024-09-09`
- 🐞 Improve Menu collapse animation smoothness. [#50751](https://github.com/ant-design/ant-design/pull/50751) [@afc163](https://github.com/afc163)
- 🐞 Fix Table cell overflow bug if edit with virtual scroll. [#50737](https://github.com/ant-design/ant-design/pull/50737) [@huiliangShen](https://github.com/huiliangShen)
- 🐞 Fix Input.Search button radius not changing with `size`. [#50734](https://github.com/ant-design/ant-design/pull/50734) [@afc163](https://github.com/afc163)
- 🐞 Fix Form password still can be toggle show/hide even if disabled. [#50616](https://github.com/ant-design/ant-design/pull/50616) [@Jarryxin](https://github.com/Jarryxin)
- 🐞 Revert [#49899](https://github.com/ant-design/ant-design/pull/49899) to fix wrap behavior for Dropdown, and re-fix wrap when out of screen edge. [#50718](https://github.com/ant-design/ant-design/pull/50718) [@afc163](https://github.com/afc163)
- 💄 Fix Badge background transition when mouse out. [#50743](https://github.com/ant-design/ant-design/pull/50743) [@coding-ice](https://github.com/coding-ice)
- TypeScript
- 🤖 Fix Collapse types for `onChange` arguments. [#50754](https://github.com/ant-design/ant-design/pull/50754) [@yuanliu147](https://github.com/yuanliu147)
## 5.20.5
`2024-09-03`

View File

@ -15,6 +15,19 @@ tag: vVERSION
---
## 5.20.6
`2024-09-09`
- 🐞 修复 Menu 折叠动画不够丝滑的问题。[#50751](https://github.com/ant-design/ant-design/pull/50751) [@afc163](https://github.com/afc163)
- 🐞 修复 Table 虚拟滚动时单元格宽度可能溢出的问题。[#50737](https://github.com/ant-design/ant-design/pull/50737) [@huiliangShen](https://github.com/huiliangShen)
- 🐞 修复 Input.Search 的按钮圆角不随 `size` 变化的问题。[#50734](https://github.com/ant-design/ant-design/pull/50734) [@afc163](https://github.com/afc163)
- 🐞 修复 Form 禁用时仍可切换密码显隐的问题。[#50616](https://github.com/ant-design/ant-design/pull/50616) [@Jarryxin](https://github.com/Jarryxin)
- 🐞 回滚 [#49899](https://github.com/ant-design/ant-design/pull/49899) 以修复 Dropdown 菜单项文字溢出菜单的问题,并重新修复屏幕视口外菜单项内容换行错误的问题。[#50752](https://github.com/ant-design/ant-design/pull/50752) [#50718](https://github.com/ant-design/ant-design/pull/50718) [@afc163](https://github.com/afc163)
- 💄 修复 Badge 鼠标移出时无背景色过渡动画的问题。[#50743](https://github.com/ant-design/ant-design/pull/50743) [@coding-ice](https://github.com/coding-ice)
- TypeScript
- 🤖 修复 Collapse 的 `onChange` 的函数入参类型。[#50754](https://github.com/ant-design/ant-design/pull/50754) [@yuanliu147](https://github.com/yuanliu147)
## 5.20.5
`2024-09-03`

View File

@ -14,7 +14,9 @@
[更新日志](./CHANGELOG.zh-CN.md) · [报告问题][github-issues-url] · [特性需求][github-issues-url] · [English](./README.md) · 中文
![](https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png)
## ❤️ 赞助者 ![](https://opencollective.com/ant-design/tiers/backers/badge.svg?label=Backers&color=brightgreen) ![](https://opencollective.com/ant-design/tiers/sponsors/badge.svg?label=Sponsors&color=brightgreen)
[![](https://opencollective.com/ant-design/tiers/sponsors.svg?avatarHeight=72)](https://opencollective.com/ant-design/contribute/sponsors-218/checkout) [![](https://opencollective.com/ant-design/tiers/backers.svg?avatarHeight=72)](https://opencollective.com/ant-design/contribute/backers-217/checkout)
[npm-image]: http://img.shields.io/npm/v/antd.svg?style=flat-square
[npm-url]: http://npmjs.org/package/antd
@ -136,11 +138,7 @@ export default App;
## ⌨️ 本地开发
你可以使用 Gitpod 进行在线开发:
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/ant-design/ant-design)
或者使用 opensumi.run 进行在线开发:
推荐使用 [opensumi.run](https://opensumi.run) 进行在线开发:
[![opensumi.run](https://custom-icon-badges.demolab.com/badge/opensumi-run-blue.svg?logo=opensumi)](https://opensumi.run/ide/ant-design/ant-design)
@ -206,8 +204,10 @@ $ npm start
2. [Stack Overflow](http://stackoverflow.com/questions/tagged/antd)(英文)
3. [Segment Fault](https://segmentfault.com/t/antd)(中文)
## ❤️ 赞助者 ![](https://opencollective.com/ant-design/tiers/backers/badge.svg?label=Backers&color=brightgreen) ![](https://opencollective.com/ant-design/tiers/sponsors/badge.svg?label=Sponsors&color=brightgreen)
## Issue 赞助
[![](https://opencollective.com/ant-design/tiers/backers.svg?avatarHeight=36)](https://opencollective.com/ant-design#support)
我们使用 [Polar.sh](https://polar.sh/ant-design) 和 [Issuehunt](https://issuehunt.io/repos/3452688) 来推动您希望看到的针对 antd 的修复和改进,请查看我们的赞助列表:
[![](https://opencollective.com/ant-design/tiers/sponsors.svg?avatarHeight=36)](https://opencollective.com/ant-design#support)
<a href="https://polar.sh/ant-design"><img src="https://polar.sh/embed/fund-our-backlog.svg?org=ant-design" /></a>
[![Let's fund issues in this repository](https://raw.githubusercontent.com/BoostIO/issuehunt-materials/master/v1/issuehunt-button-v1.svg)](https://issuehunt.io/repos/34526884)

View File

@ -14,7 +14,9 @@ An enterprise-class UI design language and React UI library.
[Changelog](./CHANGELOG.en-US.md) · [Report Bug][github-issues-url] · [Request Feature][github-issues-url] · English · [中文](./README-zh_CN.md)
![](https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png)
## ❤️ Sponsors and Backers [![](https://opencollective.com/ant-design/tiers/sponsors/badge.svg?label=Sponsors&color=brightgreen)](https://opencollective.com/ant-design#support) [![](https://opencollective.com/ant-design/tiers/backers/badge.svg?label=Backers&color=brightgreen)](https://opencollective.com/ant-design#support)
[![](https://opencollective.com/ant-design/tiers/sponsors.svg?avatarHeight=72)](https://opencollective.com/ant-design/contribute/sponsors-218/checkout) [![](https://opencollective.com/ant-design/tiers/backers.svg?avatarHeight=72)](https://opencollective.com/ant-design/contribute/backers-217/checkout)
[npm-image]: http://img.shields.io/npm/v/antd.svg?style=flat-square
[npm-url]: http://npmjs.org/package/antd
@ -117,11 +119,7 @@ export default () => (
## ⌨️ Development
Use Gitpod, a free online dev environment for GitHub.
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/ant-design/ant-design)
Or use opensumi.run, a free online pure front-end dev environment.
Use [opensumi.run](https://opensumi.run), a free online pure front-end dev environment.
[![opensumi.run](https://custom-icon-badges.demolab.com/badge/opensumi-run-blue.svg?logo=opensumi)](https://opensumi.run/ide/ant-design/ant-design)
@ -175,10 +173,10 @@ We warmly invite contributions from everyone. Before you get started, please tak
For collaborators, adhere to our [Pull Request Principle](https://github.com/ant-design/ant-design/wiki/PR-principle) and utilize our [Pull Request Template](https://github.com/ant-design/ant-design/wiki/PR-principle#pull-request-template) when creating a Pull Request.
## Issue funding
We use [Polar.sh](https://polar.sh/ant-design) and [Issuehunt](https://issuehunt.io/repos/3452688) to up-vote and promote specific features that you would like to see and implement. Check our backlog and help us:
<a href="https://polar.sh/ant-design"><img src="https://polar.sh/embed/fund-our-backlog.svg?org=ant-design" /></a>
[![Let's fund issues in this repository](https://raw.githubusercontent.com/BoostIO/issuehunt-materials/master/v1/issuehunt-button-v1.svg)](https://issuehunt.io/repos/34526884)
## ❤️ Sponsors and Backers [![](https://opencollective.com/ant-design/tiers/sponsors/badge.svg?label=Sponsors&color=brightgreen)](https://opencollective.com/ant-design#support) [![](https://opencollective.com/ant-design/tiers/backers/badge.svg?label=Backers&color=brightgreen)](https://opencollective.com/ant-design#support)
[![](https://opencollective.com/ant-design/tiers/sponsors.svg?avatarHeight=36)](https://opencollective.com/ant-design#support)
[![](https://opencollective.com/ant-design/tiers/backers.svg?avatarHeight=36)](https://opencollective.com/ant-design#support)

View File

@ -57,7 +57,11 @@
"noAccumulatingSpread": "off"
},
"a11y": {
"useKeyWithClickEvents": "off"
"noAriaHiddenOnFocusable": "off",
"noLabelWithoutControl": "off",
"useFocusableInteractive": "off",
"useKeyWithClickEvents": "off",
"useSemanticElements": "off"
}
}
},

View File

@ -1,8 +1,11 @@
import type { PropsWithChildren } from 'react';
import React, { useEffect } from 'react';
import { render } from '@testing-library/react';
import type { ImageProps, MenuProps } from 'antd';
import { waitFakeTimer } from '../../../tests/utils';
import type { ImageProps, MenuProps } from '../../index';
import {
App,
AutoComplete,
Cascader,
ColorPicker,
@ -19,11 +22,10 @@ import {
Tooltip,
Tour,
TreeSelect,
} from 'antd';
import { waitFakeTimer } from '../../../tests/utils';
} from '../../index';
import type { ZIndexConsumer, ZIndexContainer } from '../hooks/useZIndex';
import { consumerBaseZIndexOffset, containerBaseZIndexOffset, useZIndex } from '../hooks/useZIndex';
import { resetWarned } from '../warning';
import zIndexContext from '../zindexContext';
const WrapWithProvider: React.FC<PropsWithChildren<{ container: ZIndexContainer }>> = ({
@ -222,6 +224,7 @@ const getConsumerSelector = (baseSelector: string, consumer: ZIndexConsumer): st
describe('Test useZIndex hooks', () => {
beforeEach(() => {
resetWarned();
jest.useFakeTimers();
});
afterEach(() => {
@ -242,17 +245,28 @@ describe('Test useZIndex hooks', () => {
return <div>Child</div>;
};
<<<<<<< HEAD
const App: React.FC = () => (
<WrapWithProvider container={containerKey as ZIndexContainer}>
<WrapWithProvider container={containerKey as ZIndexContainer}>
<WrapWithProvider container={containerKey as ZIndexContainer}>
=======
const Demo = () => (
<WrapWithProvider containerType={containerKey as ZIndexContainer}>
<WrapWithProvider containerType={containerKey as ZIndexContainer}>
<WrapWithProvider containerType={containerKey as ZIndexContainer}>
>>>>>>> master
<Child />
</WrapWithProvider>
</WrapWithProvider>
</WrapWithProvider>
);
<<<<<<< HEAD
render(<App />);
=======
render(<Demo />);
>>>>>>> master
expect(fn).toHaveBeenLastCalledWith(
1000 + containerZIndexValue * 3 + consumerZIndexValue,
);
@ -262,7 +276,11 @@ describe('Test useZIndex hooks', () => {
const Container = containerComponent[containerKey as ZIndexContainer]!;
const Consumer = consumerComponent[key as ZIndexConsumer]!;
<<<<<<< HEAD
const App: React.FC = () => (
=======
const Demo = () => (
>>>>>>> master
<>
<Consumer rootClassName="consumer1" />
<Container rootClassName="container1">
@ -274,7 +292,7 @@ describe('Test useZIndex hooks', () => {
</>
);
const { unmount } = render(<App />);
const { unmount } = render(<Demo />);
await waitFakeTimer(1000);
@ -378,6 +396,7 @@ describe('Test useZIndex hooks', () => {
errorSpy.mockRestore();
});
<<<<<<< HEAD
it('FloatButton support zIndex', () => {
const { container, rerender } = render(
<WrapWithProvider container="FloatButton">
@ -394,5 +413,29 @@ describe('Test useZIndex hooks', () => {
</WrapWithProvider>,
);
expect(container.querySelector<HTMLElement>('.ant-float-btn')?.style.zIndex).toBe(String(666));
=======
it('not warning for static func', () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const Demo = () => {
const { modal } = App.useApp();
React.useEffect(() => {
modal.confirm({ content: <Select open /> });
}, []);
return null;
};
render(
<App>
<Demo />
</App>,
);
expect(errorSpy).not.toHaveBeenCalled();
errorSpy.mockRestore();
>>>>>>> master
});
});

View File

@ -25,6 +25,13 @@ const CONTAINER_OFFSET_MAX_COUNT = 10;
export const CONTAINER_MAX_OFFSET = CONTAINER_OFFSET * CONTAINER_OFFSET_MAX_COUNT;
/**
* Static function will default be the `CONTAINER_MAX_OFFSET`.
* But it still may have children component like Select, Dropdown.
* So the warning zIndex should exceed the `CONTAINER_MAX_OFFSET`.
*/
const CONTAINER_MAX_OFFSET_WITH_CHILDREN = CONTAINER_MAX_OFFSET + CONTAINER_OFFSET;
export const containerBaseZIndexOffset: Record<ZIndexContainer, number> = {
Modal: CONTAINER_OFFSET,
Drawer: CONTAINER_OFFSET,
@ -79,7 +86,7 @@ export const useZIndex = (
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning(componentType);
const maxZIndex = token.zIndexPopupBase + CONTAINER_MAX_OFFSET;
const maxZIndex = token.zIndexPopupBase + CONTAINER_MAX_OFFSET_WITH_CHILDREN;
const currentZIndex = result[0] || 0;
warning(

View File

@ -0,0 +1,5 @@
export default function toList<T>(candidate: T | T[], skipEmpty = false): T[] {
if (skipEmpty && (candidate === undefined || candidate === null)) return [];
return Array.isArray(candidate) ? candidate : [candidate];
}

View File

@ -15,7 +15,7 @@ import useStyle from './style';
export type { ScrollNumberProps } from './ScrollNumber';
export interface BadgeProps {
export interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
/** Number to show in badge */
count?: React.ReactNode;
showZero?: boolean;

View File

@ -27,7 +27,7 @@ export interface CollapseProps extends Pick<RcCollapseProps, 'items'> {
/** 手风琴效果 */
accordion?: boolean;
destroyInactivePanel?: boolean;
onChange?: (key: string | string[]) => void;
onChange?: (key: string[]) => void;
style?: React.CSSProperties;
className?: string;
rootClassName?: string;

View File

@ -108,7 +108,7 @@ const ColorPicker: CompoundedComponent = (props) => {
}
};
const onInternalChange: ColorPickerPanelProps['onChange'] = (data, pickColor) => {
const onInternalChange: ColorPickerPanelProps['onChange'] = (data, changeFromPickerDrag) => {
let color: AggregationColor = generateColor(data as AggregationColor);
// ignore alpha color
@ -125,7 +125,7 @@ const ColorPicker: CompoundedComponent = (props) => {
}
// Only for drag-and-drop color picking
if (!pickColor) {
if (!changeFromPickerDrag) {
onInternalChangeComplete(color);
}
};

View File

@ -32,15 +32,39 @@ exports[`renders components/color-picker/demo/base.tsx correctly 1`] = `
exports[`renders components/color-picker/demo/controlled.tsx correctly 1`] = `
<div
class="ant-color-picker-trigger"
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"
>
<div
class="ant-color-picker-color-block"
class="ant-space-item"
>
<div
class="ant-color-picker-color-block-inner"
style="background:rgb(22,119,255)"
/>
class="ant-color-picker-trigger"
>
<div
class="ant-color-picker-color-block"
>
<div
class="ant-color-picker-color-block-inner"
style="background:rgb(22,119,255)"
/>
</div>
</div>
</div>
<div
class="ant-space-item"
>
<div
class="ant-color-picker-trigger"
>
<div
class="ant-color-picker-color-block"
>
<div
class="ant-color-picker-color-block-inner"
style="background:rgb(22,119,255)"
/>
</div>
</div>
</div>
</div>
`;

View File

@ -20,6 +20,7 @@ function doMouseMove(
start: number,
end: number,
element: string | HTMLElement = 'ant-color-picker-handler',
fireMouseUp = true,
) {
const ele =
element instanceof HTMLElement ? element : container.getElementsByClassName(element)[0];
@ -44,8 +45,10 @@ function doMouseMove(
fireEvent(document, mouseMove);
}
const mouseUp = createEvent.mouseUp(document);
fireEvent(document, mouseUp);
if (fireMouseUp) {
const mouseUp = createEvent.mouseUp(document);
fireEvent(document, mouseUp);
}
}
describe('ColorPicker', () => {
@ -879,4 +882,70 @@ describe('ColorPicker', () => {
spyRect.mockRestore();
});
describe('controlled with `onChangeComplete`', () => {
let spyRect: ReturnType<typeof spyElementPrototypes>;
beforeEach(() => {
spyRect = spyElementPrototypes(HTMLElement, {
getBoundingClientRect: () => ({
x: 0,
y: 100,
width: 100,
height: 100,
}),
});
});
afterEach(() => {
spyRect.mockRestore();
});
it('lock value', async () => {
const onChange = jest.fn();
const onChangeComplete = jest.fn();
const { container } = render(
<ColorPicker value="#F00" open onChange={onChange} onChangeComplete={onChangeComplete} />,
);
doMouseMove(container, 0, 50, 'ant-color-picker-slider-handle', false);
expect(onChange).toHaveBeenCalledWith(
expect.anything(),
// Safe to change with any value but (255/0/0)
'rgb(0,255,255)',
);
expect(onChangeComplete).not.toHaveBeenCalled();
// Inline Color Block (locked)
expect(container.querySelectorAll('.ant-color-picker-color-block-inner')[0]).toHaveStyle({
background: 'rgb(255, 0, 0)',
});
// Popup Color Block (follow operation)
expect(container.querySelectorAll('.ant-color-picker-color-block-inner')[1]).toHaveStyle({
background: 'rgb(0, 255, 255)',
});
// Mouse up
fireEvent.mouseUp(document);
// Lock color back
expect(container.querySelectorAll('.ant-color-picker-color-block-inner')[1]).toHaveStyle({
background: 'rgb(255, 0, 0)',
});
});
});
it('input precision', async () => {
const onChange = jest.fn();
const { container } = render(<ColorPicker open onChange={onChange} />);
fireEvent.change(container.querySelector('.ant-color-picker-hex-input input')!, {
target: { value: '2ddcb4' },
});
const onChangeColor = onChange.mock.calls[0][0];
expect(onChangeColor.toHexString()).toBe('#2ddcb4');
});
});

View File

@ -85,6 +85,16 @@ const PanelPicker: FC = () => {
return colors[activeIndex]?.color;
}, [value, activeIndex, isSingle, lockedColor, gradientDragging]);
// ========================= Picker Color =========================
const [pickerColor, setPickerColor] = React.useState<AggregationColor | null>(activeColor);
const [forceSync, setForceSync] = React.useState(0);
const mergedPickerColor = pickerColor?.equals(activeColor) ? activeColor : pickerColor;
useLayoutEffect(() => {
setPickerColor(activeColor);
}, [forceSync, activeColor?.toHexString()]);
// ============================ Change ============================
const fillColor = (nextColor: AggregationColor | Color, info?: Info) => {
let submitColor = generateColor(nextColor);
@ -121,16 +131,28 @@ const PanelPicker: FC = () => {
return new AggregationColor(nextColors);
};
const onInternalChange = (
const onPickerChange = (
colorValue: AggregationColor | Color,
fromPicker?: boolean,
fromPicker: boolean,
info?: Info,
) => {
onChange(fillColor(colorValue, info), fromPicker);
const nextColor = fillColor(colorValue, info);
setPickerColor(nextColor);
onChange(nextColor, fromPicker);
};
const onInternalChangeComplete = (nextColor: Color, info?: Info) => {
// Trigger complete event
onChangeComplete(fillColor(nextColor, info));
// Back of origin color in case in controlled
// This will set after `onChangeComplete` to avoid `setState` trigger rerender
// which will make `fillColor` get wrong `color.cleared` state
setForceSync((ori) => ori + 1);
};
const onInputChange = (colorValue: AggregationColor) => {
onChange(fillColor(colorValue));
};
// ============================ Render ============================
@ -166,10 +188,10 @@ const PanelPicker: FC = () => {
<RcColorPicker
prefixCls={prefixCls}
value={activeColor?.toHsb()}
value={mergedPickerColor?.toHsb()}
disabledAlpha={disabledAlpha}
onChange={(colorValue, info) => {
onInternalChange(colorValue, true, info);
onPickerChange(colorValue, true, info);
}}
onChangeComplete={(colorValue, info) => {
onInternalChangeComplete(colorValue, info);
@ -178,7 +200,7 @@ const PanelPicker: FC = () => {
/>
<ColorInput
value={activeColor}
onChange={onInternalChange}
onChange={onInputChange}
prefixCls={prefixCls}
disabledAlpha={disabledAlpha}
{...injectProps}

View File

@ -1,7 +1,7 @@
## zh-CN
通过 `value``onChange` 设置组件为受控模式。
通过 `value``onChange` 设置组件为受控模式,如果通过 `onChangeComplete` 受控则会锁定展示颜色
## en-US
Set the component to controlled mode.
Set the component to controlled mode. Will lock the display color if controlled by `onChangeComplete`.

View File

@ -1,12 +1,18 @@
import React, { useState } from 'react';
import { ColorPicker } from 'antd';
import { ColorPicker, Space } from 'antd';
import type { ColorPickerProps, GetProp } from 'antd';
type Color = GetProp<ColorPickerProps, 'value'>;
const Demo: React.FC = () => {
const [color, setColor] = useState<Color>('#1677ff');
return <ColorPicker value={color} onChange={setColor} />;
return (
<Space>
<ColorPicker value={color} onChange={setColor} />
<ColorPicker value={color} onChangeComplete={setColor} />
</Space>
);
};
export default Demo;

View File

@ -61,7 +61,7 @@ Common props ref[Common props](/docs/react/common-props)
| trigger | ColorPicker trigger mode | `hover` \| `click` | `click` | |
| value | Value of color | string \| `Color` | - | |
| onChange | Callback when `value` is changed | `(value: Color, css: string) => void` | - | |
| onChangeComplete | Called when color pick ends | `(value: Color) => void` | - | 5.7.0 |
| onChangeComplete | Called when color pick ends. Will not change the display color when `value` controlled by `onChangeComplete` | `(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 |

View File

@ -62,7 +62,7 @@ group:
| trigger | 颜色选择器的触发模式 | `hover` \| `click` | `click` | |
| value | 颜色的值 | string \| `Color` | - | |
| onChange | 颜色变化的回调 | `(value: Color, css: string) => void` | - | |
| onChangeComplete | 颜色选择完成的回调 | `(value: Color) => void` | - | 5.7.0 |
| onChangeComplete | 颜色选择完成的回调,通过 `onChangeComplete``value` 受控时拖拽不会改变展示颜色 | `(value: Color) => void` | - | 5.7.0 |
| onFormatChange | 颜色格式变化的回调 | `(format: 'hex' \| 'rgb' \| 'hsb') => void` | - | |
| onOpenChange | 当 `open` 被改变时的回调 | `(open: boolean) => void` | - | |
| onClear | 清除的回调 | `() => void` | - | 5.6.0 |

View File

@ -19,9 +19,18 @@ export const getColorAlpha = (color: AggregationColor) => getRoundNumber(color.t
/** Return the color whose `alpha` is 1 */
export const genAlphaColor = (color: AggregationColor, alpha?: number) => {
const hsba = color.toHsb();
hsba.a = alpha || 1;
return generateColor(hsba);
const rgba = color.toRgb();
// Color from hsb input may get `rgb` is (0/0/0) when `hsb.b` is 0
// So if rgb is empty, we should get from hsb
if (!rgba.r && !rgba.g && !rgba.b) {
const hsba = color.toHsb();
hsba.a = alpha || 1;
return generateColor(hsba);
}
rgba.a = alpha || 1;
return generateColor(rgba);
};
/**

View File

@ -7,7 +7,15 @@ import type { PickerLocale } from '../generatePicker';
const locale: PickerLocale = {
lang: {
placeholder: 'Επιλέξτε ημερομηνία',
yearPlaceholder: 'Επιλέξτε έτος',
quarterPlaceholder: 'Επιλέξτε τρίμηνο',
monthPlaceholder: 'Επιλέξτε μήνα',
weekPlaceholder: 'Επιλέξτε εβδομάδα',
rangePlaceholder: ['Αρχική ημερομηνία', 'Τελική ημερομηνία'],
rangeYearPlaceholder: ['Αρχικό έτος', 'Τελικό έτος'],
rangeMonthPlaceholder: ['Αρχικός μήνας', 'Τελικός μήνας'],
rangeQuarterPlaceholder: ['Αρχικό τρίμηνο', 'Τελικό τρίμηνο'],
rangeWeekPlaceholder: ['Αρχική εβδομάδα', 'Τελική εβδομάδα'],
...CalendarLocale,
},
timePickerLocale: {

View File

@ -265,7 +265,7 @@ const genBaseStyle: GenerateStyle<DropdownToken> = (token) => {
// =========================== Item ===========================
[`${menuCls}-item, ${menuCls}-submenu-title`]: {
clear: 'both',
display: 'flex',
margin: 0,
padding: `${unit(paddingBlock!)} ${unit(controlPaddingHorizontal)}`,
color: token.colorText,

View File

@ -58,6 +58,7 @@ const OTPInput = React.forwardRef<InputRef, OTPInputProps>((props, ref) => {
// ========================= Render =========================
return (
<Input
type={mask === true ? 'password' : 'text'}
{...restProps}
ref={inputRef}
value={internalValue}
@ -67,7 +68,6 @@ const OTPInput = React.forwardRef<InputRef, OTPInputProps>((props, ref) => {
onKeyUp={onInternalKeyUp}
onMouseDown={syncSelection}
onMouseUp={syncSelection}
type={mask === true ? 'password' : 'text'}
/>
);
});

View File

@ -7,12 +7,12 @@ import { getMergedStatus } from '../../_util/statusUtils';
import type { InputStatus } from '../../_util/statusUtils';
import { devUseWarning } from '../../_util/warning';
import { ConfigContext } from '../../config-provider';
import type { Variant } from '../../config-provider';
import useCSSVarCls from '../../config-provider/hooks/useCSSVarCls';
import useSize from '../../config-provider/hooks/useSize';
import type { SizeType } from '../../config-provider/SizeContext';
import { FormItemInputContext } from '../../form/context';
import type { FormItemStatusContextProps } from '../../form/context';
import type { Variant } from '../../config-provider';
import type { InputRef } from '../Input';
import useStyle from '../style/otp';
import OTPInput from './OTPInput';
@ -46,6 +46,8 @@ export interface OTPProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'on
status?: InputStatus;
mask?: boolean | string;
type?: React.HTMLInputTypeAttribute;
}
function strToArr(str: string) {
@ -66,6 +68,7 @@ const OTP = React.forwardRef<OTPRef, OTPProps>((props, ref) => {
status: customStatus,
autoFocus,
mask,
type,
...restProps
} = props;
@ -213,6 +216,7 @@ const OTP = React.forwardRef<OTPRef, OTPProps>((props, ref) => {
disabled,
status: mergedStatus as InputStatus,
mask,
type,
};
return wrapCSSVar(

View File

@ -167,4 +167,9 @@ describe('Input.OTP', () => {
expect(errSpy).not.toHaveBeenCalled();
errSpy.mockRestore();
});
it('support type', () => {
const { container } = render(<OTP type="number" />);
expect(container.querySelector('input')).toHaveAttribute('type', 'number');
});
});

View File

@ -9,6 +9,7 @@ import omit from 'rc-util/lib/omit';
import isNumeric from '../_util/isNumeric';
import { ConfigContext } from '../config-provider';
import { LayoutContext } from './context';
import useStyle from './style/sider';
const dimensionMaxMap = {
xs: '479.98px',
@ -97,6 +98,12 @@ const Sider = React.forwardRef<HTMLDivElement, SiderProps>((props, ref) => {
onCollapse?.(value, type);
};
// =========================== Prefix ===========================
const { getPrefixCls } = useContext(ConfigContext);
const prefixCls = getPrefixCls('layout-sider', customizePrefixCls);
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls);
// ========================= Responsive =========================
const responsiveHandlerRef = useRef<(mql: MediaQueryListEvent | MediaQueryList) => void>();
responsiveHandlerRef.current = (mql: MediaQueryListEvent | MediaQueryList) => {
@ -145,10 +152,7 @@ const Sider = React.forwardRef<HTMLDivElement, SiderProps>((props, ref) => {
handleSetCollapsed(!collapsed, 'clickTrigger');
};
const { getPrefixCls } = useContext(ConfigContext);
const renderSider = () => {
const prefixCls = getPrefixCls('layout-sider', customizePrefixCls);
const divProps = omit(otherProps, ['collapsed']);
const rawWidth = collapsed ? collapsedWidth : width;
// use "px" as fallback unit for width
@ -200,6 +204,8 @@ const Sider = React.forwardRef<HTMLDivElement, SiderProps>((props, ref) => {
[`${prefixCls}-zero-width`]: parseFloat(siderWidth) === 0,
},
className,
hashId,
cssVarCls,
);
return (
<aside className={siderCls} {...divProps} style={divStyle} ref={ref}>
@ -211,7 +217,9 @@ const Sider = React.forwardRef<HTMLDivElement, SiderProps>((props, ref) => {
const contextValue = React.useMemo(() => ({ siderCollapsed: collapsed }), [collapsed]);
return <SiderContext.Provider value={contextValue}>{renderSider()}</SiderContext.Provider>;
return wrapCSSVar(
<SiderContext.Provider value={contextValue}>{renderSider()}</SiderContext.Provider>,
);
});
if (process.env.NODE_ENV !== 'production') {

View File

@ -165,7 +165,15 @@ exports[`renders components/layout/demo/component-token.tsx extend context corre
>
<aside
class="ant-layout-sider ant-layout-sider-dark"
style="background: rgb(255, 255, 255); flex: 0 0 200px; max-width: 200px; min-width: 200px; width: 200px;"
style="flex: 0 0 32px; max-width: 32px; min-width: 32px; width: 32px;"
>
<div
class="ant-layout-sider-children"
/>
</aside>
<aside
class="ant-layout-sider ant-layout-sider-dark"
style="flex: 0 0 200px; max-width: 200px; min-width: 200px; width: 200px;"
>
<div
class="ant-layout-sider-children"
@ -174,7 +182,7 @@ exports[`renders components/layout/demo/component-token.tsx extend context corre
class="ant-menu ant-menu-root ant-menu-inline ant-menu-light"
data-menu-list="true"
role="menu"
style="height: 100%; border-right: 0;"
style="border-right: 0;"
tabindex="0"
>
<li

View File

@ -163,7 +163,15 @@ exports[`renders components/layout/demo/component-token.tsx correctly 1`] = `
>
<aside
class="ant-layout-sider ant-layout-sider-dark"
style="background:#ffffff;flex:0 0 200px;max-width:200px;min-width:200px;width:200px"
style="flex:0 0 32px;max-width:32px;min-width:32px;width:32px"
>
<div
class="ant-layout-sider-children"
/>
</aside>
<aside
class="ant-layout-sider ant-layout-sider-dark"
style="flex:0 0 200px;max-width:200px;min-width:200px;width:200px"
>
<div
class="ant-layout-sider-children"
@ -172,7 +180,7 @@ exports[`renders components/layout/demo/component-token.tsx correctly 1`] = `
class="ant-menu ant-menu-root ant-menu-inline ant-menu-light"
data-menu-list="true"
role="menu"
style="height:100%;border-right:0"
style="border-right:0"
tabindex="0"
>
<li

View File

@ -40,7 +40,7 @@ const App: React.FC = () => {
headerHeight: 64,
headerPadding: `0 24px`,
headerColor: colorBgContainer,
siderBg: colorBgContainer,
siderBg: '#800080',
},
},
}}
@ -51,12 +51,23 @@ const App: React.FC = () => {
<div style={{ marginInlineStart: 24, fontSize: 24 }}>Ant Design</div>
</Header>
<Layout>
<Sider width={200} style={{ background: colorBgContainer }}>
<ConfigProvider
theme={{
components: {
Layout: {
siderBg: 'red',
},
},
}}
>
<Sider width={32} />
</ConfigProvider>
<Sider width={200}>
<Menu
mode="inline"
defaultSelectedKeys={['1']}
defaultOpenKeys={['sub1']}
style={{ height: '100%', borderRight: 0 }}
style={{ borderRight: 0 }}
items={items2}
/>
</Sider>

View File

@ -4,7 +4,6 @@ import type { CSSObject } from '@ant-design/cssinjs';
import type { FullToken, GenerateStyle, GetDefaultToken } from '../../theme/internal';
import { genStyleHooks } from '../../theme/internal';
import genLayoutLightStyle from './light';
export interface ComponentToken {
/** @deprecated Use headerBg instead */
@ -103,23 +102,14 @@ const genLayoutStyle: GenerateStyle<LayoutToken, CSSObject> = (token) => {
antCls, // .ant
componentCls, // .ant-layout
colorText,
triggerColor,
footerBg,
triggerBg,
headerHeight,
headerPadding,
headerColor,
footerPadding,
triggerHeight,
zeroTriggerHeight,
zeroTriggerWidth,
motionDurationMid,
motionDurationSlow,
fontSize,
borderRadius,
bodyBg,
headerBg,
siderBg,
} = token;
return {
@ -148,98 +138,6 @@ const genLayoutStyle: GenerateStyle<LayoutToken, CSSObject> = (token) => {
flex: '0 0 auto',
},
[`${componentCls}-sider`]: {
position: 'relative',
// fix firefox can't set width smaller than content on flex item
minWidth: 0,
background: siderBg,
transition: `all ${motionDurationMid}, background 0s`,
'&-children': {
height: '100%',
// Hack for fixing margin collapse bug
// https://github.com/ant-design/ant-design/issues/7967
// solution from https://stackoverflow.com/a/33132624/3040605
marginTop: -0.1,
paddingTop: 0.1,
[`${antCls}-menu${antCls}-menu-inline-collapsed`]: {
width: 'auto',
},
},
'&-has-trigger': {
paddingBottom: triggerHeight,
},
'&-right': {
order: 1,
},
'&-trigger': {
position: 'fixed',
bottom: 0,
zIndex: 1,
height: triggerHeight,
color: triggerColor,
lineHeight: unit(triggerHeight),
textAlign: 'center',
background: triggerBg,
cursor: 'pointer',
transition: `all ${motionDurationMid}`,
},
'&-zero-width': {
'> *': {
overflow: 'hidden',
},
'&-trigger': {
position: 'absolute',
top: headerHeight,
insetInlineEnd: token.calc(zeroTriggerWidth).mul(-1).equal(),
zIndex: 1,
width: zeroTriggerWidth,
height: zeroTriggerHeight,
color: triggerColor,
fontSize: token.fontSizeXL,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: siderBg,
borderStartStartRadius: 0,
borderStartEndRadius: borderRadius,
borderEndEndRadius: borderRadius,
borderEndStartRadius: 0,
cursor: 'pointer',
transition: `background ${motionDurationSlow} ease`,
'&::after': {
position: 'absolute',
inset: 0,
background: 'transparent',
transition: `all ${motionDurationSlow}`,
content: '""',
},
'&:hover::after': {
background: `rgba(255, 255, 255, 0.2)`,
},
'&-right': {
insetInlineStart: token.calc(zeroTriggerWidth).mul(-1).equal(),
borderStartStartRadius: borderRadius,
borderStartEndRadius: 0,
borderEndEndRadius: 0,
borderEndStartRadius: borderRadius,
},
},
},
},
// Light
...genLayoutLightStyle(token),
// RTL
'&-rtl': {
direction: 'rtl',
@ -320,10 +218,12 @@ export const prepareComponentToken: GetDefaultToken<'Layout'> = (token) => {
};
// ============================== Export ==============================
export const DEPRECATED_TOKENS: [keyof ComponentToken, keyof ComponentToken][] = [
['colorBgBody', 'bodyBg'],
['colorBgHeader', 'headerBg'],
['colorBgTrigger', 'triggerBg'],
];
export default genStyleHooks('Layout', (token) => [genLayoutStyle(token)], prepareComponentToken, {
deprecatedTokens: [
['colorBgBody', 'bodyBg'],
['colorBgHeader', 'headerBg'],
['colorBgTrigger', 'triggerBg'],
],
deprecatedTokens: DEPRECATED_TOKENS,
});

View File

@ -1,28 +0,0 @@
import type { CSSObject } from '@ant-design/cssinjs';
import type { LayoutToken } from '.';
import type { GenerateStyle } from '../../theme/internal';
const genLayoutLightStyle: GenerateStyle<LayoutToken, CSSObject> = (token) => {
const { componentCls, bodyBg, lightSiderBg, lightTriggerBg, lightTriggerColor } = token;
return {
[`${componentCls}-sider-light`]: {
background: lightSiderBg,
[`${componentCls}-sider-trigger`]: {
color: lightTriggerColor,
background: lightTriggerBg,
},
[`${componentCls}-sider-zero-width-trigger`]: {
color: lightTriggerColor,
background: lightTriggerBg,
border: `1px solid ${bodyBg}`, // Safe to modify to any other color
borderInlineStart: 0,
},
},
};
};
export default genLayoutLightStyle;

View File

@ -0,0 +1,147 @@
import { unit } from '@ant-design/cssinjs';
import type { CSSObject } from '@ant-design/cssinjs';
import { DEPRECATED_TOKENS, prepareComponentToken } from '.';
import type { LayoutToken } from '.';
import type { GenerateStyle } from '../../theme/interface';
import { genStyleHooks } from '../../theme/internal';
const genSiderStyle: GenerateStyle<LayoutToken, CSSObject> = (token) => {
const {
componentCls,
siderBg,
motionDurationMid,
motionDurationSlow,
antCls,
triggerHeight,
triggerColor,
triggerBg,
headerHeight,
zeroTriggerWidth,
zeroTriggerHeight,
borderRadius,
lightSiderBg,
lightTriggerColor,
lightTriggerBg,
bodyBg,
} = token;
return {
[`${componentCls}`]: {
position: 'relative',
// fix firefox can't set width smaller than content on flex item
minWidth: 0,
background: siderBg,
transition: `all ${motionDurationMid}, background 0s`,
'&-has-trigger': {
paddingBottom: triggerHeight,
},
'&-right': {
order: 1,
},
[`${componentCls}-children`]: {
height: '100%',
// Hack for fixing margin collapse bug
// https://github.com/ant-design/ant-design/issues/7967
// solution from https://stackoverflow.com/a/33132624/3040605
marginTop: -0.1,
paddingTop: 0.1,
[`${antCls}-menu${antCls}-menu-inline-collapsed`]: {
width: 'auto',
},
},
[`${componentCls}-trigger`]: {
position: 'fixed',
bottom: 0,
zIndex: 1,
height: triggerHeight,
color: triggerColor,
lineHeight: unit(triggerHeight),
textAlign: 'center',
background: triggerBg,
cursor: 'pointer',
transition: `all ${motionDurationMid}`,
},
'&-zero-width': {
'> *': {
overflow: 'hidden',
},
[`${componentCls}-trigger`]: {
position: 'absolute',
top: headerHeight,
insetInlineEnd: token.calc(zeroTriggerWidth).mul(-1).equal(),
zIndex: 1,
width: zeroTriggerWidth,
height: zeroTriggerHeight,
color: triggerColor,
fontSize: token.fontSizeXL,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: siderBg,
borderStartStartRadius: 0,
borderStartEndRadius: borderRadius,
borderEndEndRadius: borderRadius,
borderEndStartRadius: 0,
cursor: 'pointer',
transition: `background ${motionDurationSlow} ease`,
'&::after': {
position: 'absolute',
inset: 0,
background: 'transparent',
transition: `all ${motionDurationSlow}`,
content: '""',
},
'&:hover::after': {
background: `rgba(255, 255, 255, 0.2)`,
},
'&-right': {
insetInlineStart: token.calc(zeroTriggerWidth).mul(-1).equal(),
borderStartStartRadius: borderRadius,
borderStartEndRadius: 0,
borderEndEndRadius: 0,
borderEndStartRadius: borderRadius,
},
},
},
// Light
'&-light': {
background: lightSiderBg,
[`${componentCls}-trigger`]: {
color: lightTriggerColor,
background: lightTriggerBg,
},
[`${componentCls}-zero-width-trigger`]: {
color: lightTriggerColor,
background: lightTriggerBg,
border: `1px solid ${bodyBg}`, // Safe to modify to any other color
borderInlineStart: 0,
},
},
},
};
};
export default genStyleHooks(
['Layout', 'Sider'],
(token) => [genSiderStyle(token)],
prepareComponentToken,
{
deprecatedTokens: DEPRECATED_TOKENS,
},
);

View File

@ -114,6 +114,20 @@ const localeValues: Locale = {
},
},
},
Image: {
preview: 'معاينة',
},
QRCode: {
expired: 'انتهت صلاحية رمز الاستجابة السريعة',
refresh: 'انقر للتحديث',
scanned: 'تم المسح',
},
ColorPicker: {
presetEmpty: 'لا يوجد',
transparent: 'شفاف',
singleColor: 'لون واحد',
gradientColor: 'تدرج لوني',
},
};
export default localeValues;

View File

@ -5,23 +5,45 @@ import Calendar from '../calendar/locale/el_GR';
import DatePicker from '../date-picker/locale/el_GR';
import TimePicker from '../time-picker/locale/el_GR';
const typeTemplate = 'Το ${label} δεν είναι έγκυρο ${type}';
const localeValues: Locale = {
locale: 'el',
Pagination,
DatePicker,
TimePicker,
Calendar,
global: {
placeholder: 'Παρακαλώ επιλέξτε',
},
Table: {
filterTitle: 'Μενού φίλτρων',
filterConfirm: 'ΟΚ',
filterReset: 'Επαναφορά',
filterEmptyText: 'Χωρίς φίλτρα',
filterCheckall: 'Επιλογή όλων',
filterSearchPlaceholder: 'Αναζήτηση στα φίλτρα',
emptyText: 'Δεν υπάρχουν δεδομένα',
selectAll: 'Επιλογή τρέχουσας σελίδας',
selectInvert: 'Αντιστροφή τρέχουσας σελίδας',
selectNone: 'Εκκαθάριση όλων των δεδομένων',
selectionAll: 'Επιλογή όλων των δεδομένων',
sortTitle: 'Ταξινόμηση',
expand: 'Ανάπτυξη σειράς',
collapse: 'Σύμπτυξη σειράς',
triggerDesc: 'Κλικ για φθίνουσα ταξινόμηση',
triggerAsc: 'Κλικ για αύξουσα ταξινόμηση',
cancelSort: 'Κλικ για ακύρωση ταξινόμησης',
},
Modal: {
okText: 'ΟΚ',
cancelText: 'Άκυρο',
justOkText: 'ΟΚ',
justOkText: 'Εντάξει',
},
Tour: {
Next: 'Επόμενο',
Previous: 'Προηγούμενο',
Finish: 'Τέλος',
},
Popconfirm: {
okText: 'ΟΚ',
@ -32,6 +54,12 @@ const localeValues: Locale = {
searchPlaceholder: 'Αναζήτηση',
itemUnit: 'αντικείμενο',
itemsUnit: 'αντικείμενα',
remove: 'Αφαίρεση',
selectCurrent: 'Επιλογή τρέχουσας σελίδας',
removeCurrent: 'Αφαίρεση τρέχουσας σελίδας',
selectAll: 'Επιλογή όλων των δεδομένων',
removeAll: 'Αφαίρεση όλων των δεδομένων',
selectInvert: 'Αντιστροφή τρέχουσας σελίδας',
},
Upload: {
uploading: 'Μεταφόρτωση...',
@ -43,6 +71,80 @@ const localeValues: Locale = {
Empty: {
description: 'Δεν υπάρχουν δεδομένα',
},
Icon: {
icon: 'εικονίδιο',
},
Text: {
edit: 'Επεξεργασία',
copy: 'Αντιγραφή',
copied: 'Αντιγράφηκε',
expand: 'Ανάπτυξη',
collapse: 'Σύμπτυξη',
},
Form: {
optional: '(προαιρετικό)',
defaultValidateMessages: {
default: 'Σφάλμα επικύρωσης πεδίου για ${label}',
required: 'Παρακαλώ εισάγετε ${label}',
enum: 'Το ${label} πρέπει να είναι ένα από [${enum}]',
whitespace: 'Το ${label} δεν μπορεί να είναι κενός χαρακτήρας',
date: {
format: 'Η μορφή ημερομηνίας του ${label} είναι άκυρη',
parse: 'Το ${label} δεν μπορεί να μετατραπεί σε ημερομηνία',
invalid: 'Το ${label} είναι μια άκυρη ημερομηνία',
},
types: {
string: typeTemplate,
method: typeTemplate,
array: typeTemplate,
object: typeTemplate,
number: typeTemplate,
date: typeTemplate,
boolean: typeTemplate,
integer: typeTemplate,
float: typeTemplate,
regexp: typeTemplate,
email: typeTemplate,
url: typeTemplate,
hex: typeTemplate,
},
string: {
len: 'Το ${label} πρέπει να είναι ${len} χαρακτήρες',
min: 'Το ${label} πρέπει να είναι τουλάχιστον ${min} χαρακτήρες',
max: 'Το ${label} πρέπει να είναι το πολύ ${max} χαρακτήρες',
range: 'Το ${label} πρέπει να είναι μεταξύ ${min}-${max} χαρακτήρων',
},
number: {
len: 'Το ${label} πρέπει να είναι ίσο με ${len}',
min: 'Το ${label} πρέπει να είναι τουλάχιστον ${min}',
max: 'Το ${label} πρέπει να είναι το πολύ ${max}',
range: 'Το ${label} πρέπει να είναι μεταξύ ${min}-${max}',
},
array: {
len: 'Πρέπει να είναι ${len} ${label}',
min: 'Τουλάχιστον ${min} ${label}',
max: 'Το πολύ ${max} ${label}',
range: 'Το ποσό του ${label} πρέπει να είναι μεταξύ ${min}-${max}',
},
pattern: {
mismatch: 'Το ${label} δεν ταιριάζει με το μοτίβο ${pattern}',
},
},
},
Image: {
preview: 'Προεπισκόπηση',
},
QRCode: {
expired: 'Ο κωδικός QR έληξε',
refresh: 'Ανανέωση',
scanned: 'Σαρώθηκε',
},
ColorPicker: {
presetEmpty: 'Κενό',
transparent: 'Διαφανές',
singleColor: 'Μονόχρωμο',
gradientColor: 'Διαβάθμιση χρώματος',
},
};
export default localeValues;

View File

@ -36,6 +36,11 @@ const localeValues: Locale = {
triggerAsc: 'Click para ordenar en orden ascendente',
cancelSort: 'Click para cancelar ordenamiento',
},
Tour: {
Next: 'Siguiente',
Previous: 'Anterior',
Finish: 'Finalizar',
},
Modal: {
okText: 'Aceptar',
cancelText: 'Cancelar',

View File

@ -13,12 +13,13 @@ import getAllowClear from '../_util/getAllowClear';
import genPurePanel from '../_util/PurePanel';
import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import toList from '../_util/toList';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import type { Variant } from '../config-provider';
import DefaultRenderEmpty from '../config-provider/defaultRenderEmpty';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import { FormItemInputContext } from '../form/context';
import type { Variant } from '../config-provider';
import useVariant from '../form/hooks/useVariants';
import Spin from '../spin';
import useStyle from './style';
@ -238,7 +239,7 @@ Mentions._InternalPanelDoNotUseOrYouWillBeFired = PurePanel;
Mentions.getMentions = (value = '', config: MentionsConfig = {}): MentionsEntity[] => {
const { prefix = '@', split = ' ' } = config;
const prefixList: string[] = Array.isArray(prefix) ? prefix : [prefix];
const prefixList: string[] = toList(prefix);
return value
.split(split)

View File

@ -424,7 +424,7 @@ const genMenuItemStyle = (token: MenuToken): CSSObject => {
transition: [
`border-color ${motionDurationSlow}`,
`background ${motionDurationSlow}`,
`padding ${motionDurationSlow} ${motionEaseInOut}`,
`padding calc(${motionDurationSlow} + 0.1s) ${motionEaseInOut}`,
].join(','),
[`${componentCls}-item-icon, ${iconCls}`]: {

View File

@ -58,11 +58,11 @@ const getVerticalStyle: GenerateStyle<MenuToken> = (token) => {
colorTextLightSolid,
dropdownWidth,
controlHeightLG,
motionDurationMid,
motionEaseOut,
paddingXL,
itemMarginInline,
fontSizeLG,
motionDurationFast,
motionDurationSlow,
paddingXS,
boxShadowSecondary,
@ -128,7 +128,7 @@ const getVerticalStyle: GenerateStyle<MenuToken> = (token) => {
transition: [
`border-color ${motionDurationSlow}`,
`background ${motionDurationSlow}`,
`padding ${motionDurationMid} ${motionEaseOut}`,
`padding ${motionDurationFast} ${motionEaseOut}`,
].join(','),
[`> ${componentCls}-title-content`]: {

View File

@ -398,7 +398,6 @@ const genPaginationJumpStyle: GenerateStyle<PaginationToken, CSSObject> = (token
display: 'block',
margin: 'auto',
color: token.colorTextDisabled,
fontFamily: 'Arial, Helvetica, sans-serif',
letterSpacing: token.paginationEllipsisLetterSpacing,
textAlign: 'center',
textIndent: token.paginationEllipsisTextIndent,
@ -446,7 +445,6 @@ const genPaginationJumpStyle: GenerateStyle<PaginationToken, CSSObject> = (token
},
[`${componentCls}-prev, ${componentCls}-next`]: {
fontFamily: 'Arial, Helvetica, sans-serif',
outline: 0,
button: {

View File

@ -1536,7 +1536,7 @@ exports[`renders components/select/demo/custom-label-render.tsx extend context c
class="ant-select-selection-item"
>
<span>
当前 value 没有对应的选项
No option match
</span>
</span>
</div>

View File

@ -731,7 +731,7 @@ exports[`renders components/select/demo/custom-label-render.tsx correctly 1`] =
class="ant-select-selection-item"
>
<span>
当前 value 没有对应的选项
No option match
</span>
</span>
</div>

View File

@ -17,7 +17,7 @@ const labelRender: LabelRender = (props) => {
if (label) {
return value;
}
return <span> value </span>;
return <span>No option match</span>;
};
const App: React.FC = () => (

View File

@ -1698,6 +1698,127 @@ describe('Table.rowSelection', () => {
);
});
it('treeData cache with preserveSelectedRowKeys and checkStrictly false', () => {
const onChange = jest.fn();
const treeDataColumns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
},
];
const { container, rerender } = render(
<Table
expandable={{
defaultExpandAllRows: true,
}}
columns={treeDataColumns}
dataSource={[
{
key: 1,
name: 'a',
children: [
{
key: 11,
name: 'b',
},
{
key: 12,
name: 'c',
children: [
{
key: 121,
name: 'd',
},
],
},
],
},
{
key: 2,
name: 'e',
},
]}
rowSelection={{ onChange, preserveSelectedRowKeys: true, checkStrictly: false }}
/>,
);
fireEvent.click(container.querySelector('th input')!);
expect(onChange).toHaveBeenCalledWith(
[1, 11, 12, 121, 2],
[
{
key: 1,
name: 'a',
children: [
{
key: 11,
name: 'b',
},
{
key: 12,
name: 'c',
children: [
{
key: 121,
name: 'd',
},
],
},
],
},
{ key: 11, name: 'b' },
{ key: 12, name: 'c', children: [{ key: 121, name: 'd' }] },
{ key: 121, name: 'd' },
{ key: 2, name: 'e' },
],
{ type: 'all' },
);
rerender(
<Table
expandable={{
defaultExpandAllRows: true,
}}
columns={treeDataColumns}
dataSource={[
{
key: 1,
name: 'a',
children: [
{
key: 11,
name: 'b',
},
{
key: 12,
name: 'c',
children: [
{
key: 121,
name: 'd',
},
],
},
],
},
]}
rowSelection={{ onChange, preserveSelectedRowKeys: true, checkStrictly: false }}
/>,
);
fireEvent.click(container.querySelectorAll('tbody input[type="checkbox"]')[1]);
expect(onChange).toHaveBeenCalledWith(
[12, 121, 2],
[
{ key: 12, name: 'c', children: [{ key: 121, name: 'd' }] },
{ key: 121, name: 'd' },
{ key: 2, name: 'e' },
],
{ type: 'single' },
);
expect(getIndeterminateSelection(container)).toEqual([1]);
});
it('works with receive selectedRowKeys from [] to undefined', () => {
const onChange = jest.fn();
const dataSource = [{ name: 'Jack' }];

View File

@ -28974,3 +28974,516 @@ Array [
`;
exports[`renders components/table/demo/tree-table-ellipsis.tsx extend context correctly 2`] = `[]`;
exports[`renders components/table/demo/tree-table-preserveSelectedRowKeys.tsx extend context correctly 1`] = `
Array [
<div
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"
style="margin-bottom: 16px;"
>
<div
class="ant-space-item"
>
CheckStrictly:
</div>
<div
class="ant-space-item"
>
<button
aria-checked="false"
class="ant-switch"
role="switch"
type="button"
>
<div
class="ant-switch-handle"
/>
<span
class="ant-switch-inner"
>
<span
class="ant-switch-inner-checked"
/>
<span
class="ant-switch-inner-unchecked"
/>
</span>
</button>
</div>
<div
class="ant-space-item"
>
preserveSelectedRowKeys:
</div>
<div
class="ant-space-item"
>
</div>
<div
class="ant-space-item"
>
<button
aria-checked="true"
class="ant-switch ant-switch-checked"
role="switch"
type="button"
>
<div
class="ant-switch-handle"
/>
<span
class="ant-switch-inner"
>
<span
class="ant-switch-inner-checked"
/>
<span
class="ant-switch-inner-unchecked"
/>
</span>
</button>
</div>
</div>,
<div
class="ant-table-wrapper"
>
<div
class="ant-spin-nested-loading"
>
<div
class="ant-spin-container"
>
<div
class="ant-table"
>
<div
class="ant-table-container"
>
<div
class="ant-table-content"
>
<table
style="table-layout: auto;"
>
<colgroup>
<col
class="ant-table-selection-col"
/>
<col />
<col
style="width: 12%;"
/>
<col
style="width: 30%;"
/>
</colgroup>
<thead
class="ant-table-thead"
>
<tr>
<th
class="ant-table-cell ant-table-selection-column"
scope="col"
>
<div
class="ant-table-selection"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox ant-wave-target"
>
<input
aria-label="Select all"
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</div>
</th>
<th
class="ant-table-cell"
scope="col"
>
Name
</th>
<th
class="ant-table-cell"
scope="col"
>
Age
</th>
<th
class="ant-table-cell"
scope="col"
>
Address
</th>
</tr>
</thead>
<tbody
class="ant-table-tbody"
>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="key0"
>
<td
class="ant-table-cell ant-table-selection-column"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox ant-wave-target"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-with-append"
>
<span
class="ant-table-row-indent indent-level-0"
style="padding-left: 0px;"
/>
<button
aria-expanded="false"
aria-label="Expand row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-collapsed"
type="button"
/>
Edward 0
</td>
<td
class="ant-table-cell"
>
32
</td>
<td
class="ant-table-cell"
>
London Park no. 0
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="key1"
>
<td
class="ant-table-cell ant-table-selection-column"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox ant-wave-target"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-with-append"
>
<span
class="ant-table-row-indent indent-level-0"
style="padding-left: 0px;"
/>
<button
aria-expanded="false"
aria-label="Expand row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-collapsed"
type="button"
/>
Edward 1
</td>
<td
class="ant-table-cell"
>
32
</td>
<td
class="ant-table-cell"
>
London Park no. 1
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="key2"
>
<td
class="ant-table-cell ant-table-selection-column"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox ant-wave-target"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-with-append"
>
<span
class="ant-table-row-indent indent-level-0"
style="padding-left: 0px;"
/>
<button
aria-expanded="false"
aria-label="Expand row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-collapsed"
type="button"
/>
Edward 2
</td>
<td
class="ant-table-cell"
>
32
</td>
<td
class="ant-table-cell"
>
London Park no. 2
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="key3"
>
<td
class="ant-table-cell ant-table-selection-column"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox ant-wave-target"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-with-append"
>
<span
class="ant-table-row-indent indent-level-0"
style="padding-left: 0px;"
/>
<button
aria-expanded="false"
aria-label="Expand row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-collapsed"
type="button"
/>
Edward 3
</td>
<td
class="ant-table-cell"
>
32
</td>
<td
class="ant-table-cell"
>
London Park no. 3
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="key4"
>
<td
class="ant-table-cell ant-table-selection-column"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox ant-wave-target"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-with-append"
>
<span
class="ant-table-row-indent indent-level-0"
style="padding-left: 0px;"
/>
<button
aria-expanded="false"
aria-label="Expand row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-collapsed"
type="button"
/>
Edward 4
</td>
<td
class="ant-table-cell"
>
32
</td>
<td
class="ant-table-cell"
>
London Park no. 4
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<ul
class="ant-pagination ant-table-pagination ant-table-pagination-right"
>
<li
aria-disabled="true"
class="ant-pagination-prev ant-pagination-disabled"
title="Previous Page"
>
<button
class="ant-pagination-item-link"
disabled=""
tabindex="-1"
type="button"
>
<span
aria-label="left"
class="anticon anticon-left"
role="img"
>
<svg
aria-hidden="true"
data-icon="left"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"
/>
</svg>
</span>
</button>
</li>
<li
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
tabindex="0"
title="1"
>
<a
rel="nofollow"
>
1
</a>
</li>
<li
class="ant-pagination-item ant-pagination-item-2"
tabindex="0"
title="2"
>
<a
rel="nofollow"
>
2
</a>
</li>
<li
class="ant-pagination-item ant-pagination-item-3"
tabindex="0"
title="3"
>
<a
rel="nofollow"
>
3
</a>
</li>
<li
aria-disabled="false"
class="ant-pagination-next"
tabindex="0"
title="Next Page"
>
<button
class="ant-pagination-item-link"
tabindex="-1"
type="button"
>
<span
aria-label="right"
class="anticon anticon-right"
role="img"
>
<svg
aria-hidden="true"
data-icon="right"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
/>
</svg>
</span>
</button>
</li>
</ul>
</div>
</div>
</div>,
]
`;
exports[`renders components/table/demo/tree-table-preserveSelectedRowKeys.tsx extend context correctly 2`] = `[]`;

View File

@ -28118,3 +28118,514 @@ Array [
</div>,
]
`;
exports[`renders components/table/demo/tree-table-preserveSelectedRowKeys.tsx correctly 1`] = `
Array [
<div
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"
style="margin-bottom:16px"
>
<div
class="ant-space-item"
>
CheckStrictly:
</div>
<div
class="ant-space-item"
>
<button
aria-checked="false"
class="ant-switch"
role="switch"
type="button"
>
<div
class="ant-switch-handle"
/>
<span
class="ant-switch-inner"
>
<span
class="ant-switch-inner-checked"
/>
<span
class="ant-switch-inner-unchecked"
/>
</span>
</button>
</div>
<div
class="ant-space-item"
>
preserveSelectedRowKeys:
</div>
<div
class="ant-space-item"
>
</div>
<div
class="ant-space-item"
>
<button
aria-checked="true"
class="ant-switch ant-switch-checked"
role="switch"
type="button"
>
<div
class="ant-switch-handle"
/>
<span
class="ant-switch-inner"
>
<span
class="ant-switch-inner-checked"
/>
<span
class="ant-switch-inner-unchecked"
/>
</span>
</button>
</div>
</div>,
<div
class="ant-table-wrapper"
>
<div
class="ant-spin-nested-loading"
>
<div
class="ant-spin-container"
>
<div
class="ant-table"
>
<div
class="ant-table-container"
>
<div
class="ant-table-content"
>
<table
style="table-layout:auto"
>
<colgroup>
<col
class="ant-table-selection-col"
/>
<col />
<col
style="width:12%"
/>
<col
style="width:30%"
/>
</colgroup>
<thead
class="ant-table-thead"
>
<tr>
<th
class="ant-table-cell ant-table-selection-column"
scope="col"
>
<div
class="ant-table-selection"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox ant-wave-target"
>
<input
aria-label="Select all"
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</div>
</th>
<th
class="ant-table-cell"
scope="col"
>
Name
</th>
<th
class="ant-table-cell"
scope="col"
>
Age
</th>
<th
class="ant-table-cell"
scope="col"
>
Address
</th>
</tr>
</thead>
<tbody
class="ant-table-tbody"
>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="key0"
>
<td
class="ant-table-cell ant-table-selection-column"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox ant-wave-target"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-with-append"
>
<span
class="ant-table-row-indent indent-level-0"
style="padding-left:0px"
/>
<button
aria-expanded="false"
aria-label="Expand row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-collapsed"
type="button"
/>
Edward 0
</td>
<td
class="ant-table-cell"
>
32
</td>
<td
class="ant-table-cell"
>
London Park no. 0
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="key1"
>
<td
class="ant-table-cell ant-table-selection-column"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox ant-wave-target"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-with-append"
>
<span
class="ant-table-row-indent indent-level-0"
style="padding-left:0px"
/>
<button
aria-expanded="false"
aria-label="Expand row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-collapsed"
type="button"
/>
Edward 1
</td>
<td
class="ant-table-cell"
>
32
</td>
<td
class="ant-table-cell"
>
London Park no. 1
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="key2"
>
<td
class="ant-table-cell ant-table-selection-column"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox ant-wave-target"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-with-append"
>
<span
class="ant-table-row-indent indent-level-0"
style="padding-left:0px"
/>
<button
aria-expanded="false"
aria-label="Expand row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-collapsed"
type="button"
/>
Edward 2
</td>
<td
class="ant-table-cell"
>
32
</td>
<td
class="ant-table-cell"
>
London Park no. 2
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="key3"
>
<td
class="ant-table-cell ant-table-selection-column"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox ant-wave-target"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-with-append"
>
<span
class="ant-table-row-indent indent-level-0"
style="padding-left:0px"
/>
<button
aria-expanded="false"
aria-label="Expand row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-collapsed"
type="button"
/>
Edward 3
</td>
<td
class="ant-table-cell"
>
32
</td>
<td
class="ant-table-cell"
>
London Park no. 3
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="key4"
>
<td
class="ant-table-cell ant-table-selection-column"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox ant-wave-target"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell ant-table-cell-with-append"
>
<span
class="ant-table-row-indent indent-level-0"
style="padding-left:0px"
/>
<button
aria-expanded="false"
aria-label="Expand row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-collapsed"
type="button"
/>
Edward 4
</td>
<td
class="ant-table-cell"
>
32
</td>
<td
class="ant-table-cell"
>
London Park no. 4
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<ul
class="ant-pagination ant-table-pagination ant-table-pagination-right"
>
<li
aria-disabled="true"
class="ant-pagination-prev ant-pagination-disabled"
title="Previous Page"
>
<button
class="ant-pagination-item-link"
disabled=""
tabindex="-1"
type="button"
>
<span
aria-label="left"
class="anticon anticon-left"
role="img"
>
<svg
aria-hidden="true"
data-icon="left"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"
/>
</svg>
</span>
</button>
</li>
<li
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
tabindex="0"
title="1"
>
<a
rel="nofollow"
>
1
</a>
</li>
<li
class="ant-pagination-item ant-pagination-item-2"
tabindex="0"
title="2"
>
<a
rel="nofollow"
>
2
</a>
</li>
<li
class="ant-pagination-item ant-pagination-item-3"
tabindex="0"
title="3"
>
<a
rel="nofollow"
>
3
</a>
</li>
<li
aria-disabled="false"
class="ant-pagination-next"
tabindex="0"
title="Next Page"
>
<button
class="ant-pagination-item-link"
tabindex="-1"
type="button"
>
<span
aria-label="right"
class="anticon anticon-right"
role="img"
>
<svg
aria-hidden="true"
data-icon="right"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
/>
</svg>
</span>
</button>
</li>
</ul>
</div>
</div>
</div>,
]
`;

View File

@ -0,0 +1,11 @@
## zh-CN
treeData Table 使用 CheckStrictly: false & preserveSelectedRowKeys: true 的示例
https://github.com/ant-design/ant-design/issues/50621
## en-US
treeData Table using CheckStrictly: false & preserveSelectedRowKeys: true example
https://github.com/ant-design/ant-design/issues/50621

View File

@ -0,0 +1,93 @@
import React, { useState } from 'react';
import { Space, Switch, Table } from 'antd';
import type { TableColumnsType, TableProps } from 'antd';
type TableRowSelection<T extends object = object> = TableProps<T>['rowSelection'];
interface DataType {
key: React.ReactNode;
name: string;
age: number;
address: string;
children?: DataType[];
}
const columns: TableColumnsType<DataType> = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
width: '12%',
},
{
title: 'Address',
dataIndex: 'address',
width: '30%',
key: 'address',
},
];
const data: DataType[] = [];
for (let i = 0; i < 15; i++) {
data.push({
key: `key${i}`,
name: `Edward ${i}`,
age: 32,
address: `London Park no. ${i}`,
children: [
{
key: `subKey${i}1`,
name: 'Brown',
age: 16,
address: 'New York No. 3 Lake Park',
},
{
key: `subKey${i}2`,
name: 'Jimmy',
age: 16,
address: 'New York No. 3 Lake Park',
},
],
});
}
// rowSelection objects indicates the need for row selection
const rowSelection: TableRowSelection<DataType> = {
onChange: (selectedRowKeys, selectedRows) => {
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
},
onSelect: (record, selected, selectedRows) => {
console.log(record, selected, selectedRows);
},
onSelectAll: (selected, selectedRows, changeRows) => {
console.log(selected, selectedRows, changeRows);
},
};
const App: React.FC = () => {
const [checkStrictly, setCheckStrictly] = useState(false);
const [preserveSelectedRowKeys, setPreserveSelectedRowKeys] = useState(true);
return (
<>
<Space align="center" style={{ marginBottom: 16 }}>
CheckStrictly: <Switch checked={checkStrictly} onChange={setCheckStrictly} />
preserveSelectedRowKeys:{' '}
<Switch checked={preserveSelectedRowKeys} onChange={setPreserveSelectedRowKeys} />
</Space>
<Table
columns={columns}
rowSelection={{ ...rowSelection, checkStrictly, preserveSelectedRowKeys }}
dataSource={data}
pagination={{ defaultPageSize: 5 }}
/>
</>
);
};
export default App;

View File

@ -152,13 +152,20 @@ const useSelection = <RecordType extends AnyObject = AnyObject>(
updatePreserveRecordsCache(mergedSelectedKeys);
}, [mergedSelectedKeys]);
// Get flatten data
const flattedData = useMemo(
() => flattenData(childrenColumnName, pageData),
[childrenColumnName, pageData],
);
const { keyEntities } = useMemo(() => {
if (checkStrictly) {
return { keyEntities: null };
}
let convertData = data;
if (preserveSelectedRowKeys) {
const keysSet = new Set(data.map((record, index) => getRowKey(record, index)));
// use flattedData keys
const keysSet = new Set(flattedData.map((record, index) => getRowKey(record, index)));
// remove preserveRecords that duplicate data
const preserveRecords = Array.from(preserveRecordsRef.current).reduce(
(total: RecordType[], [key, value]) => (keysSet.has(key) ? total : total.concat(value)),
@ -170,13 +177,7 @@ const useSelection = <RecordType extends AnyObject = AnyObject>(
externalGetKey: getRowKey as any,
childrenPropName: childrenColumnName,
});
}, [data, getRowKey, checkStrictly, childrenColumnName, preserveSelectedRowKeys]);
// Get flatten data
const flattedData = useMemo(
() => flattenData(childrenColumnName, pageData),
[childrenColumnName, pageData],
);
}, [data, getRowKey, checkStrictly, childrenColumnName, preserveSelectedRowKeys, flattedData]);
// Get all checkbox props
const checkboxPropsMap = useMemo(() => {

View File

@ -87,7 +87,7 @@ const columns = [
<code src="./demo/fixed-columns.tsx">Fixed Columns</code>
<code src="./demo/fixed-gapped-columns.tsx" version="5.14.0">Stack Fixed Columns</code>
<code src="./demo/fixed-columns-header.tsx">Fixed Columns and Header</code>
<code src="./demo/hidden-columns.tsx">Hidden Columns</code>
<code src="./demo/hidden-columns.tsx" version="5.13.0">Hidden Columns</code>
<code src="./demo/grouping-columns.tsx">Grouping table head</code>
<code src="./demo/edit-cell.tsx">Editable Cells</code>
<code src="./demo/edit-row.tsx">Editable Rows</code>
@ -100,7 +100,7 @@ const columns = [
<code src="./demo/ellipsis-custom-tooltip.tsx">ellipsis column custom tooltip</code>
<code src="./demo/custom-empty.tsx">Custom empty</code>
<code src="./demo/summary.tsx">Summary</code>
<code src="./demo/virtual-list.tsx" version=">= 5.9.0">Virtual list</code>
<code src="./demo/virtual-list.tsx" version="5.9.0">Virtual list</code>
<code src="./demo/responsive.tsx">Responsive</code>
<code src="./demo/nest-table-border-debug.tsx" debug>Nested Bordered Table Debug</code>
<code src="./demo/pagination.tsx">Pagination Settings</code>

View File

@ -84,11 +84,12 @@ const columns = [
<code src="./demo/colspan-rowspan.tsx">表格行/列合并</code>
<code src="./demo/tree-data.tsx">树形数据展示</code>
<code src="./demo/tree-table-ellipsis.tsx" debug>树形数据省略情况测试</code>
<code src="./demo/tree-table-preserveSelectedRowKeys.tsx" debug>树形数据保留key测试</code>
<code src="./demo/fixed-header.tsx">固定表头</code>
<code src="./demo/fixed-columns.tsx">固定列</code>
<code src="./demo/fixed-gapped-columns.tsx" version="5.14.0">堆叠固定列</code>
<code src="./demo/fixed-columns-header.tsx">固定头和列</code>
<code src="./demo/hidden-columns.tsx">隐藏列</code>
<code src="./demo/hidden-columns.tsx" version="5.13.0">隐藏列</code>
<code src="./demo/grouping-columns.tsx">表头分组</code>
<code src="./demo/edit-cell.tsx">可编辑单元格</code>
<code src="./demo/edit-row.tsx">可编辑行</code>
@ -101,7 +102,7 @@ const columns = [
<code src="./demo/ellipsis-custom-tooltip.tsx">自定义单元格省略提示</code>
<code src="./demo/custom-empty.tsx">自定义空状态</code>
<code src="./demo/summary.tsx">总结栏</code>
<code src="./demo/virtual-list.tsx" version=">= 5.9.0">虚拟列表</code>
<code src="./demo/virtual-list.tsx" version="5.9.0">虚拟列表</code>
<code src="./demo/responsive.tsx">响应式</code>
<code src="./demo/nest-table-border-debug.tsx" debug>嵌套带边框的表格 Debug</code>
<code src="./demo/pagination.tsx">分页设置</code>

View File

@ -352,4 +352,24 @@ describe('Typography copy', () => {
fireEvent.click(container.querySelectorAll('.ant-typography-copy')[0]);
expect(container.querySelector('.ant-tooltip-inner')?.textContent).toBe('Copied');
});
it('copy array children', () => {
const spy = jest.spyOn(copyObj, 'default');
const bamboo = 'bamboo';
const little = 'little';
const { container } = render(
<Base component="p" copyable>
{bamboo}
{little}
</Base>,
);
fireEvent.click(container.querySelector('.ant-typography-copy')!);
// Check copy content
expect(spy.mock.calls[0][0]).toBe(`${bamboo}${little}`);
spy.mockRestore();
});
});

View File

@ -2,6 +2,7 @@ import * as React from 'react';
import copy from 'copy-to-clipboard';
import { useEvent } from 'rc-util';
import toList from '../../_util/toList';
import type { CopyConfig } from '../Base';
const useCopyClick = ({
@ -38,7 +39,7 @@ const useCopyClick = ({
try {
const text =
typeof copyConfig.text === 'function' ? await copyConfig.text() : copyConfig.text;
copy(text || String(children) || '', copyOptions);
copy(text || toList(children, true).join('') || '', copyOptions);
setCopyLoading(false);
setCopied(true);

View File

@ -10,6 +10,7 @@ import useClips, { FontGap } from './useClips';
import useRafDebounce from './useRafDebounce';
import useWatermark from './useWatermark';
import { getPixelRatio, reRendering } from './utils';
import toList from '../_util/toList';
export interface WatermarkProps {
zIndex?: number;
@ -145,7 +146,7 @@ const Watermark: React.FC<WatermarkProps> = (props) => {
let defaultHeight = 64;
if (!image && ctx.measureText) {
ctx.font = `${Number(fontSize)}px ${fontFamily}`;
const contents = Array.isArray(content) ? content : [content];
const contents = toList(content);
const sizes = contents.map((item) => {
const metrics = ctx.measureText(item!);

View File

@ -1,4 +1,5 @@
import type { WatermarkProps } from '.';
import toList from '../_util/toList';
export const FontGap = 3;
@ -55,7 +56,7 @@ export default function useClips() {
ctx.fillStyle = color;
ctx.textAlign = textAlign;
ctx.textBaseline = 'top';
const contents = Array.isArray(content) ? content : [content];
const contents = toList(content);
contents?.forEach((item, index) => {
ctx.fillText(item ?? '', contentWidth / 2, index * (mergedFontSize + FontGap * ratio));
});

View File

@ -1,6 +1,6 @@
{
"name": "antd",
"version": "5.20.5",
"version": "5.20.6",
"description": "An enterprise-class UI design language and React components implementation",
"keywords": [
"ant",
@ -31,7 +31,13 @@
"unpkg": "dist/antd.min.js",
"module": "es/index.js",
"typings": "es/index.d.ts",
"files": ["dist", "es", "lib", "locale", "BUG_VERSIONS.json"],
"files": [
"dist",
"es",
"lib",
"locale",
"BUG_VERSIONS.json"
],
"scripts": {
"api-collection": "antd-tools run api-collection",
"authors": "tsx scripts/generate-authors.ts",
@ -96,7 +102,12 @@
"tsc:old": "tsc --noEmit -p tsconfig-old-react.json",
"version": "tsx scripts/generate-version.ts"
},
"browserslist": ["> 0.5%", "last 2 versions", "Firefox ESR", "not dead"],
"browserslist": [
"> 0.5%",
"last 2 versions",
"Firefox ESR",
"not dead"
],
"dependencies": {
"@ant-design/colors": "^7.1.0",
"@ant-design/cssinjs": "^1.21.0",
@ -109,7 +120,7 @@
"@rc-component/mutate-observer": "^1.1.0",
"@rc-component/qrcode": "~1.0.0",
"@rc-component/tour": "~1.15.1",
"@rc-component/trigger": "^2.2.2",
"@rc-component/trigger": "^2.2.3",
"classnames": "^2.5.1",
"copy-to-clipboard": "^3.3.3",
"dayjs": "^1.11.11",