feat: Grid support row vertical gutter (#18979)

* row vertical gutter

* run test
This commit is contained in:
sosohime 2019-09-24 21:44:20 +08:00 committed by 偏右
parent a5efb30b83
commit a2edbc326f
9 changed files with 309 additions and 37 deletions

View File

@ -1,7 +1,7 @@
import createContext, { Context } from '@ant-design/create-react-context';
export interface RowContextState {
gutter?: number;
gutter?: [number, number];
}
const RowContext: Context<RowContextState> = createContext({});

View File

@ -414,6 +414,51 @@ exports[`renders ./components/grid/demo/gutter.md correctly 1`] = `
</div>
</div>
</div>
<div
class="ant-row"
style="margin-top:-10px;margin-bottom:-10px"
>
<div
class="ant-col ant-col-6 gutter-row"
style="padding-top:10px;padding-bottom:10px"
>
<div
class="gutter-box"
>
col-6
</div>
</div>
<div
class="ant-col ant-col-6 gutter-row"
style="padding-top:10px;padding-bottom:10px"
>
<div
class="gutter-box"
>
col-6
</div>
</div>
<div
class="ant-col ant-col-6 gutter-row"
style="padding-top:10px;padding-bottom:10px"
>
<div
class="gutter-box"
>
col-6
</div>
</div>
<div
class="ant-col ant-col-6 gutter-row"
style="padding-top:10px;padding-bottom:10px"
>
<div
class="gutter-box"
>
col-6
</div>
</div>
</div>
</div>
`;
@ -467,7 +512,105 @@ exports[`renders ./components/grid/demo/playground.md correctly 1`] = `
<span
style="margin-right:6px"
>
Gutter (px):
Horizontal Gutter (px):
</span>
<div
style="width:50%"
>
<div
class="ant-slider ant-slider-with-marks"
>
<div
class="ant-slider-rail"
/>
<div
class="ant-slider-track"
style="left:0%;right:auto;width:20%"
/>
<div
class="ant-slider-step"
>
<span
class="ant-slider-dot ant-slider-dot-active"
style="left:0%"
/>
<span
class="ant-slider-dot ant-slider-dot-active"
style="left:20%"
/>
<span
class="ant-slider-dot"
style="left:40%"
/>
<span
class="ant-slider-dot"
style="left:60%"
/>
<span
class="ant-slider-dot"
style="left:80%"
/>
<span
class="ant-slider-dot"
style="left:100%"
/>
</div>
<div
aria-disabled="false"
aria-valuemax="5"
aria-valuemin="0"
aria-valuenow="1"
class="ant-slider-handle"
role="slider"
style="left:20%;right:auto;transform:translateX(-50%)"
tabindex="0"
/>
<div
class="ant-slider-mark"
>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:0%"
>
8
</span>
<span
class="ant-slider-mark-text ant-slider-mark-text-active"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:20%"
>
16
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:40%"
>
24
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:60%"
>
32
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:80%"
>
40
</span>
<span
class="ant-slider-mark-text"
style="transform:translateX(-50%);-ms-transform:translateX(-50%);left:100%"
>
48
</span>
</div>
</div>
</div>
<span
style="margin-right:6px"
>
Vertical Gutter (px):
</span>
<div
style="width:50%"
@ -663,11 +806,11 @@ exports[`renders ./components/grid/demo/playground.md correctly 1`] = `
</div>
<div
class="ant-row"
style="margin-left:-8px;margin-right:-8px"
style="margin-left:-8px;margin-right:-8px;margin-top:-8px;margin-bottom:-8px"
>
<div
class="ant-col ant-col-6"
style="padding-left:8px;padding-right:8px"
style="padding-left:8px;padding-right:8px;padding-top:8px;padding-bottom:8px"
>
<div>
Column
@ -675,7 +818,7 @@ exports[`renders ./components/grid/demo/playground.md correctly 1`] = `
</div>
<div
class="ant-col ant-col-6"
style="padding-left:8px;padding-right:8px"
style="padding-left:8px;padding-right:8px;padding-top:8px;padding-bottom:8px"
>
<div>
Column
@ -683,7 +826,7 @@ exports[`renders ./components/grid/demo/playground.md correctly 1`] = `
</div>
<div
class="ant-col ant-col-6"
style="padding-left:8px;padding-right:8px"
style="padding-left:8px;padding-right:8px;padding-top:8px;padding-bottom:8px"
>
<div>
Column
@ -691,7 +834,44 @@ exports[`renders ./components/grid/demo/playground.md correctly 1`] = `
</div>
<div
class="ant-col ant-col-6"
style="padding-left:8px;padding-right:8px"
style="padding-left:8px;padding-right:8px;padding-top:8px;padding-bottom:8px"
>
<div>
Column
</div>
</div>
</div>
<div
class="ant-row"
style="margin-left:-8px;margin-right:-8px;margin-top:-8px;margin-bottom:-8px"
>
<div
class="ant-col ant-col-6"
style="padding-left:8px;padding-right:8px;padding-top:8px;padding-bottom:8px"
>
<div>
Column
</div>
</div>
<div
class="ant-col ant-col-6"
style="padding-left:8px;padding-right:8px;padding-top:8px;padding-bottom:8px"
>
<div>
Column
</div>
</div>
<div
class="ant-col ant-col-6"
style="padding-left:8px;padding-right:8px;padding-top:8px;padding-bottom:8px"
>
<div>
Column
</div>
</div>
<div
class="ant-col ant-col-6"
style="padding-left:8px;padding-right:8px;padding-top:8px;padding-bottom:8px"
>
<div>
Column
@ -699,7 +879,15 @@ exports[`renders ./components/grid/demo/playground.md correctly 1`] = `
</div>
</div>
<pre>
&lt;Row gutter={16}&gt;
&lt;Row gutter={[16, 16]}&gt;
&lt;Col span={6} /&gt;
&lt;Col span={6} /&gt;
&lt;Col span={6} /&gt;
&lt;Col span={6} /&gt;
&lt;/Row&gt;
</pre>
<pre>
&lt;Row gutter={[16, 16]}&gt;
&lt;Col span={6} /&gt;
&lt;Col span={6} /&gt;
&lt;Col span={6} /&gt;

View File

@ -37,7 +37,7 @@ describe('Grid', () => {
it('when typeof getGutter is object', () => {
const wrapper = mount(<Row gutter={{ xs: 8, sm: 16, md: 24 }} />);
expect(wrapper.instance().getGutter()).toBe(8);
expect(wrapper.instance().getGutter()).toEqual([8, 0]);
wrapper.unmount();
});
@ -75,8 +75,18 @@ describe('Grid', () => {
.update()
.find('div')
.prop('style'),
).toEqual(undefined);
).toEqual({});
wrapper.unmount();
expect(enquire.unregister).toHaveBeenCalled();
});
it('should work currect when gutter is array', () => {
const wrapper = mount(<Row gutter={[16, 20]} />);
expect(wrapper.find('div').prop('style')).toEqual({
marginLeft: -8,
marginRight: -8,
marginTop: -10,
marginBottom: -10,
});
});
});

