🔥feat: new component Flex (#44362)

* feat: new component Flex

* feat: new component Flex

* fix: fix

* test: add test case

* fix: fix

* update size-limit

* test: update snap

* fix: fix

* test: update snap

* chore: add use client

* fix: lint

* test: update snap

* fix

* docs: add docs

* fix: fix demo

* clear

* demo: update demo

* update demos

* fix: fix

* fix: fix

* fix: fix

* fix: fix

* test: update snap

* fix: fix

* fix: fix

* demo: update demo

* feat: CP

* fix: use token

* fix: fix

* fix: fix test case

* test: update snap

* Update components/flex/style/index.ts

Co-authored-by: MadCcc <1075746765@qq.com>
Signed-off-by: lijianan <574980606@qq.com>

* Update components/flex/demo/align.tsx

Co-authored-by: MadCcc <1075746765@qq.com>
Signed-off-by: lijianan <574980606@qq.com>

* demo: update demo

* fix: fix

* docs

* update cover

* fix: fix

* test: update case

* fix: rerun CI

* fix: fix

* fix: fix

* fix: fix

* fix: fix

* demo: update demo

* fix: fix lint

* fix: fix

* fix: fix

* fix: fix

* fix:  fix

* fix: fix test case

* fix: fix

* docs: update docs

* fix: fix

* chore: fix

* chore: fix

* docs: update docs

* docs: update

* fix: fix demo

* type: update type

* add debug demo

---------

Signed-off-by: lijianan <574980606@qq.com>
Co-authored-by: 栗嘉男 <mac@macdeMacBook-Pro.local>
Co-authored-by: MadCcc <1075746765@qq.com>
This commit is contained in:
lijianan 2023-09-14 17:04:05 +08:00 committed by GitHub
parent c5bed69883
commit 1e0c3b8c58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 2056 additions and 9 deletions

View File

@ -27,6 +27,7 @@ exports[`antd exports modules correctly 1`] = `
"Drawer", "Drawer",
"Dropdown", "Dropdown",
"Empty", "Empty",
"Flex",
"FloatButton", "FloatButton",
"Form", "Form",
"Grid", "Grid",

View File

@ -19,6 +19,7 @@ import Descriptions from '../../descriptions';
import Divider from '../../divider'; import Divider from '../../divider';
import Drawer from '../../drawer'; import Drawer from '../../drawer';
import Empty from '../../empty'; import Empty from '../../empty';
import Flex from '../../flex';
import Form from '../../form'; import Form from '../../form';
import Image from '../../image'; import Image from '../../image';
import Input from '../../input'; import Input from '../../input';
@ -1267,4 +1268,15 @@ describe('ConfigProvider support style and className props', () => {
expect(container.querySelector('.ant-picker')).toHaveStyle('color: red; font-size: 16px;'); expect(container.querySelector('.ant-picker')).toHaveStyle('color: red; font-size: 16px;');
}); });
it('Should Flex className & style works', () => {
const { container } = render(
<ConfigProvider flex={{ className: 'cp-flex', style: { backgroundColor: 'blue' } }}>
<Flex>test</Flex>
</ConfigProvider>,
);
const element = container.querySelector<HTMLDivElement>('.ant-flex');
expect(element).toHaveClass('cp-flex');
expect(element).toHaveStyle({ backgroundColor: 'blue' });
});
}); });

View File

@ -6,6 +6,7 @@ import type { WarningContextProps } from '../_util/warning';
import type { ShowWaveEffect } from '../_util/wave/interface'; import type { ShowWaveEffect } from '../_util/wave/interface';
import type { BadgeProps } from '../badge'; import type { BadgeProps } from '../badge';
import type { ButtonProps } from '../button'; import type { ButtonProps } from '../button';
import type { FlexProps } from '../flex/interface';
import type { RequiredMark } from '../form/Form'; import type { RequiredMark } from '../form/Form';
import type { InputProps } from '../input'; import type { InputProps } from '../input';
import type { Locale } from '../locale'; import type { Locale } from '../locale';
@ -61,6 +62,10 @@ export interface ButtonConfig extends ComponentStyleConfig {
styles?: ButtonProps['styles']; styles?: ButtonProps['styles'];
} }
export interface FlexConfig extends ComponentStyleConfig {
vertical?: FlexProps['vertical'];
}
export type PopupOverflow = 'viewport' | 'scroll'; export type PopupOverflow = 'viewport' | 'scroll';
export interface WaveConfig { export interface WaveConfig {
@ -154,9 +159,8 @@ export interface ConfigConsumerProps {
tree?: ComponentStyleConfig; tree?: ComponentStyleConfig;
colorPicker?: ComponentStyleConfig; colorPicker?: ComponentStyleConfig;
datePicker?: ComponentStyleConfig; datePicker?: ComponentStyleConfig;
flex?: FlexConfig;
wave?: WaveConfig; wave?: WaveConfig;
warning?: WarningContextProps; warning?: WarningContextProps;
} }

View File

@ -122,6 +122,7 @@ const {
| divider | Set Divider common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 | | divider | Set Divider common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| drawer | Set Drawer common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 | | drawer | Set Drawer common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| empty | Set Empty common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 | | empty | Set Empty common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| flex | Set Flex common props | { className?: string, style?: React.CSSProperties, vertical?: boolean } | - | 5.10.0 |
| form | Set Form common props | { className?: string, style?: React.CSSProperties, validateMessages?: [ValidateMessages](/components/form/#validatemessages), requiredMark?: boolean \| `optional`, scrollToFirstError?: boolean \| [Options](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options) } | - | requiredMark: 4.8.0; colon: 4.18.0; scrollToFirstError: 5.2.0; className: 5.7.0; style: 5.7.0 | | form | Set Form common props | { className?: string, style?: React.CSSProperties, validateMessages?: [ValidateMessages](/components/form/#validatemessages), requiredMark?: boolean \| `optional`, scrollToFirstError?: boolean \| [Options](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options) } | - | requiredMark: 4.8.0; colon: 4.18.0; scrollToFirstError: 5.2.0; className: 5.7.0; style: 5.7.0 |
| image | Set Image common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 | | image | Set Image common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| input | Set Input common props | { autoComplete?: string, className?: string, style?: React.CSSProperties } | - | 4.2.0 | | input | Set Input common props | { autoComplete?: string, className?: string, style?: React.CSSProperties } | - | 4.2.0 |

View File

@ -1,3 +1,5 @@
'use client';
import * as React from 'react'; import * as React from 'react';
import { createTheme } from '@ant-design/cssinjs'; import { createTheme } from '@ant-design/cssinjs';
import IconContext from '@ant-design/icons/lib/components/Context'; import IconContext from '@ant-design/icons/lib/components/Context';
@ -27,6 +29,7 @@ import type {
ConfigConsumerProps, ConfigConsumerProps,
CSPConfig, CSPConfig,
DirectionType, DirectionType,
FlexConfig,
PopupOverflow, PopupOverflow,
Theme, Theme,
ThemeConfig, ThemeConfig,
@ -194,7 +197,7 @@ export interface ConfigProviderProps {
tree?: ComponentStyleConfig; tree?: ComponentStyleConfig;
colorPicker?: ComponentStyleConfig; colorPicker?: ComponentStyleConfig;
datePicker?: ComponentStyleConfig; datePicker?: ComponentStyleConfig;
flex?: FlexConfig;
/** /**
* Wave is special component which only patch on the effect of component interaction. * Wave is special component which only patch on the effect of component interaction.
*/ */
@ -335,6 +338,7 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
tree, tree,
colorPicker, colorPicker,
datePicker, datePicker,
flex,
wave, wave,
warning: warningConfig, warning: warningConfig,
} = props; } = props;
@ -425,6 +429,7 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
tree, tree,
colorPicker, colorPicker,
datePicker, datePicker,
flex,
wave, wave,
warning: warningConfig, warning: warningConfig,
}; };

View File

@ -124,6 +124,7 @@ const {
| divider | 设置 Divider 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 | | divider | 设置 Divider 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| drawer | 设置 Drawer 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 | | drawer | 设置 Drawer 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| empty | 设置 Empty 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 | | empty | 设置 Empty 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| flex | 设置 Flex 组件的通用属性 | { className?: string, style?: React.CSSProperties, vertical?: boolean } | - | 5.10.0 |
| form | 设置 Form 组件的通用属性 | { className?: string, style?: React.CSSProperties, validateMessages?: [ValidateMessages](/components/form-cn#validatemessages), requiredMark?: boolean \| `optional`, colon?: boolean, scrollToFirstError?: boolean \| [Options](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options)} | - | requiredMark: 4.8.0; colon: 4.18.0; scrollToFirstError: 5.2.0; className: 5.7.0; style: 5.7.0 | | form | 设置 Form 组件的通用属性 | { className?: string, style?: React.CSSProperties, validateMessages?: [ValidateMessages](/components/form-cn#validatemessages), requiredMark?: boolean \| `optional`, colon?: boolean, scrollToFirstError?: boolean \| [Options](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options)} | - | requiredMark: 4.8.0; colon: 4.18.0; scrollToFirstError: 5.2.0; className: 5.7.0; style: 5.7.0 |
| image | 设置 Image 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 | | image | 设置 Image 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| input | 设置 Input 组件的通用属性 | { autoComplete?: string, className?: string, style?: React.CSSProperties } | - | 5.7.0 | | input | 设置 Input 组件的通用属性 | { autoComplete?: string, className?: string, style?: React.CSSProperties } | - | 5.7.0 |

View File

@ -0,0 +1,675 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders components/flex/demo/align.tsx extend context correctly 1`] = `
<div
class="ant-flex ant-flex-align-start ant-flex-gap-middle ant-flex-vertical"
>
<p>
Select justify :
</p>
<div
class="ant-segmented"
>
<div
class="ant-segmented-group"
>
<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="flex-start"
>
flex-start
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="center"
>
center
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="flex-end"
>
flex-end
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="space-between"
>
space-between
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="space-around"
>
space-around
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="space-evenly"
>
space-evenly
</div>
</label>
</div>
</div>
<p>
Select align :
</p>
<div
class="ant-segmented"
>
<div
class="ant-segmented-group"
>
<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="flex-start"
>
flex-start
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="center"
>
center
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="flex-end"
>
flex-end
</div>
</label>
</div>
</div>
<div
class="ant-flex ant-flex-align-flex-start ant-flex-justify-flex-start"
style="width: 100%; height: 120px; border-radius: 6px; border: 1px solid #40a9ff;"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
</div>
</div>
`;
exports[`renders components/flex/demo/align.tsx extend context correctly 2`] = `[]`;
exports[`renders components/flex/demo/basic.tsx extend context correctly 1`] = `
<div
class="ant-flex ant-flex-align-stretch ant-flex-gap-middle ant-flex-vertical"
>
<div
class="ant-radio-group ant-radio-group-outline"
>
<label
class="ant-radio-wrapper ant-radio-wrapper-checked"
>
<span
class="ant-radio ant-wave-target ant-radio-checked"
>
<input
checked=""
class="ant-radio-input"
type="radio"
value="horizontal"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
horizontal
</span>
</label>
<label
class="ant-radio-wrapper"
>
<span
class="ant-radio ant-wave-target"
>
<input
class="ant-radio-input"
type="radio"
value="vertical"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
vertical
</span>
</label>
</div>
<div
class="ant-flex"
>
<div
style="width: 25%; height: 54px; background-color: rgba(22, 119, 255, 0.749);"
/>
<div
style="width: 25%; height: 54px; background-color: rgb(22, 119, 255);"
/>
<div
style="width: 25%; height: 54px; background-color: rgba(22, 119, 255, 0.749);"
/>
<div
style="width: 25%; height: 54px; background-color: rgb(22, 119, 255);"
/>
</div>
</div>
`;
exports[`renders components/flex/demo/basic.tsx extend context correctly 2`] = `[]`;
exports[`renders components/flex/demo/combination.tsx extend context correctly 1`] = `
<div
class="ant-card ant-card-bordered ant-card-hoverable"
style="width: 620px;"
>
<div
class="ant-card-body"
style="padding: 0px; overflow: hidden;"
>
<div
class="ant-flex ant-flex-justify-space-between"
>
<img
alt="avatar"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
style="display: block; width: 273px;"
/>
<div
class="ant-flex ant-flex-align-flex-end ant-flex-justify-space-between ant-flex-vertical"
style="padding: 32px;"
>
<h3
class="ant-typography"
>
“antd is an enterprise-class UI design language and React UI library.”
</h3>
<a
class="ant-btn ant-btn-primary"
href="https://ant.design"
target="_blank"
>
<span>
Get Start
</span>
</a>
</div>
</div>
</div>
</div>
`;
exports[`renders components/flex/demo/combination.tsx extend context correctly 2`] = `[]`;
exports[`renders components/flex/demo/debug.tsx extend context correctly 1`] = `
Array [
<div
class="ant-flex ant-flex-align-stretch ant-flex-vertical"
>
<div
style="height: 60px; background-color: rgba(22, 119, 255, 0.749);"
/>
<div
style="height: 60px; background-color: rgb(22, 119, 255);"
/>
<div
style="height: 60px; background-color: rgba(22, 119, 255, 0.749);"
/>
<div
style="height: 60px; background-color: rgb(22, 119, 255);"
/>
</div>,
<div
class="ant-flex"
style="margin-top: 20px;"
>
<div
style="width: 25%; height: 40px; background-color: rgba(22, 119, 255, 0.749);"
/>
<div
style="width: 25%; height: 60px; background-color: rgb(22, 119, 255);"
/>
<div
style="width: 25%; height: 40px; background-color: rgba(22, 119, 255, 0.749);"
/>
<div
style="width: 25%; height: 60px; background-color: rgb(22, 119, 255);"
/>
</div>,
]
`;
exports[`renders components/flex/demo/debug.tsx extend context correctly 2`] = `[]`;
exports[`renders components/flex/demo/gap.tsx extend context correctly 1`] = `
<div
class="ant-flex ant-flex-align-stretch ant-flex-gap-middle ant-flex-vertical"
>
<div
class="ant-radio-group ant-radio-group-outline"
>
<label
class="ant-radio-wrapper ant-radio-wrapper-checked"
>
<span
class="ant-radio ant-wave-target ant-radio-checked"
>
<input
checked=""
class="ant-radio-input"
type="radio"
value="small"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
small
</span>
</label>
<label
class="ant-radio-wrapper"
>
<span
class="ant-radio ant-wave-target"
>
<input
class="ant-radio-input"
type="radio"
value="middle"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
middle
</span>
</label>
<label
class="ant-radio-wrapper"
>
<span
class="ant-radio ant-wave-target"
>
<input
class="ant-radio-input"
type="radio"
value="large"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
large
</span>
</label>
<label
class="ant-radio-wrapper"
>
<span
class="ant-radio ant-wave-target"
>
<input
class="ant-radio-input"
type="radio"
value="customize"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
customize
</span>
</label>
</div>
<div
class="ant-flex ant-flex-gap-small"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Default
</span>
</button>
<button
class="ant-btn ant-btn-dashed"
type="button"
>
<span>
Dashed
</span>
</button>
<button
class="ant-btn ant-btn-link"
type="button"
>
<span>
Link
</span>
</button>
</div>
</div>
`;
exports[`renders components/flex/demo/gap.tsx extend context correctly 2`] = `[]`;
exports[`renders components/flex/demo/wrap.tsx extend context correctly 1`] = `
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
</div>
`;
exports[`renders components/flex/demo/wrap.tsx extend context correctly 2`] = `[]`;

