diff --git a/components/grid/__tests__/index.test.tsx b/components/grid/__tests__/index.test.tsx
index 466f33e9d492..cbe5d55b7876 100644
--- a/components/grid/__tests__/index.test.tsx
+++ b/components/grid/__tests__/index.test.tsx
@@ -143,4 +143,44 @@ describe('Grid', () => {
xxl: false,
});
});
+
+ it('should align by responsive align prop', () => {
+ const matchMediaSpy = jest.spyOn(window, 'matchMedia');
+ matchMediaSpy.mockImplementation(
+ query =>
+ ({
+ addListener: (cb: (e: { matches: boolean }) => void) => {
+ cb({ matches: query === '(max-width: 575px)' });
+ },
+ removeListener: jest.fn(),
+ matches: query === '(max-width: 575px)',
+ } as any),
+ );
+ const { container } = render(
);
+ expect(container.innerHTML).toContain('ant-row-middle');
+ const { container: container2 } = render(
);
+ expect(container2.innerHTML).toContain('ant-row-middle');
+ const { container: container3 } = render(
);
+ expect(container3.innerHTML).not.toContain('ant-row-middle');
+ });
+
+ it('should justify by responsive justify prop', () => {
+ const matchMediaSpy = jest.spyOn(window, 'matchMedia');
+ matchMediaSpy.mockImplementation(
+ query =>
+ ({
+ addListener: (cb: (e: { matches: boolean }) => void) => {
+ cb({ matches: query === '(max-width: 575px)' });
+ },
+ removeListener: jest.fn(),
+ matches: query === '(max-width: 575px)',
+ } as any),
+ );
+ const { container } = render(
);
+ expect(container.innerHTML).toContain('ant-row-center');
+ const { container: container2 } = render(
);
+ expect(container2.innerHTML).toContain('ant-row-center');
+ const { container: container3 } = render(
);
+ expect(container3.innerHTML).not.toContain('ant-row-center');
+ });
});
diff --git a/components/grid/index.en-US.md b/components/grid/index.en-US.md
index 6387aa9b4aaf..ef285894530a 100644
--- a/components/grid/index.en-US.md
+++ b/components/grid/index.en-US.md
@@ -44,9 +44,9 @@ If the Ant Design grid layout component does not meet your needs, you can use th
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
-| align | Vertical alignment | `top` \| `middle` \| `bottom` | `top` | |
+| align | Vertical alignment | `top` \| `middle` \| `bottom` \| `stretch` \| `{[key in 'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'xxl']: 'top' \| 'middle' \| 'bottom' \| 'stretch'}` | `top` | object: 4.24.0 |
| gutter | Spacing between grids, could be a number or a object like { xs: 8, sm: 16, md: 24}. Or you can use array to make horizontal and vertical spacing work at the same time `[horizontal, vertical]` | number \| object \| array | 0 | |
-| justify | Horizontal arrangement | `start` \| `end` \| `center` \| `space-around` \| `space-between` \| `space-evenly` | `start` | |
+| justify | Horizontal arrangement | `start` \| `end` \| `center` \| `space-around` \| `space-between` \| `space-evenly` \| `{[key in 'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'xxl']: 'start' \| 'end' \| 'center' \| 'space-around' \| 'space-between' \| 'space-evenly'}` | `start` | object: 4.24.0 |
| wrap | Auto wrap line | boolean | true | 4.8.0 |
### Col
diff --git a/components/grid/index.zh-CN.md b/components/grid/index.zh-CN.md
index e8ed5b41ac04..6fbe36bcad6b 100644
--- a/components/grid/index.zh-CN.md
+++ b/components/grid/index.zh-CN.md
@@ -43,9 +43,9 @@ Ant Design 的布局组件若不能满足你的需求,你也可以直接使用
| 成员 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
-| align | 垂直对齐方式 | `top` \| `middle` \| `bottom` | `top` | |
+| align | 垂直对齐方式 | `top` \| `middle` \| `bottom` \| `stretch` \| `{[key in 'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'xxl']: 'top' \| 'middle' \| 'bottom' \| 'stretch'}` | `top` | object: 4.24.0 |
| gutter | 栅格间隔,可以写成像素值或支持响应式的对象写法来设置水平间隔 { xs: 8, sm: 16, md: 24}。或者使用数组形式同时设置 `[水平间距, 垂直间距]` | number \| object \| array | 0 | |
-| justify | 水平排列方式 | `start` \| `end` \| `center` \| `space-around` \| `space-between` \| `space-evenly` | `start` | |
+| justify | 水平排列方式 | `start` \| `end` \| `center` \| `space-around` \| `space-between` \| `space-evenly` \| `{[key in 'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'xxl']: 'start' \| 'end' \| 'center' \| 'space-around' \| 'space-between' \| 'space-evenly'}` | `start` | object: 4.24.0 |
| wrap | 是否自动换行 | boolean | true | 4.8.0 |
### Col
diff --git a/components/grid/row.tsx b/components/grid/row.tsx
index 1457dfcfd0ff..ff32a141fb77 100644
--- a/components/grid/row.tsx
+++ b/components/grid/row.tsx
@@ -10,16 +10,50 @@ import RowContext from './RowContext';
const RowAligns = tuple('top', 'middle', 'bottom', 'stretch');
const RowJustify = tuple('start', 'end', 'center', 'space-around', 'space-between', 'space-evenly');
+type Responsive = 'xxl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs';
+type ResponsiveLike = {
+ [key in Responsive]?: T;
+};
+
type Gap = number | undefined;
export type Gutter = number | undefined | Partial>;
+
+type ResponsiveAligns = ResponsiveLike;
+type ResponsiveJustify = ResponsiveLike;
export interface RowProps extends React.HTMLAttributes {
gutter?: Gutter | [Gutter, Gutter];
- align?: typeof RowAligns[number];
- justify?: typeof RowJustify[number];
+ align?: typeof RowAligns[number] | ResponsiveAligns;
+ justify?: typeof RowJustify[number] | ResponsiveJustify;
prefixCls?: string;
wrap?: boolean;
}
+function useMergePropByScreen(oriProp: RowProps['align'] | RowProps['justify'], screen: ScreenMap) {
+ const [prop, setProp] = React.useState(typeof oriProp === 'string' ? oriProp : '');
+
+ const clacMergeAlignOrJustify = () => {
+ if (typeof oriProp !== 'object') {
+ return;
+ }
+ for (let i = 0; i < responsiveArray.length; i++) {
+ const breakpoint: Breakpoint = responsiveArray[i];
+ // if do not match, do nothing
+ if (!screen[breakpoint]) continue;
+ const curVal = oriProp[breakpoint];
+ if (curVal !== undefined) {
+ setProp(curVal);
+ return;
+ }
+ }
+ };
+
+ React.useEffect(() => {
+ clacMergeAlignOrJustify();
+ }, [JSON.stringify(oriProp), screen]);
+
+ return prop;
+}
+
const Row = React.forwardRef((props, ref) => {
const {
prefixCls: customizePrefixCls,
@@ -43,6 +77,20 @@ const Row = React.forwardRef((props, ref) => {
xl: true,
xxl: true,
});
+ // to save screens info when responsiveObserve callback had been call
+ const [curScreens, setCurScreens] = React.useState({
+ xs: false,
+ sm: false,
+ md: false,
+ lg: false,
+ xl: false,
+ xxl: false,
+ });
+
+ // ================================== calc reponsive data ==================================
+ const mergeAlign = useMergePropByScreen(align, curScreens);
+
+ const mergeJustify = useMergePropByScreen(justify, curScreens);
const supportFlexGap = useFlexGapSupport();
@@ -51,6 +99,7 @@ const Row = React.forwardRef((props, ref) => {
// ================================== Effect ==================================
React.useEffect(() => {
const token = ResponsiveObserve.subscribe(screen => {
+ setCurScreens(screen);
const currentGutter = gutterRef.current || 0;
if (
(!Array.isArray(currentGutter) && typeof currentGutter === 'object') ||
@@ -89,8 +138,8 @@ const Row = React.forwardRef((props, ref) => {
prefixCls,
{
[`${prefixCls}-no-wrap`]: wrap === false,
- [`${prefixCls}-${justify}`]: justify,
- [`${prefixCls}-${align}`]: align,
+ [`${prefixCls}-${mergeJustify}`]: mergeJustify,
+ [`${prefixCls}-${mergeAlign}`]: mergeAlign,
[`${prefixCls}-rtl`]: direction === 'rtl',
},
className,