View File

@ -102,13 +102,25 @@ export default class Col extends React.Component<ColProps, {}> {
<RowContext.Consumer>
{({ gutter }) => {
let { style } = others;
if (gutter! > 0) {
if (gutter) {
style = {
paddingLeft: gutter! / 2,
paddingRight: gutter! / 2,
...(gutter[0]! > 0
? {
paddingLeft: gutter[0]! / 2,
paddingRight: gutter[0]! / 2,
}
: {}),
...(gutter[1]! > 0
? {
paddingTop: gutter[1]! / 2,
paddingBottom: gutter[1]! / 2,
}
: {}),
...style,
};
}
return (
<div {...others} style={style} className={classes}>
{children}

View File

@ -11,12 +11,16 @@ title:
如果要支持响应式,可以写成 `{ xs: 8, sm: 16, md: 24, lg: 32 }`
如果需要垂直间距,可以写成数组形式 `[水平间距, 垂直间距]` `[16, { xs: 8, sm: 16, md: 24, lg: 32 }]`
## en-US
You can use the `gutter` property of `Row` as grid spacing, we recommend set it to `(16 + 8n) px`. (`n` stands for natural number.)
You can set it to a object like `{ xs: 8, sm: 16, md: 24, lg: 32 }` for responsive design.
You can use a array to set vertical spacing, `[horizontal, vertical]` `[16, { xs: 8, sm: 16, md: 24, lg: 32 }]`.
```jsx
import { Row, Col } from 'antd';
@ -36,6 +40,20 @@ ReactDOM.render(
<div className="gutter-box">col-6</div>
</Col>
</Row>
<Row gutter={[{ xs: 8, sm: 16, md: 24, lg: 32 }, 20]}>
<Col className="gutter-row" span={6}>
<div className="gutter-box">col-6</div>
</Col>
<Col className="gutter-row" span={6}>
<div className="gutter-box">col-6</div>
</Col>
<Col className="gutter-row" span={6}>
<div className="gutter-box">col-6</div>
</Col>
<Col className="gutter-row" span={6}>
<div className="gutter-box">col-6</div>
</Col>
</Row>
</div>,
mountNode,
);

View File

@ -19,17 +19,23 @@ import { Row, Col, Slider } from 'antd';
class App extends React.Component {
gutters = {};
vgutters = {};
colCounts = {};
constructor() {
super();
this.state = {
gutterKey: 1,
vgutterKey: 1,
colCountKey: 2,
};
[8, 16, 24, 32, 40, 48].forEach((value, i) => {
this.gutters[i] = value;
});
[8, 16, 24, 32, 40, 48].forEach((value, i) => {
this.vgutters[i] = value;
});
[2, 3, 4, 6, 8, 12].forEach((value, i) => {
this.colCounts[i] = value;
});
@ -39,12 +45,16 @@ class App extends React.Component {
this.setState({ gutterKey });
};
onVGutterChange = vgutterKey => {
this.setState({ vgutterKey });
};
onColCountChange = colCountKey => {
this.setState({ colCountKey });
};
render() {
const { gutterKey, colCountKey } = this.state;
const { gutterKey, vgutterKey, colCountKey } = this.state;
const cols = [];
const colCount = this.colCounts[colCountKey];
let colCode = '';
@ -59,7 +69,7 @@ class App extends React.Component {
return (
<div>
<div style={{ marginBottom: 16 }}>
<span style={{ marginRight: 6 }}>Gutter (px): </span>
<span style={{ marginRight: 6 }}>Horizontal Gutter (px): </span>
<div style={{ width: '50%' }}>
<Slider
min={0}
@ -70,6 +80,17 @@ class App extends React.Component {
step={null}
/>
</div>
<span style={{ marginRight: 6 }}>Vertical Gutter (px): </span>
<div style={{ width: '50%' }}>
<Slider
min={0}
max={Object.keys(this.vgutters).length - 1}
value={vgutterKey}
onChange={this.onVGutterChange}
marks={this.vgutters}
step={null}
/>
</div>
<span style={{ marginRight: 6 }}>Column Count:</span>
<div style={{ width: '50%' }}>
<Slider
@ -82,8 +103,10 @@ class App extends React.Component {
/>
</div>
</div>
<Row gutter={this.gutters[gutterKey]}>{cols}</Row>
<pre>{`<Row gutter={${this.gutters[gutterKey]}}>\n${colCode}</Row>`}</pre>
<Row gutter={[this.gutters[gutterKey], this.vgutters[vgutterKey]]}>{cols}</Row>
<Row gutter={[this.gutters[gutterKey], this.vgutters[vgutterKey]]}>{cols}</Row>
<pre>{`<Row gutter={[${this.gutters[gutterKey]}, ${this.vgutters[vgutterKey]}]}>\n${colCode}</Row>`}</pre>
<pre>{`<Row gutter={[${this.gutters[gutterKey]}, ${this.vgutters[vgutterKey]}]}>\n${colCode}</Row>`}</pre>
</div>
);
}

View File

@ -91,7 +91,7 @@ If the Ant Design grid layout component does not meet your needs, you can use th
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| align | the vertical alignment of the flex layout: `top` `middle` `bottom` | string | `top` | |
| gutter | spacing between grids, could be a number or a object like `{ xs: 8, sm: 16, md: 24}` | number/object | 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 of the flex layout: `start` `end` `center` `space-around` `space-between` | string | `start` | |
| type | layout mode, optional `flex`, [browser support](http://caniuse.com/#search=flex) | string | | |

View File

@ -90,7 +90,7 @@ Ant Design 的布局组件若不能满足你的需求,你也可以直接使用
| 成员 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| align | flex 布局下的垂直对齐方式:`top` `middle` `bottom` | string | `top` | |
| gutter | 栅格间隔,可以写成像素值或支持响应式的对象写法 `{ xs: 8, sm: 16, md: 24}` | number/object | 0 | |
| gutter | 栅格间隔,可以写成像素值或支持响应式的对象写法来设置水平间隔 `{ xs: 8, sm: 16, md: 24}`,或者使用数组形式同时设置`[水平间距, 垂直间距]` | number/object/array | 0 | |
| justify | flex 布局下的水平排列方式:`start` `end` `center` `space-around` `space-between` | string | `start` | |
| type | 布局模式,可选 `flex`[现代浏览器](http://caniuse.com/#search=flex) 下有效 | string | | |

View File

@ -12,8 +12,10 @@ import ResponsiveObserve, {
const RowAligns = tuple('top', 'middle', 'bottom', 'stretch');
const RowJustify = tuple('start', 'end', 'center', 'space-around', 'space-between');
export type Gutter = number | Partial<Record<Breakpoint, number>>;
export interface RowProps extends React.HTMLAttributes<HTMLDivElement> {
gutter?: number | Partial<Record<Breakpoint, number>>;
gutter?: Gutter | [Gutter, Gutter];
type?: 'flex';
align?: (typeof RowAligns)[number];
justify?: (typeof RowJustify)[number];
@ -35,7 +37,7 @@ export default class Row extends React.Component<RowProps, RowState> {
justify: PropTypes.oneOf(RowJustify),
className: PropTypes.string,
children: PropTypes.node,
gutter: PropTypes.oneOfType([PropTypes.object, PropTypes.number]),
gutter: PropTypes.oneOfType([PropTypes.object, PropTypes.number, PropTypes.array]),
prefixCls: PropTypes.string,
};
@ -47,7 +49,11 @@ export default class Row extends React.Component<RowProps, RowState> {
componentDidMount() {
this.token = ResponsiveObserve.subscribe(screens => {
if (typeof this.props.gutter === 'object') {
const { gutter } = this.props;
if (
typeof gutter === 'object' ||
(Array.isArray(gutter) && (typeof gutter[0] === 'object' || typeof gutter[1] === 'object'))
) {
this.setState({ screens });
}
});
@ -57,17 +63,24 @@ export default class Row extends React.Component<RowProps, RowState> {
ResponsiveObserve.unsubscribe(this.token);
}
getGutter(): number | undefined {
const { gutter } = this.props;
if (typeof gutter === 'object') {
for (let i = 0; i < responsiveArray.length; i++) {
const breakpoint: Breakpoint = responsiveArray[i];
if (this.state.screens[breakpoint] && gutter[breakpoint] !== undefined) {
return gutter[breakpoint];
getGutter(): [number, number] {
const gutter: [number, number] = [0, 0];
const { gutter: gutter_setting } = this.props;
(Array.isArray(gutter_setting) ? gutter_setting : [gutter_setting, 0]).forEach((g, index) => {
if (typeof g === 'object') {
for (let i = 0; i < responsiveArray.length; i++) {
const breakpoint: Breakpoint = responsiveArray[i];
if (this.state.screens[breakpoint] && g[breakpoint] !== undefined) {
gutter[index] = g[breakpoint] as number;
}
}
} else {
gutter[index] = g as number;
}
}
return gutter as number;
});
return gutter;
}
renderRow = ({ getPrefixCls }: ConfigConsumerProps) => {
@ -92,16 +105,24 @@ export default class Row extends React.Component<RowProps, RowState> {
},
className,
);
const rowStyle =
gutter! > 0
const rowStyle = {
...(gutter[0]! > 0
? {
marginLeft: gutter! / -2,
marginRight: gutter! / -2,
...style,
marginLeft: gutter[0]! / -2,
marginRight: gutter[0]! / -2,
}
: style;
: {}),
...(gutter[1]! > 0
? {
marginTop: gutter[1]! / -2,
marginBottom: gutter[1]! / -2,
}
: {}),
...style,
};
const otherProps = { ...others };
delete otherProps.gutter;
return (
<RowContext.Provider value={{ gutter }}>
<div {...otherProps} className={classes} style={rowStyle}>