View File

@ -0,0 +1,663 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders components/flex/demo/align.tsx correctly 1`] = `
<div
class="ant-flex ant-flex-align-start ant-flex-gap-middle ant-flex-vertical"
>
<p>
Select justify :
</p>
<div
class="ant-segmented"
>
<div
class="ant-segmented-group"
>
<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="flex-start"
>
flex-start
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="center"
>
center
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="flex-end"
>
flex-end
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="space-between"
>
space-between
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="space-around"
>
space-around
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="space-evenly"
>
space-evenly
</div>
</label>
</div>
</div>
<p>
Select align :
</p>
<div
class="ant-segmented"
>
<div
class="ant-segmented-group"
>
<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="flex-start"
>
flex-start
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="center"
>
center
</div>
</label>
<label
class="ant-segmented-item"
>
<input
class="ant-segmented-item-input"
type="radio"
/>
<div
class="ant-segmented-item-label"
title="flex-end"
>
flex-end
</div>
</label>
</div>
</div>
<div
class="ant-flex ant-flex-align-flex-start ant-flex-justify-flex-start"
style="width:100%;height:120px;border-radius:6px;border:1px solid #40a9ff"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
</div>
</div>
`;
exports[`renders components/flex/demo/basic.tsx correctly 1`] = `
<div
class="ant-flex ant-flex-align-stretch ant-flex-gap-middle ant-flex-vertical"
>
<div
class="ant-radio-group ant-radio-group-outline"
>
<label
class="ant-radio-wrapper ant-radio-wrapper-checked"
>
<span
class="ant-radio ant-wave-target ant-radio-checked"
>
<input
checked=""
class="ant-radio-input"
type="radio"
value="horizontal"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
horizontal
</span>
</label>
<label
class="ant-radio-wrapper"
>
<span
class="ant-radio ant-wave-target"
>
<input
class="ant-radio-input"
type="radio"
value="vertical"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
vertical
</span>
</label>
</div>
<div
class="ant-flex"
>
<div
style="width:25%;height:54px;background-color:#1677ffbf"
/>
<div
style="width:25%;height:54px;background-color:#1677ff"
/>
<div
style="width:25%;height:54px;background-color:#1677ffbf"
/>
<div
style="width:25%;height:54px;background-color:#1677ff"
/>
</div>
</div>
`;
exports[`renders components/flex/demo/combination.tsx correctly 1`] = `
<div
class="ant-card ant-card-bordered ant-card-hoverable"
style="width:620px"
>
<div
class="ant-card-body"
style="padding:0;overflow:hidden"
>
<div
class="ant-flex ant-flex-justify-space-between"
>
<img
alt="avatar"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
style="display:block;width:273px"
/>
<div
class="ant-flex ant-flex-align-flex-end ant-flex-justify-space-between ant-flex-vertical"
style="padding:32px"
>
<h3
class="ant-typography"
>
“antd is an enterprise-class UI design language and React UI library.”
</h3>
<a
class="ant-btn ant-btn-primary"
href="https://ant.design"
target="_blank"
>
<span>
Get Start
</span>
</a>
</div>
</div>
</div>
</div>
`;
exports[`renders components/flex/demo/debug.tsx correctly 1`] = `
Array [
<div
class="ant-flex ant-flex-align-stretch ant-flex-vertical"
>
<div
style="height:60px;background-color:#1677ffbf"
/>
<div
style="height:60px;background-color:#1677ff"
/>
<div
style="height:60px;background-color:#1677ffbf"
/>
<div
style="height:60px;background-color:#1677ff"
/>
</div>,
<div
class="ant-flex"
style="margin-top:20px"
>
<div
style="width:25%;height:40px;background-color:#1677ffbf"
/>
<div
style="width:25%;height:60px;background-color:#1677ff"
/>
<div
style="width:25%;height:40px;background-color:#1677ffbf"
/>
<div
style="width:25%;height:60px;background-color:#1677ff"
/>
</div>,
]
`;
exports[`renders components/flex/demo/gap.tsx correctly 1`] = `
<div
class="ant-flex ant-flex-align-stretch ant-flex-gap-middle ant-flex-vertical"
>
<div
class="ant-radio-group ant-radio-group-outline"
>
<label
class="ant-radio-wrapper ant-radio-wrapper-checked"
>
<span
class="ant-radio ant-wave-target ant-radio-checked"
>
<input
checked=""
class="ant-radio-input"
type="radio"
value="small"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
small
</span>
</label>
<label
class="ant-radio-wrapper"
>
<span
class="ant-radio ant-wave-target"
>
<input
class="ant-radio-input"
type="radio"
value="middle"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
middle
</span>
</label>
<label
class="ant-radio-wrapper"
>
<span
class="ant-radio ant-wave-target"
>
<input
class="ant-radio-input"
type="radio"
value="large"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
large
</span>
</label>
<label
class="ant-radio-wrapper"
>
<span
class="ant-radio ant-wave-target"
>
<input
class="ant-radio-input"
type="radio"
value="customize"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
customize
</span>
</label>
</div>
<div
class="ant-flex ant-flex-gap-small"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Primary
</span>
</button>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
Default
</span>
</button>
<button
class="ant-btn ant-btn-dashed"
type="button"
>
<span>
Dashed
</span>
</button>
<button
class="ant-btn ant-btn-link"
type="button"
>
<span>
Link
</span>
</button>
</div>
</div>
`;
exports[`renders components/flex/demo/wrap.tsx correctly 1`] = `
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Button
</span>
</button>
</div>
`;

View File

@ -0,0 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Flex rtl render component should be rendered correctly in RTL direction 1`] = `
<div
class="ant-flex ant-flex-rtl"
>
<div>
test1
</div>
<div>
test2
</div>
</div>
`;

