chore: merge master into feature

This commit is contained in:
栗嘉男 2024-08-21 00:00:02 +08:00
commit dee90b2509
10 changed files with 408 additions and 149 deletions

View File

@ -16,6 +16,20 @@ tag: vVERSION
---
## 5.20.2
`2024-08-19`
- 💄 Fix the suffix style problem of InputNumber without control. [#50450](https://github.com/ant-design/ant-design/pull/50450) [@coding-ice](https://github.com/coding-ice)
- 🆕 Form `rule.message` supports skipping variable substitution through `\\${}`. [#50412](https://github.com/ant-design/ant-design/pull/50412) [@zombieJ](https://github.com/zombieJ)
- 🐞 Fixed the issue where the rounded corners of the trigger element are missing when the FloatButton component has shape="square" and in menu mode when the menu pops up. [#50408](https://github.com/ant-design/ant-design/pull/50408) [@li-jia-nan](https://github.com/li-jia-nan)
- 🐞 Fixed the problem that Upload.Dragger does not work when dragging and dropping upload folders. [#50394](https://github.com/ant-design/ant-design/pull/50394) [@huiliangShen](https://github.com/huiliangShen)
- 🐞 Fixed the issue where the arrow icon disappears after hovering when Select specifies `getPopcontainer={node=node.parentNode}`. [#50382](https://github.com/ant-design/ant-design/pull/50382) [@afc163](https://github.com/afc163)
- 🐞 Fixed the arrow misalignment error when Popover sets the `arrow.pointAtCenter` property. [#50260](https://github.com/ant-design/ant-design/pull/50260) [@Wxh16144](https://github.com/Wxh16144)
- 📖 Transfer adds Russian and Ukrainian localization copy. [#50429](https://github.com/ant-design/ant-design/pull/50429) [@alexlag](https://github.com/alexlag)
- TypeScript
- 🤖 Roll back the Table partial generic constraint object to any to reduce break changes caused by [#50351](https://github.com/ant-design/ant-design/pull/50351). [#50372](https://github.com/ant-design/ant-design/pull/50372) [@crazyair](https://github.com/crazyair)
## 5.20.1
`2024-08-11`

View File

@ -15,6 +15,20 @@ tag: vVERSION
---
## 5.20.2
`2024-08-19`
- 💄 修复 InputNumber 没有控件的后缀样式问题。[#50450](https://github.com/ant-design/ant-design/pull/50450) [@coding-ice](https://github.com/coding-ice)
- 🆕 Form `rule.message` 支持通过 `\\${}` 跳过变量替换。[#50412](https://github.com/ant-design/ant-design/pull/50412) [@zombieJ](https://github.com/zombieJ)
- 🐞 修复了 FloatButton 组件当 shape=“square” 时,并且在菜单模式下,菜单弹出时 trigger 元素圆角缺失的问题。[#50408](https://github.com/ant-design/ant-design/pull/50408) [@li-jia-nan](https://github.com/li-jia-nan)
- 🐞 修复 Upload.Dragger 拖拽上传文件夹时不工作问题。[#50394](https://github.com/ant-design/ant-design/pull/50394) [@huiliangShen](https://github.com/huiliangShen)
- 🐞 修复 Select 指定 `getPopcontainer={node=node.parentNode}` 时箭头图标 hover 后会消失的问题。[#50382](https://github.com/ant-design/ant-design/pull/50382) [@afc163](https://github.com/afc163)
- 🐞 修复 Popover 设置 `arrow.pointAtCenter` 属性时箭头未对齐错误。[#50260](https://github.com/ant-design/ant-design/pull/50260) [@Wxh16144](https://github.com/Wxh16144)
- 📖 Transfer 补充俄罗斯语和乌克兰语本地化文案。[#50429](https://github.com/ant-design/ant-design/pull/50429) [@alexlag](https://github.com/alexlag)
- TypeScript
- 🤖 将 Table 部分泛型约束 object 回滚为 any以减少 [#50351](https://github.com/ant-design/ant-design/pull/50351) 造成的 break change。[#50372](https://github.com/ant-design/ant-design/pull/50372) [@crazyair](https://github.com/crazyair)
## 5.20.1
`2024-08-11`

View File

@ -34,12 +34,15 @@ function doMouseMove(
});
fireEvent(ele, mouseDown);
// Drag
const mouseMove: any = new Event('mousemove');
mouseMove.pageX = end;
mouseMove.pageY = end;
fireEvent(document, mouseMove);
// Drag
if (start !== end) {
const mouseMove: any = new Event('mousemove');
mouseMove.pageX = end;
mouseMove.pageY = end;
fireEvent(document, mouseMove);
}
const mouseUp = createEvent.mouseUp(document);
fireEvent(document, mouseUp);
@ -848,4 +851,32 @@ describe('ColorPicker', () => {
);
});
});
it('onChangeComplete with default empty color should not be alpha', async () => {
const spyRect = spyElementPrototypes(HTMLElement, {
getBoundingClientRect: () => ({
x: 0,
y: 100,
width: 100,
height: 100,
}),
});
const handleChangeComplete = jest.fn();
const { container } = render(<ColorPicker open onChangeComplete={handleChangeComplete} />);
// Move
doMouseMove(container, 50, 50);
expect(handleChangeComplete).toHaveBeenCalledTimes(1);
const color = handleChangeComplete.mock.calls[0][0];
expect(color.toRgb()).toEqual({
r: 255,
g: 128,
b: 128,
a: 1,
});
spyRect.mockRestore();
});
});

View File

@ -17,6 +17,11 @@ const components = {
slider: ColorSlider,
};
type Info = {
type?: 'hue' | 'alpha';
value?: number;
};
const PanelPicker: FC = () => {
const panelPickerContext = useContext(PanelPickerContext);
@ -81,32 +86,10 @@ const PanelPicker: FC = () => {
}, [value, activeIndex, isSingle, lockedColor, gradientDragging]);
// ============================ Change ============================
const fillColor = (nextColor: AggregationColor) => {
if (mode === 'single') {
return nextColor;
}
const nextColors = [...colors];
nextColors[activeIndex] = {
...nextColors[activeIndex],
color: nextColor,
};
return new AggregationColor(nextColors);
};
const onInternalChange = (
colorValue: AggregationColor | Color,
fromPicker?: boolean,
info?: {
type?: 'hue' | 'alpha';
value?: number;
},
) => {
const nextColor = generateColor(colorValue);
let submitColor = nextColor;
const fillColor = (nextColor: AggregationColor | Color, info?: Info) => {
let submitColor = generateColor(nextColor);
// Fill alpha color to 100% if origin is cleared color
if (value.cleared) {
const rgb = submitColor.toRgb();
@ -125,11 +108,29 @@ const PanelPicker: FC = () => {
}
}
onChange(fillColor(submitColor), fromPicker);
if (mode === 'single') {
return submitColor;
}
const nextColors = [...colors];
nextColors[activeIndex] = {
...nextColors[activeIndex],
color: submitColor,
};
return new AggregationColor(nextColors);
};
const onInternalChangeComplete = (nextColor: AggregationColor) => {
onChangeComplete(fillColor(nextColor));
const onInternalChange = (
colorValue: AggregationColor | Color,
fromPicker?: boolean,
info?: Info,
) => {
onChange(fillColor(colorValue, info), fromPicker);
};
const onInternalChangeComplete = (nextColor: Color, info?: Info) => {
onChangeComplete(fillColor(nextColor, info));
};
// ============================ Render ============================
@ -170,8 +171,8 @@ const PanelPicker: FC = () => {
onChange={(colorValue, info) => {
onInternalChange(colorValue, true, info);
}}
onChangeComplete={(colorValue) => {
onInternalChangeComplete(generateColor(colorValue));
onChangeComplete={(colorValue, info) => {
onInternalChangeComplete(colorValue, info);
}}
components={components}
/>

View File

@ -1,8 +1,9 @@
import React, { memo, useCallback, useContext, useEffect } from 'react';
import React from 'react';
import CloseOutlined from '@ant-design/icons/CloseOutlined';
import FileTextOutlined from '@ant-design/icons/FileTextOutlined';
import classNames from 'classnames';
import CSSMotion from 'rc-motion';
import { useEvent } from 'rc-util';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import { useZIndex } from '../_util/hooks/useZIndex';
@ -12,10 +13,10 @@ import { ConfigContext } from '../config-provider';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import { FloatButtonGroupProvider } from './context';
import FloatButton, { floatButtonPrefixCls } from './FloatButton';
import type { FloatButtonGroupProps, FloatButtonRef } from './interface';
import type { FloatButtonGroupProps } from './interface';
import useStyle from './style';
const FloatButtonGroup: React.FC<FloatButtonGroupProps> = (props) => {
const FloatButtonGroup: React.FC<Readonly<FloatButtonGroupProps>> = (props) => {
const {
prefixCls: customizePrefixCls,
className,
@ -30,11 +31,12 @@ const FloatButtonGroup: React.FC<FloatButtonGroupProps> = (props) => {
children,
onOpenChange,
open: customOpen,
onClick: onTriggerButtonClick,
...floatButtonProps
} = props;
const { direction, getPrefixCls, floatButtonGroup } =
useContext<ConfigConsumerProps>(ConfigContext);
React.useContext<ConfigConsumerProps>(ConfigContext);
const mergedCloseIcon = closeIcon ?? floatButtonGroup?.closeIcon ?? <CloseOutlined />;
@ -65,53 +67,53 @@ const FloatButtonGroup: React.FC<FloatButtonGroupProps> = (props) => {
const floatButtonGroupRef = React.useRef<HTMLDivElement>(null);
const floatButtonRef = React.useRef<FloatButtonRef['nativeElement']>(null);
// ========================== Open ==========================
const hoverTrigger = trigger === 'hover';
const clickTrigger = trigger === 'click';
const hoverAction = React.useMemo<React.DOMAttributes<HTMLDivElement>>(() => {
const hoverTypeAction = {
onMouseEnter() {
setOpen(true);
onOpenChange?.(true);
},
onMouseLeave() {
setOpen(false);
onOpenChange?.(false);
},
};
return trigger === 'hover' ? hoverTypeAction : {};
}, [trigger]);
const triggerOpen = useEvent((nextOpen: boolean) => {
if (open !== nextOpen) {
setOpen(nextOpen);
onOpenChange?.(nextOpen);
}
});
const handleOpenChange = () => {
setOpen((prevState) => {
onOpenChange?.(!prevState);
return !prevState;
});
// ===================== Trigger: Hover =====================
const onMouseEnter: React.MouseEventHandler<HTMLDivElement> = () => {
if (hoverTrigger) {
triggerOpen(true);
}
};
const onClick = useCallback(
(e: MouseEvent) => {
if (floatButtonGroupRef.current?.contains(e.target as Node)) {
if (floatButtonRef.current?.contains(e.target as Node)) {
handleOpenChange();
}
return;
}
setOpen(false);
onOpenChange?.(false);
},
[trigger],
);
useEffect(() => {
if (trigger === 'click') {
document.addEventListener('click', onClick);
return () => {
document.removeEventListener('click', onClick);
};
const onMouseLeave: React.MouseEventHandler<HTMLDivElement> = () => {
if (hoverTrigger) {
triggerOpen(false);
}
}, [trigger]);
};
// =================== Warning =====================
// ===================== Trigger: Click =====================
const onInternalTriggerButtonClick: FloatButtonGroupProps['onClick'] = (e) => {
if (clickTrigger) {
triggerOpen(!open);
}
onTriggerButtonClick?.(e);
};
React.useEffect(() => {
if (clickTrigger) {
const onDocClick = (e: MouseEvent) => {
// Skip if click on the group
if (floatButtonGroupRef.current?.contains(e.target as Node)) {
return;
}
triggerOpen(false);
};
document.addEventListener('click', onDocClick, { capture: true });
return () => document.removeEventListener('click', onDocClick, { capture: true });
}
}, [clickTrigger]);
// ======================== Warning =========================
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning('FloatButton.Group');
@ -122,9 +124,17 @@ const FloatButtonGroup: React.FC<FloatButtonGroupProps> = (props) => {
);
}
// ========================= Render =========================
return wrapCSSVar(
<FloatButtonGroupProvider value={shape}>
<div ref={floatButtonGroupRef} className={groupCls} style={mergedStyle} {...hoverAction}>
<div
ref={floatButtonGroupRef}
className={groupCls}
style={mergedStyle}
// Hover trigger
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
>
{isMenuMode ? (
<>
<CSSMotion visible={open} motionName={`${groupPrefixCls}-wrap`}>
@ -133,12 +143,12 @@ const FloatButtonGroup: React.FC<FloatButtonGroupProps> = (props) => {
)}
</CSSMotion>
<FloatButton
ref={floatButtonRef}
type={type}
icon={open ? mergedCloseIcon : icon}
description={description}
aria-label={props['aria-label']}
className={`${groupPrefixCls}-trigger`}
onClick={onInternalTriggerButtonClick}
{...floatButtonProps}
/>
</>
@ -150,4 +160,4 @@ const FloatButtonGroup: React.FC<FloatButtonGroupProps> = (props) => {
);
};
export default memo(FloatButtonGroup);
export default FloatButtonGroup;

View File

@ -30400,6 +30400,89 @@ exports[`renders components/form/demo/variant.tsx extend context correctly 1`] =
class="ant-form ant-form-horizontal"
style="max-width: 600px;"
>
<div
class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
>
<div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-6"
>
<label
class=""
for="variant"
title="Form variant"
>
Form variant
</label>
</div>
<div
class="ant-col ant-form-item-control ant-col-xs-24 ant-col-sm-14"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<div
class="ant-segmented"
id="variant"
>
<div
class="ant-segmented-group"
>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="outlined"
>
outlined
</div>
</label>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="filled"
>
filled
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="borderless"
>
borderless
</div>
</label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-form-item"
>

View File

@ -12619,6 +12619,89 @@ exports[`renders components/form/demo/variant.tsx correctly 1`] = `
class="ant-form ant-form-horizontal"
style="max-width:600px"
>
<div
class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
>
<div
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-6"
>
<label
class=""
for="variant"
title="Form variant"
>
Form variant
</label>
</div>
<div
class="ant-col ant-form-item-control ant-col-xs-24 ant-col-sm-14"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<div
class="ant-segmented"
id="variant"
>
<div
class="ant-segmented-group"
>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="outlined"
>
outlined
</div>
</label>
<label
class="ant-segmented-item ant-segmented-item-selected"
>
<input
checked=""
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="filled"
>
filled
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="borderless"
>
borderless
</div>
</label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-form-item"
>

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import {
Button,
Cascader,
@ -9,7 +9,9 @@ import {
Mentions,
Select,
TreeSelect,
Segmented,
} from 'antd';
import type { FormProps } from 'antd';
const { RangePicker } = DatePicker;
@ -24,78 +26,99 @@ const formItemLayout = {
},
};
const App: React.FC = () => (
<Form {...formItemLayout} variant="filled" style={{ maxWidth: 600 }}>
<Form.Item label="Input" name="Input" rules={[{ required: true, message: 'Please input!' }]}>
<Input />
</Form.Item>
const App: React.FC = () => {
const [componentVariant, setComponentVariant] = useState<FormProps['variant']>('filled');
<Form.Item
label="InputNumber"
name="InputNumber"
rules={[{ required: true, message: 'Please input!' }]}
const onFormVariantChange = ({ variant }: { variant: FormProps['variant'] }) => {
setComponentVariant(variant);
};
return (
<Form
{...formItemLayout}
onValuesChange={onFormVariantChange}
variant={componentVariant}
style={{ maxWidth: 600 }}
initialValues={{ variant: componentVariant }}
>
<InputNumber style={{ width: '100%' }} />
</Form.Item>
<Form.Item label="Form variant" name="variant">
<Segmented options={['outlined', 'filled', 'borderless']} />
</Form.Item>
<Form.Item
label="TextArea"
name="TextArea"
rules={[{ required: true, message: 'Please input!' }]}
>
<Input.TextArea />
</Form.Item>
<Form.Item label="Input" name="Input" rules={[{ required: true, message: 'Please input!' }]}>
<Input />
</Form.Item>
<Form.Item
label="Mentions"
name="Mentions"
rules={[{ required: true, message: 'Please input!' }]}
>
<Mentions />
</Form.Item>
<Form.Item
label="InputNumber"
name="InputNumber"
rules={[{ required: true, message: 'Please input!' }]}
>
<InputNumber style={{ width: '100%' }} />
</Form.Item>
<Form.Item label="Select" name="Select" rules={[{ required: true, message: 'Please input!' }]}>
<Select />
</Form.Item>
<Form.Item
label="TextArea"
name="TextArea"
rules={[{ required: true, message: 'Please input!' }]}
>
<Input.TextArea />
</Form.Item>
<Form.Item
label="Cascader"
name="Cascader"
rules={[{ required: true, message: 'Please input!' }]}
>
<Cascader />
</Form.Item>
<Form.Item
label="Mentions"
name="Mentions"
rules={[{ required: true, message: 'Please input!' }]}
>
<Mentions />
</Form.Item>
<Form.Item
label="TreeSelect"
name="TreeSelect"
rules={[{ required: true, message: 'Please input!' }]}
>
<TreeSelect />
</Form.Item>
<Form.Item
label="Select"
name="Select"
rules={[{ required: true, message: 'Please input!' }]}
>
<Select />
</Form.Item>
<Form.Item
label="DatePicker"
name="DatePicker"
rules={[{ required: true, message: 'Please input!' }]}
>
<DatePicker />
</Form.Item>
<Form.Item
label="Cascader"
name="Cascader"
rules={[{ required: true, message: 'Please input!' }]}
>
<Cascader />
</Form.Item>
<Form.Item
label="RangePicker"
name="RangePicker"
rules={[{ required: true, message: 'Please input!' }]}
>
<RangePicker />
</Form.Item>
<Form.Item
label="TreeSelect"
name="TreeSelect"
rules={[{ required: true, message: 'Please input!' }]}
>
<TreeSelect />
</Form.Item>
<Form.Item wrapperCol={{ offset: 6, span: 16 }}>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
<Form.Item
label="DatePicker"
name="DatePicker"
rules={[{ required: true, message: 'Please input!' }]}
>
<DatePicker />
</Form.Item>
<Form.Item
label="RangePicker"
name="RangePicker"
rules={[{ required: true, message: 'Please input!' }]}
>
<RangePicker />
</Form.Item>
<Form.Item wrapperCol={{ offset: 6, span: 16 }}>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
export default App;

View File

@ -38,7 +38,7 @@ export interface ComponentToken {
*/
cardPaddingLG: string;
/**
* @desc
* @desc
* @descEN Font size of title
*/
titleFontSize: number;

View File

@ -1,6 +1,6 @@
{
"name": "antd",
"version": "5.20.1",
"version": "5.20.2",
"description": "An enterprise-class UI design language and React components implementation",
"keywords": [
"ant",