(ConfigContext);
+
+ const prefixCls = getPrefixCls('flex', customizePrefixCls);
+
+ const [wrapSSR, hashId] = useStyle(prefixCls);
+
+ const mergedVertical = vertical ?? ctxFlex?.vertical;
+
+ const mergedCls = classNames(
+ className,
+ rootClassName,
+ ctxFlex?.className,
+ prefixCls,
+ hashId,
+ createFlexClassNames(prefixCls, props),
+ {
+ [`${prefixCls}-rtl`]: ctxDirection === 'rtl',
+ [`${prefixCls}-gap-${gap}`]: isPresetSize(gap),
+ [`${prefixCls}-vertical`]: mergedVertical,
+ },
+ );
+
+ const mergedStyle: React.CSSProperties = { ...ctxFlex?.style, ...style };
+
+ if (flex) {
+ mergedStyle.flex = flex;
+ }
+
+ if (gap && !isPresetSize(gap)) {
+ mergedStyle.gap = gap;
+ }
+
+ return wrapSSR(
+
+ {children}
+ ,
+ );
+});
+
+if (process.env.NODE_ENV !== 'production') {
+ Flex.displayName = 'Flex';
+}
+
+export default Flex;
diff --git a/components/flex/index.zh-CN.md b/components/flex/index.zh-CN.md
new file mode 100644
index 0000000000..8ca29d83ea
--- /dev/null
+++ b/components/flex/index.zh-CN.md
@@ -0,0 +1,46 @@
+---
+category: Components
+subtitle: 弹性布局
+group: 布局
+title: Flex
+cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*SMzgSJZE_AwAAAAAAAAAAAAADrJ8AQ/original
+coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*8yArQ43EGccAAAAAAAAAAAAADrJ8AQ/original
+tag: New
+---
+
+弹性布局。自 `5.10.0` 版本开始提供该组件。
+
+## 何时使用
+
+- 适合设置元素之间的间距。
+- 适合设置各种水平、垂直对齐方式。
+
+## 代码演示
+
+
+基本布局
+对齐方式
+设置间隙
+自动换行
+组合使用
+调试专用
+
+## API
+
+> 自 `antd@5.10.0` 版本开始提供该组件。Flex 组件默认行为在水平模式下,为向上对齐,在垂直模式下,为拉伸对齐,你可以通过属性进行调整。
+
+通用属性参考:[通用属性](/docs/react/common-props)
+
+| 属性 | 说明 | 类型 | 默认值 | 版本 |
+| --- | --- | --- | --- | --- |
+| vertical | flex 主轴的方向是否垂直,使用 `flex-direction: column` | boolean | `false` |
+| wrap | 设置元素单行显示还是多行显示 | 参考 [flex-wrap](https://developer.mozilla.org/zh-CN/docs/Web/CSS/flex-wrap) | nowrap | |
+| justify | 设置元素在主轴方向上的对齐方式 | 参考 [justify-content](https://developer.mozilla.org/zh-CN/docs/Web/CSS/justify-content) | normal | |
+| align | 设置元素在交叉轴方向上的对齐方式 | 参考 [align-items](https://developer.mozilla.org/zh-CN/docs/Web/CSS/align-items) | normal | |
+| flex | flex CSS 简写属性 | 参考 [flex](https://developer.mozilla.org/zh-CN/docs/Web/CSS/flex) | normal | |
+| gap | 设置网格之间的间隙 | `small` \| `middle` \| `large` \| string \| number | - | |
+| component | 自定义元素类型 | React.ComponentType | `div` | |
+
+## Design Token
+
+
diff --git a/components/flex/interface.ts b/components/flex/interface.ts
new file mode 100644
index 0000000000..d5240cfcae
--- /dev/null
+++ b/components/flex/interface.ts
@@ -0,0 +1,17 @@
+import type React from 'react';
+
+import type { AnyObject } from '../_util/type';
+import type { SizeType } from '../config-provider/SizeContext';
+
+export interface FlexProps extends React.HTMLAttributes {
+ prefixCls?: string;
+ rootClassName?: string;
+ vertical?: boolean;
+ wrap?: React.CSSProperties['flexWrap'];
+ justify?: React.CSSProperties['justifyContent'];
+ align?: React.CSSProperties['alignItems'];
+ flex?: React.CSSProperties['flex'];
+ gap?: React.CSSProperties['gap'] | SizeType;
+ children: React.ReactNode;
+ component?: React.ComponentType | string;
+}
diff --git a/components/flex/style/index.ts b/components/flex/style/index.ts
new file mode 100644
index 0000000000..d90e646578
--- /dev/null
+++ b/components/flex/style/index.ts
@@ -0,0 +1,111 @@
+import type { CSSInterpolation } from '@ant-design/cssinjs';
+
+import type { FullToken, GenerateStyle } from '../../theme/internal';
+import { genComponentStyleHook, mergeToken } from '../../theme/internal';
+import { alignItemsValues, flexWrapValues, justifyContentValues } from '../utils';
+
+/** Component only token. Which will handle additional calculation of alias token */
+export interface ComponentToken {
+ // Component token here
+}
+
+export interface FlexToken extends FullToken<'Flex'> {
+ /**
+ * @nameZH 小间隙
+ * @nameEN Small Gap
+ * @desc 控制元素的小间隙。
+ * @descEN Control the small gap of the element.
+ */
+ flexGapSM: number;
+ /**
+ * @nameZH 间隙
+ * @nameEN Gap
+ * @desc 控制元素的间隙。
+ * @descEN Control the gap of the element.
+ */
+ flexGap: number;
+ /**
+ * @nameZH 大间隙
+ * @nameEN Large Gap
+ * @desc 控制元素的大间隙。
+ * @descEN Control the large gap of the element.
+ */
+ flexGapLG: number;
+}
+
+const genFlexStyle: GenerateStyle = (token) => {
+ const { componentCls } = token;
+ return {
+ [componentCls]: {
+ display: 'flex',
+ '&-vertical': {
+ flexDirection: 'column',
+ },
+ '&-rtl': {
+ direction: 'rtl',
+ },
+ '&:empty': {
+ display: 'none',
+ },
+ },
+ };
+};
+
+const genFlexGapStyle: GenerateStyle = (token) => {
+ const { componentCls } = token;
+ return {
+ [componentCls]: {
+ '&-gap-small': {
+ gap: token.flexGapSM,
+ },
+ '&-gap-middle': {
+ gap: token.flexGap,
+ },
+ '&-gap-large': {
+ gap: token.flexGapLG,
+ },
+ },
+ };
+};
+
+const genFlexWrapStyle: GenerateStyle = (token) => {
+ const { componentCls } = token;
+ const wrapStyle: CSSInterpolation = {};
+ flexWrapValues.forEach((value) => {
+ wrapStyle[`${componentCls}-wrap-${value}`] = { flexWrap: value };
+ });
+ return wrapStyle;
+};
+
+const genAlignItemsStyle: GenerateStyle = (token) => {
+ const { componentCls } = token;
+ const alignStyle: CSSInterpolation = {};
+ alignItemsValues.forEach((value) => {
+ alignStyle[`${componentCls}-align-${value}`] = { alignItems: value };
+ });
+ return alignStyle;
+};
+
+const genJustifyContentStyle: GenerateStyle = (token) => {
+ const { componentCls } = token;
+ const justifyStyle: CSSInterpolation = {};
+ justifyContentValues.forEach((value) => {
+ justifyStyle[`${componentCls}-justify-${value}`] = { justifyContent: value };
+ });
+ return justifyStyle;
+};
+
+export default genComponentStyleHook<'Flex'>('Flex', (token) => {
+ const flexToken = mergeToken(token, {
+ flexGapSM: token.paddingXS,
+ flexGap: token.padding,
+ flexGapLG: token.paddingLG,
+ });
+ return [
+ genFlexStyle(flexToken),
+ genFlexGapStyle(flexToken),
+ genFlexWrapStyle(flexToken),
+ genAlignItemsStyle(flexToken),
+ genJustifyContentStyle(flexToken),
+ ];
+});
diff --git a/components/flex/utils.ts b/components/flex/utils.ts
new file mode 100644
index 0000000000..1cd814e697
--- /dev/null
+++ b/components/flex/utils.ts
@@ -0,0 +1,68 @@
+import classNames from 'classnames';
+
+import type { FlexProps } from './interface';
+
+export const flexWrapValues = ['wrap', 'nowrap', 'wrap-reverse'] as const;
+
+export const justifyContentValues = [
+ 'flex-start',
+ 'flex-end',
+ 'start',
+ 'end',
+ 'center',
+ 'space-between',
+ 'space-around',
+ 'space-evenly',
+ 'stretch',
+ 'normal',
+ 'left',
+ 'right',
+] as const;
+
+export const alignItemsValues = [
+ 'center',
+ 'start',
+ 'end',
+ 'flex-start',
+ 'flex-end',
+ 'self-start',
+ 'self-end',
+ 'baseline',
+ 'normal',
+ 'stretch',
+] as const;
+
+const genClsWrap = (prefixCls: string, props: FlexProps) => {
+ const wrapCls: Record = {};
+ flexWrapValues.forEach((cssKey) => {
+ wrapCls[`${prefixCls}-wrap-${cssKey}`] = props.wrap === cssKey;
+ });
+ return wrapCls;
+};
+
+const genClsAlign = (prefixCls: string, props: FlexProps) => {
+ const alignCls: Record = {};
+ alignItemsValues.forEach((cssKey) => {
+ alignCls[`${prefixCls}-align-${cssKey}`] = props.align === cssKey;
+ });
+ alignCls[`${prefixCls}-align-stretch`] = !props.align && !!props.vertical;
+ return alignCls;
+};
+
+const genClsJustify = (prefixCls: string, props: FlexProps) => {
+ const justifyCls: Record = {};
+ justifyContentValues.forEach((cssKey) => {
+ justifyCls[`${prefixCls}-justify-${cssKey}`] = props.justify === cssKey;
+ });
+ return justifyCls;
+};
+
+function createFlexClassNames(prefixCls: string, props: FlexProps) {
+ return classNames({
+ ...genClsWrap(prefixCls, props),
+ ...genClsAlign(prefixCls, props),
+ ...genClsJustify(prefixCls, props),
+ });
+}
+
+export default createFlexClassNames;
diff --git a/components/grid/__tests__/server.test.tsx b/components/grid/__tests__/server.test.tsx
index 528919f4df..a223446e9f 100644
--- a/components/grid/__tests__/server.test.tsx
+++ b/components/grid/__tests__/server.test.tsx
@@ -13,10 +13,10 @@ describe('Grid.Server', () => {
,
);
- expect((container.querySelector('.ant-row') as HTMLElement)?.style.marginLeft).toBe('-4px');
- expect((container.querySelector('.ant-row') as HTMLElement)?.style.marginRight).toBe('-4px');
- expect((container.querySelector('.ant-row') as HTMLElement)?.style.marginTop).toBe('');
- expect((container.querySelector('.ant-row') as HTMLElement)?.style.marginBottom).toBe('');
+ expect(container.querySelector('.ant-row')?.style.marginLeft).toBe('-4px');
+ expect(container.querySelector('.ant-row')?.style.marginRight).toBe('-4px');
+ expect(container.querySelector('.ant-row')?.style.marginTop).toBe('');
+ expect(container.querySelector('.ant-row')?.style.marginBottom).toBe('');
expect((container.querySelector('.ant-col') as HTMLElement)?.style.paddingLeft).toBe('4px');
expect((container.querySelector('.ant-col') as HTMLElement)?.style.paddingRight).toBe('4px');
diff --git a/components/index.ts b/components/index.ts
index c88e98b25b..0d459f3275 100644
--- a/components/index.ts
+++ b/components/index.ts
@@ -54,6 +54,8 @@ export type {
} from './dropdown';
export { default as Empty } from './empty';
export type { EmptyProps } from './empty';
+export { default as Flex } from './flex';
+export type { FlexProps } from './flex/interface';
export { default as FloatButton } from './float-button';
export type { FloatButtonGroupProps, FloatButtonProps } from './float-button/interface';
export { default as Form } from './form';
diff --git a/components/theme/interface/components.ts b/components/theme/interface/components.ts
index 296efdfa00..fa0b42de32 100644
--- a/components/theme/interface/components.ts
+++ b/components/theme/interface/components.ts
@@ -20,10 +20,11 @@ import type { ComponentToken as DividerComponentToken } from '../../divider/styl
import type { ComponentToken as DrawerComponentToken } from '../../drawer/style';
import type { ComponentToken as DropdownComponentToken } from '../../dropdown/style';
import type { ComponentToken as EmptyComponentToken } from '../../empty/style';
+import type { ComponentToken as FlexComponentToken } from '../../flex/style';
import type { ComponentToken as FloatButtonComponentToken } from '../../float-button/style';
import type { ComponentToken as ImageComponentToken } from '../../image/style';
-import type { ComponentToken as InputComponentToken } from '../../input/style';
import type { ComponentToken as InputNumberComponentToken } from '../../input-number/style';
+import type { ComponentToken as InputComponentToken } from '../../input/style';
import type { ComponentToken as LayoutComponentToken } from '../../layout/style';
import type { ComponentToken as ListComponentToken } from '../../list/style';
import type { ComponentToken as MentionsComponentToken } from '../../mentions/style';
@@ -55,8 +56,8 @@ import type { ComponentToken as TimelineComponentToken } from '../../timeline/st
import type { ComponentToken as TooltipComponentToken } from '../../tooltip/style';
import type { ComponentToken as TourComponentToken } from '../../tour/style';
import type { ComponentToken as TransferComponentToken } from '../../transfer/style';
-import type { ComponentToken as TreeComponentToken } from '../../tree/style';
import type { ComponentToken as TreeSelectComponentToken } from '../../tree-select/style';
+import type { ComponentToken as TreeComponentToken } from '../../tree/style';
import type { ComponentToken as TypographyComponentToken } from '../../typography/style';
import type { ComponentToken as UploadComponentToken } from '../../upload/style';
import type { ComponentToken as FormComponentToken } from '../../form/style';
@@ -82,6 +83,7 @@ export interface ComponentTokenMap {
Drawer?: DrawerComponentToken;
Dropdown?: DropdownComponentToken;
Empty?: EmptyComponentToken;
+ Flex?: FlexComponentToken;
FloatButton?: FloatButtonComponentToken;
Form?: FormComponentToken;
Grid?: {};
diff --git a/tests/__snapshots__/index.test.ts.snap b/tests/__snapshots__/index.test.ts.snap
index f5ad146a56..0a4758fc7e 100644
--- a/tests/__snapshots__/index.test.ts.snap
+++ b/tests/__snapshots__/index.test.ts.snap
@@ -27,6 +27,7 @@ exports[`antd dist files exports modules correctly 1`] = `
"Drawer",
"Dropdown",
"Empty",
+ "Flex",
"FloatButton",
"Form",
"Grid",