View File

@ -0,0 +1,3 @@
import { extendTest } from '../../../tests/shared/demoTest';
extendTest('flex');

View File

@ -0,0 +1,3 @@
import demoTest from '../../../tests/shared/demoTest';
demoTest('flex');

View File

@ -0,0 +1,5 @@
import { imageDemoTest } from '../../../tests/shared/imageTest';
describe('flex image', () => {
imageDemoTest('flex');
});

View File

@ -0,0 +1,70 @@
import React from 'react';
import Flex from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { render } from '../../../tests/utils';
const FunCom = React.forwardRef<HTMLDivElement, { className?: string }>((props, ref) => (
<div className={props.className} ref={ref}>
test FC
</div>
));
class ClassCom extends React.PureComponent<{ className?: string }> {
render() {
return <div className={this.props.className}>test Class</div>;
}
}
describe('Flex', () => {
mountTest(() => (
<Flex>
<div>test1</div>
<div>test2</div>
</Flex>
));
rtlTest(() => (
<Flex>
<div>test1</div>
<div>test2</div>
</Flex>
));
it('Flex', () => {
const { container, rerender } = render(<Flex justify="center">test</Flex>);
expect(container.querySelector('.ant-flex')).toHaveStyle({ justifyContent: 'center' });
rerender(<Flex flex="0 1 auto">test</Flex>);
expect(container.querySelector('.ant-flex')).toHaveStyle({ flex: '0 1 auto' });
rerender(<Flex gap={100}>test</Flex>);
expect(container.querySelector('.ant-flex')).toHaveStyle({ gap: '100px' });
});
it('Component work', () => {
const testFcRef = React.createRef<HTMLDivElement>();
const testClsRef = React.createRef<ClassCom>();
const { container, rerender } = render(<Flex>test</Flex>);
expect(container.querySelector<HTMLDivElement>('.ant-flex')?.tagName).toBe('DIV');
rerender(<Flex component="span">test</Flex>);
expect(container.querySelector<HTMLSpanElement>('.ant-flex')?.tagName).toBe('SPAN');
rerender(<Flex component={(props) => <FunCom {...props} ref={testFcRef} />}>test</Flex>);
expect(container.querySelector<HTMLDivElement>('.ant-flex')?.textContent).toBe('test FC');
expect(testFcRef.current).toBeTruthy();
rerender(<Flex component={(props) => <ClassCom {...props} ref={testClsRef} />}>test</Flex>);
expect(container.querySelector<HTMLDivElement>('.ant-flex')?.textContent).toBe('test Class');
expect(testClsRef.current).toBeTruthy();
});
it('when vertical=true should stretch work', () => {
const { container, rerender } = render(<Flex vertical>test</Flex>);
expect(container.querySelector<HTMLDivElement>('.ant-flex')).toHaveClass(
'ant-flex-align-stretch',
);
rerender(
<Flex vertical align="center">
test
</Flex>,
);
expect(container.querySelector<HTMLDivElement>('.ant-flex')).toHaveClass(
'ant-flex-align-center',
);
});
});

