Merge branch 'feature' into feat-splitPanel

This commit is contained in:
Wanpan 2024-08-23 21:52:55 +08:00 committed by GitHub
commit 03c8074958
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 1098 additions and 242 deletions

View File

@ -1,7 +1,6 @@
import React from 'react';
import { BgColorsOutlined, SmileOutlined } from '@ant-design/icons';
import { FloatButton } from 'antd';
import { useTheme } from 'antd-style';
import { CompactTheme, DarkTheme } from 'antd-token-previewer/es/icons';
// import { Motion } from 'antd-token-previewer/es/icons';
import { FormattedMessage, useLocation } from 'dumi';
@ -20,7 +19,6 @@ export interface ThemeSwitchProps {
const ThemeSwitch: React.FC<ThemeSwitchProps> = (props) => {
const { value = ['light'], onChange } = props;
const token = useTheme();
const { pathname, search } = useLocation();
// const isMotionOff = value.includes('motion-off');
@ -38,7 +36,7 @@ const ThemeSwitch: React.FC<ThemeSwitchProps> = (props) => {
>
<Link
to={getLocalizedPathname('/theme-editor', isZhCN(pathname), search)}
style={{ display: 'block', marginBottom: token.margin }}
style={{ display: 'block' }}
>
<FloatButton
icon={<BgColorsOutlined />}

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,16 +13,17 @@ 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,
style,
shape = 'circle',
type = 'default',
placement = 'top',
icon = <FileTextOutlined />,
closeIcon,
description,
@ -29,23 +31,29 @@ 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 />;
const prefixCls = getPrefixCls(floatButtonPrefixCls, customizePrefixCls);
const rootCls = useCSSVarCls(prefixCls);
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls);
const groupPrefixCls = `${prefixCls}-group`;
const isMenuMode = trigger && ['click', 'hover'].includes(trigger);
const isValidPlacement = placement && ['top', 'left', 'right', 'bottom'].includes(placement);
const groupCls = classNames(groupPrefixCls, hashId, cssVarCls, rootCls, className, {
[`${groupPrefixCls}-rtl`]: direction === 'rtl',
[`${groupPrefixCls}-${shape}`]: shape,
[`${groupPrefixCls}-${shape}-shadow`]: !trigger,
[`${groupPrefixCls}-${shape}-shadow`]: !isMenuMode,
[`${groupPrefixCls}-${placement}`]: isMenuMode && isValidPlacement, // 只有菜单模式才支持弹出方向
});
// ============================ zIndex ============================
@ -59,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');
@ -116,10 +124,18 @@ const FloatButtonGroup: React.FC<FloatButtonGroupProps> = (props) => {
);
}
// ========================= Render =========================
return wrapCSSVar(
<FloatButtonGroupProvider value={shape}>
<div ref={floatButtonGroupRef} className={groupCls} style={mergedStyle} {...hoverAction}>
{trigger && ['click', 'hover'].includes(trigger) ? (
<div
ref={floatButtonGroupRef}
className={groupCls}
style={mergedStyle}
// Hover trigger
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
>
{isMenuMode ? (
<>
<CSSMotion visible={open} motionName={`${groupPrefixCls}-wrap`}>
{({ className: motionClassName }) => (
@ -127,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}
/>
</>
@ -144,4 +160,4 @@ const FloatButtonGroup: React.FC<FloatButtonGroupProps> = (props) => {
);
};
export default memo(FloatButtonGroup);
export default FloatButtonGroup;

View File

@ -574,7 +574,7 @@ Array [
</span>
</button>,
<div
class="ant-float-btn-group ant-float-btn-group-circle"
class="ant-float-btn-group ant-float-btn-group-circle ant-float-btn-group-top"
style="inset-inline-end: 24px;"
>
<div
@ -737,7 +737,7 @@ Array [
</button>
</div>,
<div
class="ant-float-btn-group ant-float-btn-group-square"
class="ant-float-btn-group ant-float-btn-group-square ant-float-btn-group-top"
style="inset-inline-end: 88px;"
>
<div
@ -1292,7 +1292,7 @@ exports[`renders components/float-button/demo/group.tsx extend context correctly
exports[`renders components/float-button/demo/group-menu.tsx extend context correctly 1`] = `
Array [
<div
class="ant-float-btn-group ant-float-btn-group-circle"
class="ant-float-btn-group ant-float-btn-group-circle ant-float-btn-group-top"
style="inset-inline-end: 24px;"
>
<button
@ -1333,7 +1333,7 @@ Array [
</button>
</div>,
<div
class="ant-float-btn-group ant-float-btn-group-circle"
class="ant-float-btn-group ant-float-btn-group-circle ant-float-btn-group-top"
style="inset-inline-end: 94px;"
>
<button
@ -1378,6 +1378,184 @@ Array [
exports[`renders components/float-button/demo/group-menu.tsx extend context correctly 2`] = `[]`;
exports[`renders components/float-button/demo/placement.tsx extend context correctly 1`] = `
<div
class="ant-flex ant-flex-align-center ant-flex-justify-space-evenly"
style="width: 100%; height: 100vh; overflow: hidden; position: relative;"
>
<div
style="width: 100px; height: 100px; position: relative;"
>
<div
class="ant-float-btn-group ant-float-btn-group-circle ant-float-btn-group-top"
style="position: absolute; inset-inline-end: 30px; bottom: 80px;"
>
<button
class="ant-float-btn ant-float-btn-group-trigger ant-float-btn-default ant-float-btn-circle"
type="button"
>
<div
class="ant-float-btn-body"
>
<div
class="ant-float-btn-content"
>
<div
class="ant-float-btn-icon"
>
<span
aria-label="up"
class="anticon anticon-up"
role="img"
>
<svg
aria-hidden="true"
data-icon="up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
/>
</svg>
</span>
</div>
</div>
</div>
</button>
</div>
<div
class="ant-float-btn-group ant-float-btn-group-circle ant-float-btn-group-right"
style="position: absolute; inset-inline-end: -20px; bottom: 30px;"
>
<button
class="ant-float-btn ant-float-btn-group-trigger ant-float-btn-default ant-float-btn-circle"
type="button"
>
<div
class="ant-float-btn-body"
>
<div
class="ant-float-btn-content"
>
<div
class="ant-float-btn-icon"
>
<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>
</div>
</div>
</div>
</button>
</div>
<div
class="ant-float-btn-group ant-float-btn-group-circle ant-float-btn-group-bottom"
style="position: absolute; inset-inline-end: 30px; bottom: -20px;"
>
<button
class="ant-float-btn ant-float-btn-group-trigger ant-float-btn-default ant-float-btn-circle"
type="button"
>
<div
class="ant-float-btn-body"
>
<div
class="ant-float-btn-content"
>
<div
class="ant-float-btn-icon"
>
<span
aria-label="down"
class="anticon anticon-down"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</div>
</div>
</div>
</button>
</div>
<div
class="ant-float-btn-group ant-float-btn-group-circle ant-float-btn-group-left"
style="position: absolute; inset-inline-end: 80px; bottom: 30px;"
>
<button
class="ant-float-btn ant-float-btn-group-trigger ant-float-btn-default ant-float-btn-circle"
type="button"
>
<div
class="ant-float-btn-body"
>
<div
class="ant-float-btn-content"
>
<div
class="ant-float-btn-icon"
>
<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>
</div>
</div>
</div>
</button>
</div>
</div>
</div>
`;
exports[`renders components/float-button/demo/placement.tsx extend context correctly 2`] = `[]`;
exports[`renders components/float-button/demo/render-panel.tsx extend context correctly 1`] = `
<div
style="display: flex; column-gap: 16px; align-items: center;"
@ -1614,7 +1792,7 @@ exports[`renders components/float-button/demo/render-panel.tsx extend context co
</button>
</div>
<div
class="ant-float-btn-group ant-float-btn-pure ant-float-btn-group-circle"
class="ant-float-btn-group ant-float-btn-pure ant-float-btn-group-circle ant-float-btn-group-top"
>
<div
class="ant-float-btn-group-wrap-appear ant-float-btn-group-wrap-appear-start ant-float-btn-group-wrap ant-float-btn-group-wrap"

View File

@ -526,7 +526,7 @@ Array [
</span>
</button>,
<div
class="ant-float-btn-group ant-float-btn-group-circle"
class="ant-float-btn-group ant-float-btn-group-circle ant-float-btn-group-top"
style="inset-inline-end:24px"
>
<div
@ -689,7 +689,7 @@ Array [
</button>
</div>,
<div
class="ant-float-btn-group ant-float-btn-group-square"
class="ant-float-btn-group ant-float-btn-group-square ant-float-btn-group-top"
style="inset-inline-end:88px"
>
<div
@ -1238,7 +1238,7 @@ Array [
exports[`renders components/float-button/demo/group-menu.tsx correctly 1`] = `
Array [
<div
class="ant-float-btn-group ant-float-btn-group-circle"
class="ant-float-btn-group ant-float-btn-group-circle ant-float-btn-group-top"
style="inset-inline-end:24px"
>
<button
@ -1279,7 +1279,7 @@ Array [
</button>
</div>,
<div
class="ant-float-btn-group ant-float-btn-group-circle"
class="ant-float-btn-group ant-float-btn-group-circle ant-float-btn-group-top"
style="inset-inline-end:94px"
>
<button
@ -1322,6 +1322,182 @@ Array [
]
`;
exports[`renders components/float-button/demo/placement.tsx correctly 1`] = `
<div
class="ant-flex ant-flex-align-center ant-flex-justify-space-evenly"
style="width:100%;height:100vh;overflow:hidden;position:relative"
>
<div
style="width:100px;height:100px;position:relative"
>
<div
class="ant-float-btn-group ant-float-btn-group-circle ant-float-btn-group-top"
style="position:absolute;inset-inline-end:30px;bottom:80px"
>
<button
class="ant-float-btn ant-float-btn-group-trigger ant-float-btn-default ant-float-btn-circle"
type="button"
>
<div
class="ant-float-btn-body"
>
<div
class="ant-float-btn-content"
>
<div
class="ant-float-btn-icon"
>
<span
aria-label="up"
class="anticon anticon-up"
role="img"
>
<svg
aria-hidden="true"
data-icon="up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
/>
</svg>
</span>
</div>
</div>
</div>
</button>
</div>
<div
class="ant-float-btn-group ant-float-btn-group-circle ant-float-btn-group-right"
style="position:absolute;inset-inline-end:-20px;bottom:30px"
>
<button
class="ant-float-btn ant-float-btn-group-trigger ant-float-btn-default ant-float-btn-circle"
type="button"
>
<div
class="ant-float-btn-body"
>
<div
class="ant-float-btn-content"
>
<div
class="ant-float-btn-icon"
>
<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>
</div>
</div>
</div>
</button>
</div>
<div
class="ant-float-btn-group ant-float-btn-group-circle ant-float-btn-group-bottom"
style="position:absolute;inset-inline-end:30px;bottom:-20px"
>
<button
class="ant-float-btn ant-float-btn-group-trigger ant-float-btn-default ant-float-btn-circle"
type="button"
>
<div
class="ant-float-btn-body"
>
<div
class="ant-float-btn-content"
>
<div
class="ant-float-btn-icon"
>
<span
aria-label="down"
class="anticon anticon-down"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</div>
</div>
</div>
</button>
</div>
<div
class="ant-float-btn-group ant-float-btn-group-circle ant-float-btn-group-left"
style="position:absolute;inset-inline-end:80px;bottom:30px"
>
<button
class="ant-float-btn ant-float-btn-group-trigger ant-float-btn-default ant-float-btn-circle"
type="button"
>
<div
class="ant-float-btn-body"
>
<div
class="ant-float-btn-content"
>
<div
class="ant-float-btn-icon"
>
<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>
</div>
</div>
</div>
</button>
</div>
</div>
</div>
`;
exports[`renders components/float-button/demo/render-panel.tsx correctly 1`] = `
<div
style="display:flex;column-gap:16px;align-items:center"
@ -1558,7 +1734,7 @@ exports[`renders components/float-button/demo/render-panel.tsx correctly 1`] = `
</button>
</div>
<div
class="ant-float-btn-group ant-float-btn-pure ant-float-btn-group-circle"
class="ant-float-btn-group ant-float-btn-pure ant-float-btn-group-circle ant-float-btn-group-top"
>
<div
class="ant-float-btn-group-wrap"

View File

@ -120,4 +120,16 @@ describe('FloatButtonGroup', () => {
expect(container.querySelector('.ant-badge')).toBeTruthy();
});
it('FloatButton.Group should support placement', () => {
(['bottom', 'left', 'right', 'top'] as const).forEach((placement) => {
const { container } = render(
<FloatButton.Group placement={placement} trigger="click" open>
<FloatButton />
</FloatButton.Group>,
);
const element = container.querySelector<HTMLDivElement>('.ant-float-btn-group');
expect(element).toHaveClass(`ant-float-btn-group-${placement}`);
});
});
});

View File

@ -0,0 +1,7 @@
## zh-CN
自定义弹出位置,提供了四个预设值:`top`、`right`、`bottom`、`left`,默认值为 `top`
## en-US
Customize animation placement, providing four preset placement: `top`, `right`, `bottom`, `left`, the `top` position by default.

View File

@ -0,0 +1,74 @@
import React from 'react';
import {
CommentOutlined,
DownOutlined,
LeftOutlined,
RightOutlined,
UpOutlined,
} from '@ant-design/icons';
import { Flex, FloatButton } from 'antd';
const BOX_SIZE = 100;
const BUTTON_SIZE = 40;
const wrapperStyle: React.CSSProperties = {
width: '100%',
height: '100vh',
overflow: 'hidden',
position: 'relative',
};
const boxStyle: React.CSSProperties = {
width: BOX_SIZE,
height: BOX_SIZE,
position: 'relative',
};
const insetInlineEnd: React.CSSProperties['insetInlineEnd'][] = [
(BOX_SIZE - BUTTON_SIZE) / 2,
-(BUTTON_SIZE / 2),
(BOX_SIZE - BUTTON_SIZE) / 2,
BOX_SIZE - BUTTON_SIZE / 2,
];
const bottom: React.CSSProperties['bottom'][] = [
BOX_SIZE - BUTTON_SIZE / 2,
(BOX_SIZE - BUTTON_SIZE) / 2,
-BUTTON_SIZE / 2,
(BOX_SIZE - BUTTON_SIZE) / 2,
];
const icons = [
<UpOutlined key="up" />,
<RightOutlined key="right" />,
<DownOutlined key="down" />,
<LeftOutlined key="left" />,
];
const App: React.FC = () => (
<Flex justify="space-evenly" align="center" style={wrapperStyle}>
<div style={boxStyle}>
{(['top', 'right', 'bottom', 'left'] as const).map((placement, i) => {
const style: React.CSSProperties = {
position: 'absolute',
insetInlineEnd: insetInlineEnd[i],
bottom: bottom[i],
};
return (
<FloatButton.Group
key={placement}
trigger="click"
placement={placement}
style={style}
icon={icons[i]}
>
<FloatButton />
<FloatButton icon={<CommentOutlined />} />
</FloatButton.Group>
);
})}
</div>
</Flex>
);
export default App;

View File

@ -26,6 +26,7 @@ tag: 5.0.0
<code src="./demo/group.tsx" iframe="360">FloatButton Group</code>
<code src="./demo/group-menu.tsx" iframe="360">Menu mode</code>
<code src="./demo/controlled.tsx" iframe="360">Controlled mode</code>
<code src="./demo/placement.tsx" iframe="380" version="5.21.0">placement</code>
<code src="./demo/back-top.tsx" iframe="360">BackTop</code>
<code src="./demo/badge.tsx" iframe="360">badge</code>
<code src="./demo/badge-debug.tsx" iframe="360" debug>debug dot</code>
@ -59,6 +60,7 @@ Common props ref[Common props](/docs/react/common-props)
| trigger | Which action can trigger menu open/close | `click` \| `hover` | - | |
| open | Whether the menu is visible or not, use it with trigger | boolean | - | |
| closeIcon | Customize close button icon | React.ReactNode | `<CloseOutlined />` | |
| placement | Customize menu animation placement | `top` \| `left` \| `right` \| `bottom` | `top` | 5.21.0 |
| onOpenChange | Callback executed when active menu is changed, use it with trigger | (open: boolean) => void | - | |
### FloatButton.BackTop

View File

@ -27,6 +27,7 @@ tag: 5.0.0
<code src="./demo/group.tsx" iframe="360">浮动按钮组</code>
<code src="./demo/group-menu.tsx" iframe="360">菜单模式</code>
<code src="./demo/controlled.tsx" iframe="360">受控模式</code>
<code src="./demo/placement.tsx" iframe="380" version="5.21.0">弹出方向</code>
<code src="./demo/back-top.tsx" iframe="360">回到顶部</code>
<code src="./demo/badge.tsx" iframe="360">徽标数</code>
<code src="./demo/badge-debug.tsx" iframe="360" debug>调试小圆点使用</code>
@ -60,6 +61,7 @@ tag: 5.0.0
| trigger | 触发方式(有触发方式为菜单模式) | `click` \| `hover` | - | |
| open | 受控展开,需配合 trigger 一起使用 | boolean | - | |
| closeIcon | 自定义关闭按钮 | React.ReactNode | `<CloseOutlined />` | |
| placement | 自定义菜单弹出位置 | `top` \| `left` \| `right` \| `bottom` | `top` | 5.21.0 |
| onOpenChange | 展开收起时的回调,需配合 trigger 一起使用 | (open: boolean) => void | - | |
### FloatButton.BackTop

View File

@ -49,6 +49,8 @@ export interface FloatButtonGroupProps extends FloatButtonProps {
open?: boolean;
// 关闭按钮自定义图标
closeIcon?: React.ReactNode;
// 菜单弹出方向
placement?: 'top' | 'left' | 'right' | 'bottom';
// 展开收起的回调
onOpenChange?: (open: boolean) => void;
}

View File

@ -1,12 +1,12 @@
import type { CSSObject } from '@ant-design/cssinjs';
import { Keyframes, unit } from '@ant-design/cssinjs';
import { unit } from '@ant-design/cssinjs';
import { resetComponent } from '../../style';
import { initFadeMotion } from '../../style/motion/fade';
import { initMotion } from '../../style/motion/motion';
import type { FullToken, GenerateStyle, GetDefaultToken } from '../../theme/internal';
import { genStyleHooks, mergeToken } from '../../theme/internal';
import getOffset from '../util';
import floatButtonGroupMotion from './keyframes';
/** Component only token. Which will handle additional calculation of alias token */
export interface ComponentToken {
@ -26,7 +26,7 @@ export interface ComponentToken {
* @desc FloatButton Token
* @descEN Token for FloatButton component
*/
type FloatButtonToken = FullToken<'FloatButton'> & {
export type FloatButtonToken = FullToken<'FloatButton'> & {
/**
* @desc FloatButton
* @descEN Color of FloatButton
@ -86,58 +86,6 @@ type FloatButtonToken = FullToken<'FloatButton'> & {
floatButtonInsetInlineEnd: number;
};
const initFloatButtonGroupMotion = (token: FloatButtonToken) => {
const { componentCls, floatButtonSize, motionDurationSlow, motionEaseInOutCirc } = token;
const groupPrefixCls = `${componentCls}-group`;
const moveDownIn = new Keyframes('antFloatButtonMoveDownIn', {
'0%': {
transform: `translate3d(0, ${unit(floatButtonSize)}, 0)`,
transformOrigin: '0 0',
opacity: 0,
},
'100%': {
transform: 'translate3d(0, 0, 0)',
transformOrigin: '0 0',
opacity: 1,
},
});
const moveDownOut = new Keyframes('antFloatButtonMoveDownOut', {
'0%': {
transform: 'translate3d(0, 0, 0)',
transformOrigin: '0 0',
opacity: 1,
},
'100%': {
transform: `translate3d(0, ${unit(floatButtonSize)}, 0)`,
transformOrigin: '0 0',
opacity: 0,
},
});
return [
{
[`${groupPrefixCls}-wrap`]: {
...initMotion(`${groupPrefixCls}-wrap`, moveDownIn, moveDownOut, motionDurationSlow, true),
},
},
{
[`${groupPrefixCls}-wrap`]: {
[`
&${groupPrefixCls}-wrap-enter,
&${groupPrefixCls}-wrap-appear
`]: {
opacity: 0,
animationTimingFunction: motionEaseInOutCirc,
},
[`&${groupPrefixCls}-wrap-leave`]: {
animationTimingFunction: motionEaseInOutCirc,
},
},
},
];
};
// ============================== Group ==============================
const floatButtonGroupStyle: GenerateStyle<FloatButtonToken, CSSObject> = (token) => {
const {
@ -157,22 +105,25 @@ const floatButtonGroupStyle: GenerateStyle<FloatButtonToken, CSSObject> = (token
[groupPrefixCls]: {
...resetComponent(token),
zIndex: zIndexPopupBase,
display: 'block',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
border: 'none',
position: 'fixed',
width: floatButtonSize,
height: 'auto',
boxShadow: 'none',
minWidth: floatButtonSize,
minHeight: floatButtonSize,
insetInlineEnd: token.floatButtonInsetInlineEnd,
insetBlockEnd: token.floatButtonInsetBlockEnd,
bottom: token.floatButtonInsetBlockEnd,
borderRadius: borderRadiusLG,
[`${groupPrefixCls}-wrap`]: {
zIndex: -1,
display: 'block',
position: 'relative',
marginBottom: margin,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
position: 'absolute',
},
[`&${groupPrefixCls}-rtl`]: {
direction: 'rtl',
@ -181,14 +132,30 @@ const floatButtonGroupStyle: GenerateStyle<FloatButtonToken, CSSObject> = (token
position: 'static',
},
},
[`${groupPrefixCls}-top > ${groupPrefixCls}-wrap`]: {
flexDirection: 'column',
top: 'auto',
bottom: calc(floatButtonSize).add(margin).equal(),
},
[`${groupPrefixCls}-bottom > ${groupPrefixCls}-wrap`]: {
flexDirection: 'column',
top: calc(floatButtonSize).add(margin).equal(),
bottom: 'auto',
},
[`${groupPrefixCls}-right > ${groupPrefixCls}-wrap`]: {
flexDirection: 'row',
left: { _skip_check_: true, value: calc(floatButtonSize).add(margin).equal() },
right: { _skip_check_: true, value: 'auto' },
},
[`${groupPrefixCls}-left > ${groupPrefixCls}-wrap`]: {
flexDirection: 'row',
left: { _skip_check_: true, value: 'auto' },
right: { _skip_check_: true, value: calc(floatButtonSize).add(margin).equal() },
},
[`${groupPrefixCls}-circle`]: {
[`${componentCls}-circle:not(:last-child)`]: {
marginBottom: token.margin,
[`${componentCls}-body`]: {
width: floatButtonSize,
height: floatButtonSize,
borderRadius: '50%',
},
gap: margin,
[`${groupPrefixCls}-wrap`]: {
gap: margin,
},
},
[`${groupPrefixCls}-square`]: {
@ -217,14 +184,23 @@ const floatButtonGroupStyle: GenerateStyle<FloatButtonToken, CSSObject> = (token
},
},
[`${groupPrefixCls}-wrap`]: {
display: 'block',
borderRadius: borderRadiusLG,
boxShadow: token.boxShadowSecondary,
[`${componentCls}-square`]: {
boxShadow: 'none',
marginTop: 0,
borderRadius: 0,
padding: floatButtonBodyPadding,
[`${componentCls}-body`]: {
width: token.floatButtonBodySize,
height: token.floatButtonBodySize,
borderRadius: borderRadiusSM,
},
},
},
},
[`${groupPrefixCls}-top > ${groupPrefixCls}-wrap, ${groupPrefixCls}-bottom > ${groupPrefixCls}-wrap`]:
{
[`> ${componentCls}-square`]: {
'&:first-child': {
borderStartStartRadius: borderRadiusLG,
borderStartEndRadius: borderRadiusLG,
@ -236,13 +212,25 @@ const floatButtonGroupStyle: GenerateStyle<FloatButtonToken, CSSObject> = (token
'&:not(:last-child)': {
borderBottom: `${unit(token.lineWidth)} ${token.lineType} ${token.colorSplit}`,
},
[`${componentCls}-body`]: {
width: token.floatButtonBodySize,
height: token.floatButtonBodySize,
},
},
[`${groupPrefixCls}-left > ${groupPrefixCls}-wrap, ${groupPrefixCls}-right > ${groupPrefixCls}-wrap`]:
{
[`> ${componentCls}-square`]: {
'&:first-child': {
borderStartStartRadius: borderRadiusLG,
borderEndStartRadius: borderRadiusLG,
},
'&:last-child': {
borderStartEndRadius: borderRadiusLG,
borderEndEndRadius: borderRadiusLG,
},
'&:not(:last-child)': {
borderInlineEnd: `${unit(token.lineWidth)} ${token.lineType} ${token.colorSplit}`,
},
},
},
},
[`${groupPrefixCls}-circle-shadow`]: {
boxShadow: 'none',
},
@ -290,7 +278,7 @@ const sharedFloatButtonStyle: GenerateStyle<FloatButtonToken, CSSObject> = (toke
width: floatButtonSize,
height: floatButtonSize,
insetInlineEnd: token.floatButtonInsetInlineEnd,
insetBlockEnd: token.floatButtonInsetBlockEnd,
bottom: token.floatButtonInsetBlockEnd,
boxShadow: token.boxShadowSecondary,
// Pure Panel
'&-pure': {
@ -454,12 +442,11 @@ export default genStyleHooks(
floatButtonBodyPadding: paddingXXS,
badgeOffset: calc(paddingXXS).mul(1.5).equal(),
});
return [
floatButtonGroupStyle(floatButtonToken),
sharedFloatButtonStyle(floatButtonToken),
initFadeMotion(token),
initFloatButtonGroupMotion(floatButtonToken),
floatButtonGroupMotion(floatButtonToken),
];
},
prepareComponentToken,

View File

@ -0,0 +1,153 @@
import { Keyframes, unit } from '@ant-design/cssinjs';
import type { FloatButtonToken } from '.';
import { initMotion } from '../../style/motion/motion';
const floatButtonGroupMotion = (token: FloatButtonToken) => {
const { componentCls, floatButtonSize, motionDurationSlow, motionEaseInOutCirc, calc } = token;
const moveTopIn = new Keyframes('antFloatButtonMoveTopIn', {
'0%': {
transform: `translate3d(0, ${unit(floatButtonSize)}, 0)`,
transformOrigin: '0 0',
opacity: 0,
},
'100%': {
transform: 'translate3d(0, 0, 0)',
transformOrigin: '0 0',
opacity: 1,
},
});
const moveTopOut = new Keyframes('antFloatButtonMoveTopOut', {
'0%': {
transform: 'translate3d(0, 0, 0)',
transformOrigin: '0 0',
opacity: 1,
},
'100%': {
transform: `translate3d(0, ${unit(floatButtonSize)}, 0)`,
transformOrigin: '0 0',
opacity: 0,
},
});
const moveRightIn = new Keyframes('antFloatButtonMoveRightIn', {
'0%': {
transform: `translate3d(${calc(floatButtonSize).mul(-1).equal()}, 0, 0)`,
transformOrigin: '0 0',
opacity: 0,
},
'100%': {
transform: 'translate3d(0, 0, 0)',
transformOrigin: '0 0',
opacity: 1,
},
});
const moveRightOut = new Keyframes('antFloatButtonMoveRightOut', {
'0%': {
transform: 'translate3d(0, 0, 0)',
transformOrigin: '0 0',
opacity: 1,
},
'100%': {
transform: `translate3d(${calc(floatButtonSize).mul(-1).equal()}, 0, 0)`,
transformOrigin: '0 0',
opacity: 0,
},
});
const moveBottomIn = new Keyframes('antFloatButtonMoveBottomIn', {
'0%': {
transform: `translate3d(0, ${calc(floatButtonSize).mul(-1).equal()}, 0)`,
transformOrigin: '0 0',
opacity: 0,
},
'100%': {
transform: 'translate3d(0, 0, 0)',
transformOrigin: '0 0',
opacity: 1,
},
});
const moveBottomOut = new Keyframes('antFloatButtonMoveBottomOut', {
'0%': {
transform: 'translate3d(0, 0, 0)',
transformOrigin: '0 0',
opacity: 1,
},
'100%': {
transform: `translate3d(0, ${calc(floatButtonSize).mul(-1).equal()}, 0)`,
transformOrigin: '0 0',
opacity: 0,
},
});
const moveLeftIn = new Keyframes('antFloatButtonMoveLeftIn', {
'0%': {
transform: `translate3d(${unit(floatButtonSize)}, 0, 0)`,
transformOrigin: '0 0',
opacity: 0,
},
'100%': {
transform: 'translate3d(0, 0, 0)',
transformOrigin: '0 0',
opacity: 1,
},
});
const moveLeftOut = new Keyframes('antFloatButtonMoveLeftOut', {
'0%': {
transform: 'translate3d(0, 0, 0)',
transformOrigin: '0 0',
opacity: 1,
},
'100%': {
transform: `translate3d(${unit(floatButtonSize)}, 0, 0)`,
transformOrigin: '0 0',
opacity: 0,
},
});
const groupPrefixCls = `${componentCls}-group`;
return [
{
[groupPrefixCls]: {
[`&${groupPrefixCls}-top ${groupPrefixCls}-wrap`]: initMotion(
`${groupPrefixCls}-wrap`,
moveTopIn,
moveTopOut,
motionDurationSlow,
true,
),
[`&${groupPrefixCls}-bottom ${groupPrefixCls}-wrap`]: initMotion(
`${groupPrefixCls}-wrap`,
moveBottomIn,
moveBottomOut,
motionDurationSlow,
true,
),
[`&${groupPrefixCls}-left ${groupPrefixCls}-wrap`]: initMotion(
`${groupPrefixCls}-wrap`,
moveLeftIn,
moveLeftOut,
motionDurationSlow,
true,
),
[`&${groupPrefixCls}-right ${groupPrefixCls}-wrap`]: initMotion(
`${groupPrefixCls}-wrap`,
moveRightIn,
moveRightOut,
motionDurationSlow,
true,
),
},
},
{
[`${groupPrefixCls}-wrap`]: {
[`&${groupPrefixCls}-wrap-enter, &${groupPrefixCls}-wrap-appear`]: {
opacity: 0,
animationTimingFunction: motionEaseInOutCirc,
},
[`&${groupPrefixCls}-wrap-leave`]: {
opacity: 1,
animationTimingFunction: motionEaseInOutCirc,
},
},
},
];
};
export default floatButtonGroupMotion;

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",