View File

@ -0,0 +1,7 @@
## zh-CN
设置对齐方式。
## en-US
Set align.

View File

@ -0,0 +1,43 @@
import React from 'react';
import { Button, Flex, Segmented } from 'antd';
import type { FlexProps } from 'antd';
import type { SegmentedProps } from 'antd/es/segmented';
const boxStyle: React.CSSProperties = {
width: '100%',
height: 120,
borderRadius: 6,
border: '1px solid #40a9ff',
};
const justifyOptions = [
'flex-start',
'center',
'flex-end',
'space-between',
'space-around',
'space-evenly',
];
const alignOptions = ['flex-start', 'center', 'flex-end'];
const App: React.FC = () => {
const [justify, setJustify] = React.useState<FlexProps['justify']>(justifyOptions[0]);
const [alignItems, setAlignItems] = React.useState<FlexProps['align']>(alignOptions[0]);
return (
<Flex gap="middle" align="start" vertical>
<p>Select justify :</p>
<Segmented options={justifyOptions} onChange={setJustify as SegmentedProps['onChange']} />
<p>Select align :</p>
<Segmented options={alignOptions} onChange={setAlignItems as SegmentedProps['onChange']} />
<Flex style={boxStyle} justify={justify} align={alignItems}>
<Button type="primary">Primary</Button>
<Button type="primary">Primary</Button>
<Button type="primary">Primary</Button>
<Button type="primary">Primary</Button>
</Flex>
</Flex>
);
};
export default App;

View File

@ -0,0 +1,7 @@
## zh-CN
最简单的用法。
## en-US
The basic usage.

View File

@ -0,0 +1,27 @@
/* eslint-disable react/no-array-index-key */
import React from 'react';
import { Flex, Radio } from 'antd';
const baseStyle: React.CSSProperties = {
width: '25%',
height: 54,
};
const App: React.FC = () => {
const [value, setValue] = React.useState<string>('horizontal');
return (
<Flex gap="middle" vertical>
<Radio.Group value={value} onChange={(e) => setValue(e.target.value)}>
<Radio value="horizontal">horizontal</Radio>
<Radio value="vertical">vertical</Radio>
</Radio.Group>
<Flex vertical={value === 'vertical'}>
{Array.from({ length: 4 }).map((_, i) => (
<div key={i} style={{ ...baseStyle, backgroundColor: i % 2 ? '#1677ff' : '#1677ffbf' }} />
))}
</Flex>
</Flex>
);
};
export default App;

View File

@ -0,0 +1,7 @@
## zh-CN
嵌套使用,可以实现更复杂的布局。
## en-US
Nesting can achieve more complex layouts.

View File

@ -0,0 +1,33 @@
import React from 'react';
import { Button, Card, Flex, Typography } from 'antd';
const cardStyle: React.CSSProperties = {
width: 620,
};
const imgStyle: React.CSSProperties = {
display: 'block',
width: 273,
};
const App: React.FC = () => (
<Card hoverable style={cardStyle} bodyStyle={{ padding: 0, overflow: 'hidden' }}>
<Flex justify="space-between">
<img
alt="avatar"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
style={imgStyle}
/>
<Flex vertical align="flex-end" justify="space-between" style={{ padding: 32 }}>
<Typography.Title level={3}>
antd is an enterprise-class UI design language and React UI library.
</Typography.Title>
<Button type="primary" href="https://ant.design" target="_blank">
Get Start
</Button>
</Flex>
</Flex>
</Card>
);
export default App;

View File

@ -0,0 +1,7 @@
## zh-CN
调试专用。
## en-US
Use for debug.

View File

@ -0,0 +1,33 @@
/* eslint-disable react/no-array-index-key */
import React from 'react';
import { Flex } from 'antd';
const App: React.FC = () => (
<>
<Flex vertical>
{Array.from({ length: 4 }).map((_, i) => (
<div
key={i}
style={{
height: 60,
backgroundColor: i % 2 ? '#1677ff' : '#1677ffbf',
}}
/>
))}
</Flex>
<Flex style={{ marginTop: 20 }}>
{Array.from({ length: 4 }).map((_, i) => (
<div
key={i}
style={{
width: '25%',
height: i % 2 ? 60 : 40,
backgroundColor: i % 2 ? '#1677ff' : '#1677ffbf',
}}
/>
))}
</Flex>
</>
);
export default App;

View File

@ -0,0 +1,7 @@
## zh-CN
使用 `gap` 设置元素之间的间距,预设了 `small`、`middle`、`large` 三种尺寸,也可以自定义间距。
## en-US
Set the `gap` between elements, which has three preset sizes: `small`, `middle`, `large`, You can also customize the gap size.

View File

@ -0,0 +1,28 @@
import React from 'react';
import { Button, Flex, Radio, Slider } from 'antd';
import type { SizeType } from 'antd/es/config-provider/SizeContext';
const App: React.FC = () => {
const [gapSize, setGapSize] = React.useState<SizeType | 'customize'>('small');
const [customGapSize, setCustomGapSize] = React.useState<number>(0);
return (
<Flex gap="middle" vertical>
<Radio.Group value={gapSize} onChange={(e) => setGapSize(e.target.value)}>
{['small', 'middle', 'large', 'customize'].map((size) => (
<Radio key={size} value={size}>
{size}
</Radio>
))}
</Radio.Group>
{gapSize === 'customize' && <Slider value={customGapSize} onChange={setCustomGapSize} />}
<Flex gap={gapSize !== 'customize' ? gapSize : customGapSize}>
<Button type="primary">Primary</Button>
<Button>Default</Button>
<Button type="dashed">Dashed</Button>
<Button type="link">Link</Button>
</Flex>
</Flex>
);
};
export default App;

View File

@ -0,0 +1,7 @@
## zh-CN
自动换行。
## en-US
Auto wrap line.

View File

@ -0,0 +1,14 @@
import React from 'react';
import { Button, Flex } from 'antd';
const Demo: React.FC = () => (
<Flex wrap="wrap" gap="small">
{Array.from({ length: 24 }, (_, i) => (
<Button key={i} type="primary">
Button
</Button>
))}
</Flex>
);
export default Demo;

View File

@ -0,0 +1,45 @@
---
category: Components
group: Layout
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
---
Flex. Available since `5.10.0`.
## When To Use
- Good for setting spacing between elements.
- Suitable for setting various horizontal and vertical alignments.
## Examples
<!-- prettier-ignore -->
<code src="./demo/basic.tsx">Basic</code>
<code src="./demo/align.tsx">align</code>
<code src="./demo/gap.tsx">gap</code>
<code src="./demo/wrap.tsx">Wrap</code>
<code src="./demo/combination.tsx">combination</code>
<code src="./demo/debug.tsx" debug>debug</code>
## API
> This component is available since `antd@5.10.0`. The default behavior of Flex in horizontal mode is to align upward, In vertical mode, aligns the stretch, You can adjust this via properties.
Common props ref[Common props](/docs/react/common-props)
| Property | Description | type | Default | Version |
| --- | --- | --- | --- | --- |
| vertical | Is direction of the flex vertical, use `flex-direction: column` | boolean | `false` | |
| wrap | Set whether the element is displayed in a single line or in multiple lines | reference [flex-wrap](https://developer.mozilla.org/en-US/docs/Web/CSS/flex-wrap) | nowrap | |
| justify | Sets the alignment of elements in the direction of the main axis | reference [justify-content](https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content) | normal | |
| align | Sets the alignment of elements in the direction of the cross axis | reference [align-items](https://developer.mozilla.org/en-US/docs/Web/CSS/align-items) | normal | |
| flex | flex CSS shorthand properties | reference [flex](https://developer.mozilla.org/en-US/docs/Web/CSS/flex) | normal | |
| gap | Sets the gap between grids | `small` \| `middle` \| `large` \| string \| number | - | |
| component | custom element type | React.ComponentType | `div` | |
## Design Token
<ComponentTokenTable component="Flex"></ComponentTokenTable>

78
components/flex/index.tsx Normal file
View File

@ -0,0 +1,78 @@
import React from 'react';
import classNames from 'classnames';
import omit from 'rc-util/lib/omit';
import { isPresetSize } from '../_util/gapSize';
import { ConfigContext } from '../config-provider';
import type { ConfigConsumerProps } from '../config-provider';
import type { FlexProps } from './interface';
import useStyle from './style';
import createFlexClassNames from './utils';
const Flex = React.forwardRef<HTMLElement, FlexProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
rootClassName,
className,
style,
flex,
gap,
children,
vertical = false,
component: Component = 'div',
...othersProps
} = props;
const {
flex: ctxFlex,
direction: ctxDirection,
getPrefixCls,
} = React.useContext<ConfigConsumerProps>(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(
<Component
ref={ref}
className={mergedCls}
style={mergedStyle}
{...omit(othersProps, ['justify', 'wrap', 'align'])}
>
{children}
</Component>,
);
});
if (process.env.NODE_ENV !== 'production') {
Flex.displayName = 'Flex';
}
export default Flex;

View File

@ -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` 版本开始提供该组件。
## 何时使用
- 适合设置元素之间的间距。
- 适合设置各种水平、垂直对齐方式。
## 代码演示
<!-- prettier-ignore -->
<code src="./demo/basic.tsx">基本布局</code>
<code src="./demo/align.tsx">对齐方式</code>
<code src="./demo/gap.tsx">设置间隙</code>
<code src="./demo/wrap.tsx">自动换行</code>
<code src="./demo/combination.tsx">组合使用</code>
<code src="./demo/debug.tsx" debug>调试专用</code>
## 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
<ComponentTokenTable component="Flex"></ComponentTokenTable>

View File

@ -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<P = AnyObject> extends React.HTMLAttributes<HTMLElement> {
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<P> | string;
}

View File

@ -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<FlexToken> = (token) => {
const { componentCls } = token;
return {
[componentCls]: {
display: 'flex',
'&-vertical': {
flexDirection: 'column',
},
'&-rtl': {
direction: 'rtl',
},
'&:empty': {
display: 'none',
},
},
};
};
const genFlexGapStyle: GenerateStyle<FlexToken> = (token) => {
const { componentCls } = token;
return {
[componentCls]: {
'&-gap-small': {
gap: token.flexGapSM,
},
'&-gap-middle': {
gap: token.flexGap,
},
'&-gap-large': {
gap: token.flexGapLG,
},
},
};
};
const genFlexWrapStyle: GenerateStyle<FlexToken> = (token) => {
const { componentCls } = token;
const wrapStyle: CSSInterpolation = {};
flexWrapValues.forEach((value) => {
wrapStyle[`${componentCls}-wrap-${value}`] = { flexWrap: value };
});
return wrapStyle;
};
const genAlignItemsStyle: GenerateStyle<FlexToken> = (token) => {
const { componentCls } = token;
const alignStyle: CSSInterpolation = {};
alignItemsValues.forEach((value) => {
alignStyle[`${componentCls}-align-${value}`] = { alignItems: value };
});
return alignStyle;
};
const genJustifyContentStyle: GenerateStyle<FlexToken> = (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<FlexToken>(token, {
flexGapSM: token.paddingXS,
flexGap: token.padding,
flexGapLG: token.paddingLG,
});
return [
genFlexStyle(flexToken),
genFlexGapStyle(flexToken),
genFlexWrapStyle(flexToken),
genAlignItemsStyle(flexToken),
genJustifyContentStyle(flexToken),
];
});

68
components/flex/utils.ts Normal file
View File

@ -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<PropertyKey, boolean> = {};
flexWrapValues.forEach((cssKey) => {
wrapCls[`${prefixCls}-wrap-${cssKey}`] = props.wrap === cssKey;
});
return wrapCls;
};
const genClsAlign = (prefixCls: string, props: FlexProps) => {
const alignCls: Record<PropertyKey, boolean> = {};
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<PropertyKey, boolean> = {};
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;

View File

@ -13,10 +13,10 @@ describe('Grid.Server', () => {
</Row>, </Row>,
); );
expect((container.querySelector('.ant-row') as HTMLElement)?.style.marginLeft).toBe('-4px'); expect(container.querySelector<HTMLElement>('.ant-row')?.style.marginLeft).toBe('-4px');
expect((container.querySelector('.ant-row') as HTMLElement)?.style.marginRight).toBe('-4px'); expect(container.querySelector<HTMLElement>('.ant-row')?.style.marginRight).toBe('-4px');
expect((container.querySelector('.ant-row') as HTMLElement)?.style.marginTop).toBe(''); expect(container.querySelector<HTMLElement>('.ant-row')?.style.marginTop).toBe('');
expect((container.querySelector('.ant-row') as HTMLElement)?.style.marginBottom).toBe(''); expect(container.querySelector<HTMLElement>('.ant-row')?.style.marginBottom).toBe('');
expect((container.querySelector('.ant-col') as HTMLElement)?.style.paddingLeft).toBe('4px'); expect((container.querySelector('.ant-col') as HTMLElement)?.style.paddingLeft).toBe('4px');
expect((container.querySelector('.ant-col') as HTMLElement)?.style.paddingRight).toBe('4px'); expect((container.querySelector('.ant-col') as HTMLElement)?.style.paddingRight).toBe('4px');

View File

@ -54,6 +54,8 @@ export type {
} from './dropdown'; } from './dropdown';
export { default as Empty } from './empty'; export { default as Empty } from './empty';
export type { EmptyProps } 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 { default as FloatButton } from './float-button';
export type { FloatButtonGroupProps, FloatButtonProps } from './float-button/interface'; export type { FloatButtonGroupProps, FloatButtonProps } from './float-button/interface';
export { default as Form } from './form'; export { default as Form } from './form';

View File

@ -20,10 +20,11 @@ import type { ComponentToken as DividerComponentToken } from '../../divider/styl
import type { ComponentToken as DrawerComponentToken } from '../../drawer/style'; import type { ComponentToken as DrawerComponentToken } from '../../drawer/style';
import type { ComponentToken as DropdownComponentToken } from '../../dropdown/style'; import type { ComponentToken as DropdownComponentToken } from '../../dropdown/style';
import type { ComponentToken as EmptyComponentToken } from '../../empty/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 FloatButtonComponentToken } from '../../float-button/style';
import type { ComponentToken as ImageComponentToken } from '../../image/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 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 LayoutComponentToken } from '../../layout/style';
import type { ComponentToken as ListComponentToken } from '../../list/style'; import type { ComponentToken as ListComponentToken } from '../../list/style';
import type { ComponentToken as MentionsComponentToken } from '../../mentions/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 TooltipComponentToken } from '../../tooltip/style';
import type { ComponentToken as TourComponentToken } from '../../tour/style'; import type { ComponentToken as TourComponentToken } from '../../tour/style';
import type { ComponentToken as TransferComponentToken } from '../../transfer/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 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 TypographyComponentToken } from '../../typography/style';
import type { ComponentToken as UploadComponentToken } from '../../upload/style'; import type { ComponentToken as UploadComponentToken } from '../../upload/style';
import type { ComponentToken as FormComponentToken } from '../../form/style'; import type { ComponentToken as FormComponentToken } from '../../form/style';
@ -82,6 +83,7 @@ export interface ComponentTokenMap {
Drawer?: DrawerComponentToken; Drawer?: DrawerComponentToken;
Dropdown?: DropdownComponentToken; Dropdown?: DropdownComponentToken;
Empty?: EmptyComponentToken; Empty?: EmptyComponentToken;
Flex?: FlexComponentToken;
FloatButton?: FloatButtonComponentToken; FloatButton?: FloatButtonComponentToken;
Form?: FormComponentToken; Form?: FormComponentToken;
Grid?: {}; Grid?: {};

View File

@ -27,6 +27,7 @@ exports[`antd dist files exports modules correctly 1`] = `
"Drawer", "Drawer",
"Dropdown", "Dropdown",
"Empty", "Empty",
"Flex",
"FloatButton", "FloatButton",
"Form", "Form",
"Grid", "Grid",