Merge branch 'feature-2.8'

This commit is contained in:
Wei Zhu 2017-03-05 21:34:57 +08:00
commit 67bbae9ea8
111 changed files with 3631 additions and 526 deletions

View File

@ -20,6 +20,8 @@ For displaying anchor hyperlinks on page and jumping between them.
| offsetTop | Pixels to offset from top when calculating position of scroll | number | 0 |
| offsetBottom | Pixels to offset from bottom when calculating position of scroll | number | - |
| bounds | Bounding distance of anchor area | number | 5(px) |
| affix | Fixed mode of Anchor | boolean | false |
| showInkInFixed | Whether show ink-balls in Fixed mode | boolean | false |
### Link Props

View File

@ -14,6 +14,7 @@ export interface AnchorProps {
className?: string;
style?: React.CSSProperties;
affix?: boolean;
showInkInFixed?: boolean;
}
export default class Anchor extends React.Component<AnchorProps, any> {
@ -22,6 +23,7 @@ export default class Anchor extends React.Component<AnchorProps, any> {
static defaultProps = {
prefixCls: 'ant-anchor',
affix: true,
showInkInFixed: false,
};
static childContextTypes = {
@ -91,6 +93,7 @@ export default class Anchor extends React.Component<AnchorProps, any> {
}
renderAnchorLink = (child) => {
const { href } = child.props;
if (child.type.__ANT_ANCHOR_LINK && href) {
this.anchorHelper.addLink(href);
@ -98,7 +101,7 @@ export default class Anchor extends React.Component<AnchorProps, any> {
onClick: this.clickAnchorLink,
prefixCls: this.props.prefixCls,
bounds: this.props.bounds,
affix: this.props.affix,
affix: this.props.affix || this.props.showInkInFixed,
offsetTop: this.props.offsetTop,
});
}
@ -106,7 +109,7 @@ export default class Anchor extends React.Component<AnchorProps, any> {
}
render() {
const { prefixCls, offsetTop, style, className = '', affix } = this.props;
const { prefixCls, offsetTop, style, className = '', affix, showInkInFixed } = this.props;
const { activeAnchor, animated } = this.state;
const inkClass = classNames({
[`${prefixCls}-ink-ball`]: true,
@ -119,7 +122,7 @@ export default class Anchor extends React.Component<AnchorProps, any> {
}, className);
const anchorClass = classNames(prefixCls, {
'fixed': !affix,
'fixed': !affix && !showInkInFixed,
});
const anchorContent = (

View File

@ -21,6 +21,8 @@ title: Anchor
| offsetTop | 距离窗口顶部达到指定偏移量后触发 | number | |
| offsetBottom | 距离窗口底部达到指定偏移量后触发 | number | |
| bounds | 锚点区域边界 | number | 5(px) |
| affix | 固定模式 | boolean | false |
| showInkInFixed | 固定模式是否显示小圆点 | boolean | false |
### Link Props

View File

@ -1,139 +1,153 @@
exports[`test renders ./components/badge/demo/basic.md correctly 1`] = `
<span
class="ant-badge"
title="5">
<a
class="head-example"
href="#" />
<sup
class="ant-scroll-number ant-badge-count"
data-show="true">
<span
class="ant-scroll-number-only"
style="transition:none;-webkit-transform:translateY(-1500%);transform:translateY(-1500%);">
<p
class="">
0
</p>
<p
class="">
1
</p>
<p
class="">
2
</p>
<p
class="">
3
</p>
<p
class="">
4
</p>
<p
class="">
5
</p>
<p
class="">
6
</p>
<p
class="">
7
</p>
<p
class="">
8
</p>
<p
class="">
9
</p>
<p
class="">
0
</p>
<p
class="">
1
</p>
<p
class="">
2
</p>
<p
class="">
3
</p>
<p
class="">
4
</p>
<p
class="current">
5
</p>
<p
class="">
6
</p>
<p
class="">
7
</p>
<p
class="">
8
</p>
<p
class="">
9
</p>
<p
class="">
0
</p>
<p
class="">
1
</p>
<p
class="">
2
</p>
<p
class="">
3
</p>
<p
class="">
4
</p>
<p
class="">
5
</p>
<p
class="">
6
</p>
<p
class="">
7
</p>
<p
class="">
8
</p>
<p
class="">
9
</p>
</span>
</sup>
</span>
<div>
<span
class="ant-badge"
title="5">
<a
class="head-example"
href="#" />
<sup
class="ant-scroll-number ant-badge-count"
data-show="true">
<span
class="ant-scroll-number-only"
style="transition:none;-webkit-transform:translateY(-1500%);transform:translateY(-1500%);">
<p
class="">
0
</p>
<p
class="">
1
</p>
<p
class="">
2
</p>
<p
class="">
3
</p>
<p
class="">
4
</p>
<p
class="">
5
</p>
<p
class="">
6
</p>
<p
class="">
7
</p>
<p
class="">
8
</p>
<p
class="">
9
</p>
<p
class="">
0
</p>
<p
class="">
1
</p>
<p
class="">
2
</p>
<p
class="">
3
</p>
<p
class="">
4
</p>
<p
class="current">
5
</p>
<p
class="">
6
</p>
<p
class="">
7
</p>
<p
class="">
8
</p>
<p
class="">
9
</p>
<p
class="">
0
</p>
<p
class="">
1
</p>
<p
class="">
2
</p>
<p
class="">
3
</p>
<p
class="">
4
</p>
<p
class="">
5
</p>
<p
class="">
6
</p>
<p
class="">
7
</p>
<p
class="">
8
</p>
<p
class="">
9
</p>
</span>
</sup>
</span>
<span
class="ant-badge"
title="0">
<a
class="head-example"
href="#" />
<sup
class="ant-scroll-number ant-badge-count"
data-show="true">
0
</sup>
</span>
</div>
`;
exports[`test renders ./components/badge/demo/change.md correctly 1`] = `

View File

@ -7,19 +7,24 @@ title:
## zh-CN
简单的徽章展示。
简单的徽章展示,当 `count``0` 时,默认不显示,但是可以使用 `showZero` 修改为显示
## en-US
Simplest Usage.
Simplest Usage. Badge will be hidden when `count` is `0`, but we can use `showZero` to show it.
````jsx
import { Badge } from 'antd';
ReactDOM.render(
<Badge count={5}>
<a href="#" className="head-example" />
</Badge>
<div>
<Badge count={5}>
<a href="#" className="head-example" />
</Badge>
<Badge count={0} showZero>
<a href="#" className="head-example" />
</Badge>
</div>
, mountNode);
````

View File

@ -27,6 +27,7 @@ Badge normally appears in proximity to notification or head picture with eye-cat
|----------------|-------------------------|------------|---------|
| count | Number to show in badge | number | |
| overflowCount | Max count to show | number | 99 |
| showZero | Whether to show badge when `count` is zero | boolean | false |
| dot | Whether to show red dot without number | boolean | false |
| status | Set Badge as a status dot | Enum{ 'success', 'processing, 'default', 'error', 'warning' } | '' |
| text | If `status` is set, `text` is to set the text of status dot | string | '' |

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { PropTypes } from 'react';
import Animate from 'rc-animate';
import ScrollNumber from './ScrollNumber';
import classNames from 'classnames';
@ -7,6 +7,7 @@ import warning from '../_util/warning';
export interface BadgeProps {
/** Number to show in badge */
count: number | string;
showZero?: boolean;
/** Max count to show */
overflowCount?: number;
/** whether to show red dot without number */
@ -22,21 +23,35 @@ export default class Badge extends React.Component<BadgeProps, any> {
static defaultProps = {
prefixCls: 'ant-badge',
count: null,
showZero: false,
dot: false,
overflowCount: 99,
};
static propTypes = {
count: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number,
count: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),
dot: React.PropTypes.bool,
overflowCount: React.PropTypes.number,
showZero: PropTypes.bool,
dot: PropTypes.bool,
overflowCount: PropTypes.number,
};
render() {
const { count, prefixCls, overflowCount, className, style, children, dot, status, text, ...restProps } = this.props;
const {
count,
showZero,
prefixCls,
overflowCount,
className,
style,
children,
dot,
status,
text,
...restProps,
} = this.props;
const isDot = dot || status;
let displayCount = count > overflowCount ? `${overflowCount}+` : count;
// dot mode don't need count
@ -44,8 +59,9 @@ export default class Badge extends React.Component<BadgeProps, any> {
displayCount = '';
}
// null undefined "" "0" 0
const hidden = (!displayCount || displayCount === '0') && !isDot;
const isZero = displayCount === '0' || displayCount === 0;
const isEmpty = displayCount === null || displayCount === undefined || displayCount === '';
const hidden = (isEmpty || (isZero && !showZero)) && !isDot;
const scrollNumberCls = classNames({
[`${prefixCls}-dot`]: isDot,
[`${prefixCls}-count`]: !isDot,

View File

@ -24,10 +24,11 @@ title: Badge
<Badge count={5} />
```
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|----------------|----------------------------------|------------|---------|--------|
| count | 展示的数字,大于 overflowCount 时显示为 `${overflowCount}+`,为 0 时隐藏 | number | | |
| overflowCount | 展示封顶的数字值 | number | | 99 |
| dot | 不展示数字,只有一个小红点 | boolean | | false |
| status | 设置 Badge 为状态点 | Enum | 'success'、'processing'、'default'、'error'、'warning' | '' |
| text | 在设置了 `status` 的前提下有效,设置状态点的文本 | string | | '' |
| 参数 | 说明 | 类型 | 默认值 |
|----------------|----------------------------------|------------|---------|
| count | 展示的数字,大于 overflowCount 时显示为 `${overflowCount}+`,为 0 时隐藏 | number | |
| overflowCount | 展示封顶的数字值 | number | 99 |
| showZero | 当数值为 0 时,是否展示 Badge | boolean | false |
| dot | 不展示数字,只有一个小红点 | boolean | false |
| status | 设置 Badge 为状态点 | Enum{ 'success', 'processing, 'default', 'error', 'warning' } | '' |
| text | 在设置了 `status` 的前提下有效,设置状态点的文本 | string | '' |

View File

@ -2598,3 +2598,799 @@ exports[`test renders ./components/calendar/demo/notice-calendar.md correctly 1`
</div>
</div>
`;
exports[`test renders ./components/calendar/demo/select.md correctly 1`] = `
<div>
<div
class="ant-alert ant-alert-info ant-alert-no-icon"
data-show="true">
<span
class="ant-alert-message">
You selected date: 2017-01-25
</span>
<span
class="ant-alert-description" />
</div>
<div
class=" ant-fullcalendar-fullscreen">
<div
class="ant-fullcalendar-header">
<div
class="ant-fullcalendar-year-select ant-select ant-select-enabled">
<div
aria-autocomplete="list"
aria-expanded="false"
aria-haspopup="true"
class="ant-select-selection
ant-select-selection--single"
role="combobox"
tabindex="0">
<div
class="ant-select-selection__rendered">
<div
class="ant-select-selection-selected-value"
style="display:block;opacity:1;"
title="2017年">
2017年
</div>
</div>
<span
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none;"
unselectable="unselectable">
<b />
</span>
</div>
</div>
<div
class="ant-fullcalendar-month-select ant-select ant-select-enabled">
<div
aria-autocomplete="list"
aria-expanded="false"
aria-haspopup="true"
class="ant-select-selection
ant-select-selection--single"
role="combobox"
tabindex="0">
<div
class="ant-select-selection__rendered">
<div
class="ant-select-selection-selected-value"
style="display:block;opacity:1;"
title="1月">
1月
</div>
</div>
<span
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none;"
unselectable="unselectable">
<b />
</span>
</div>
</div>
<div
class="ant-radio-group ant-radio-group-default">
<label
class="ant-radio-button-wrapper ant-radio-button-wrapper-checked">
<span
class="ant-radio-button ant-radio-button-checked">
<input
checked=""
class="ant-radio-button-input"
type="radio" />
<span
class="ant-radio-button-inner" />
</span>
<span>
</span>
</label>
<label
class="ant-radio-button-wrapper">
<span
class="ant-radio-button">
<input
class="ant-radio-button-input"
type="radio" />
<span
class="ant-radio-button-inner" />
</span>
<span>
</span>
</label>
</div>
</div>
<div
class="ant-fullcalendar ant-fullcalendar-full ant-fullcalendar-fullscreen"
tabindex="0">
<div
class="ant-fullcalendar-calendar-body">
<table
cellspacing="0"
class="ant-fullcalendar-table"
role="grid">
<thead>
<tr
role="row">
<th
class="ant-fullcalendar-column-header"
role="columnheader"
title="周一">
<span
class="ant-fullcalendar-column-header-inner">
</span>
</th>
<th
class="ant-fullcalendar-column-header"
role="columnheader"
title="周二">
<span
class="ant-fullcalendar-column-header-inner">
</span>
</th>
<th
class="ant-fullcalendar-column-header"
role="columnheader"
title="周三">
<span
class="ant-fullcalendar-column-header-inner">
</span>
</th>
<th
class="ant-fullcalendar-column-header"
role="columnheader"
title="周四">
<span
class="ant-fullcalendar-column-header-inner">
</span>
</th>
<th
class="ant-fullcalendar-column-header"
role="columnheader"
title="周五">
<span
class="ant-fullcalendar-column-header-inner">
</span>
</th>
<th
class="ant-fullcalendar-column-header"
role="columnheader"
title="周六">
<span
class="ant-fullcalendar-column-header-inner">
</span>
</th>
<th
class="ant-fullcalendar-column-header"
role="columnheader"
title="周日">
<span
class="ant-fullcalendar-column-header-inner">
</span>
</th>
</tr>
</thead>
<tbody
class="ant-fullcalendar-tbody">
<tr
role="row">
<td
class="ant-fullcalendar-cell ant-fullcalendar-last-month-cell"
role="gridcell"
title="2016-12-26">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
26
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell ant-fullcalendar-last-month-cell"
role="gridcell"
title="2016-12-27">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
27
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell ant-fullcalendar-last-month-cell"
role="gridcell"
title="2016-12-28">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
28
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell ant-fullcalendar-last-month-cell"
role="gridcell"
title="2016-12-29">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
29
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell ant-fullcalendar-last-month-cell"
role="gridcell"
title="2016-12-30">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
30
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell ant-fullcalendar-last-month-cell"
role="gridcell"
title="2016-12-31">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
31
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-1">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
01
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
</tr>
<tr
role="row">
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-2">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
02
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-3">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
03
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-4">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
04
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-5">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
05
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-6">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
06
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-7">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
07
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-8">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
08
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
</tr>
<tr
role="row">
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-9">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
09
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-10">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
10
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-11">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
11
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-12">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
12
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-13">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
13
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-14">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
14
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-15">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
15
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
</tr>
<tr
role="row">
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-16">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
16
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-17">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
17
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-18">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
18
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-19">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
19
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-20">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
20
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-21">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
21
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-22">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
22
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
</tr>
<tr
role="row">
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-23">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
23
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-24">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
24
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell ant-fullcalendar-selected-day"
role="gridcell"
title="2017-1-25">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
25
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-26">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
26
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-27">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
27
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-28">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
28
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-29">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
29
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
</tr>
<tr
role="row">
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-30">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
30
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell"
role="gridcell"
title="2017-1-31">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
31
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell ant-fullcalendar-next-month-btn-day"
role="gridcell"
title="2017-2-1">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
01
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell ant-fullcalendar-next-month-btn-day"
role="gridcell"
title="2017-2-2">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
02
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell ant-fullcalendar-next-month-btn-day"
role="gridcell"
title="2017-2-3">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
03
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell ant-fullcalendar-next-month-btn-day"
role="gridcell"
title="2017-2-4">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
04
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
<td
class="ant-fullcalendar-cell ant-fullcalendar-next-month-btn-day"
role="gridcell"
title="2017-2-5">
<div
class="ant-fullcalendar-date">
<div
class="ant-fullcalendar-value">
05
</div>
<div
class="ant-fullcalendar-content" />
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
`;

View File

@ -0,0 +1,17 @@
import React from 'react';
import Moment from 'moment';
import { mount } from 'enzyme';
import Calendar from '..';
describe('Calendar', () => {
it('Calendar should be selectable', () => {
const onSelect = jest.fn();
const wrapper = mount(
<Calendar onSelect={onSelect} />
);
wrapper.find('.ant-fullcalendar-cell').at(0).simulate('click');
expect(onSelect).toBeCalledWith(expect.anything());
const value = onSelect.mock.calls[0][0];
expect(Moment.isMoment(value)).toBe(true);
});
});

View File

@ -0,0 +1,46 @@
---
order: 3
title:
zh-CN: 选择功能
en-US: Selectable Calendar
---
## zh-CN
一个通用的日历面板,支持年/月切换。
## en-US
A basic calendar component with Year/Month switch.
````jsx
import { Calendar, Alert } from 'antd';
import moment from 'moment';
class App extends React.Component {
state = {
value: moment('2017-01-25'),
selectedValue: moment('2017-01-25'),
}
onSelect = (value) => {
this.setState({
value,
selectedValue: value,
});
}
onPanelChange = (value) => {
this.setState({ value });
}
render() {
const { value, selectedValue } = this.state;
return (
<div>
<Alert message={`You selected date: ${selectedValue && selectedValue.format('YYYY-MM-DD')}`} />
<Calendar value={value} onSelect={this.onSelect} onPanelChange={this.onPanelChange} />
</div>
);
}
}
ReactDOM.render(<App />, mountNode);
````

View File

@ -26,6 +26,7 @@ moment.locale('zh-cn');
dateCellRender={dateCellRender}
monthCellRender={monthCellRender}
onPanelChange={onPanelChange}
onSelect={onSelect}
/>
```
@ -35,7 +36,10 @@ moment.locale('zh-cn');
| defaultValue | set default date | [moment](http://momentjs.com/) | default date |
| mode | can be set to month or year | string | month |
| fullscreen | to set whether full-screen display | boolean | true |
| dateCellRender | to set the way of renderer the date cell | function(date: moment): ReactNode | - |
| monthCellRender | to set the way of renderer the month cell | function(date: moment): ReactNode | - |
| dateCellRender | to set the way of renderer the date cell, the returned content will be appended to the cell | function(date: moment): ReactNode | - |
| monthCellRender | to set the way of renderer the month cell, the returned content will be appended to the cell | function(date: moment): ReactNode | - |
| dateFullCellRender | to set the way of renderer the date cell,the returned content will override the cell | function(date: moment): ReactNode | - |
| monthFullCellRender | to set the way of renderer the month cell,the returned content will override the cell | function(date: moment): ReactNode | - |
| locale | set locale | object | [default](https://github.com/ant-design/ant-design/blob/master/components/date-picker/locale/example.json) |
| onPanelChange| the callback when panel change | function(date: moment, mode: string) | - |
| onPanelChange| callback when panel change | function(date: moment, mode: string) | - |
| onSelect | callback when select date | function(date: moment | - |

View File

@ -33,9 +33,12 @@ export interface CalendarProps {
fullscreen?: boolean;
dateCellRender?: (date: moment.Moment) => React.ReactNode;
monthCellRender?: (date: moment.Moment) => React.ReactNode;
dateFullCellRender?: (date: moment.Moment) => React.ReactNode;
monthFullCellRender?: (date: moment.Moment) => React.ReactNode;
locale?: any;
style?: React.CSSProperties;
onPanelChange?: (date?: moment.Moment, mode?: CalendarMode) => void;
onSelect?: (date?: moment.Moment) => void;
}
export interface CalendarState {
@ -49,11 +52,15 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
fullscreen: true,
prefixCls: PREFIX_CLS,
mode: 'month',
onSelect: noop,
onPanelChange: noop,
};
static propTypes = {
monthCellRender: PropTypes.func,
dateCellRender: PropTypes.func,
monthFullCellRender: PropTypes.func,
dateFullCellRender: PropTypes.func,
fullscreen: PropTypes.bool,
locale: PropTypes.object,
prefixCls: PropTypes.string,
@ -61,6 +68,7 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
style: PropTypes.object,
onPanelChange: PropTypes.func,
value: PropTypes.object,
onSelect: PropTypes.func,
};
static contextTypes = {
@ -123,13 +131,16 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
);
}
setValue = (value) => {
if (!('value' in this.props) && this.state.value !== value) {
setValue = (value, way: 'select' | 'changePanel') => {
if (!('value' in this.props)) {
this.setState({ value });
}
const onPanelChange = this.props.onPanelChange;
if (onPanelChange) {
onPanelChange(value, this.state.mode);
if (way === 'select') {
if (this.props.onSelect) {
this.props.onSelect(value);
}
} else if (way === 'changePanel') {
this.onPanelChange(value, this.state.mode);
}
}
@ -137,13 +148,29 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
const mode = (type === 'date') ? 'month' : 'year';
if (this.state.mode !== mode) {
this.setState({ mode });
const onPanelChange = this.props.onPanelChange;
if (onPanelChange) {
onPanelChange(this.state.value, mode);
}
this.onPanelChange(this.state.value, mode);
}
}
onHeaderValueChange = (value) => {
this.setValue(value, 'changePanel');
}
onHeaderTypeChange = (type) => {
this.setType(type);
}
onPanelChange(value, mode) {
const { onPanelChange } = this.props;
if (onPanelChange) {
onPanelChange(value, mode);
}
}
onSelect = (value) => {
this.setValue(value, 'select');
}
render() {
const { state, props, context } = this;
const { value, mode } = state;
@ -151,7 +178,7 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
if (value && localeCode) {
value.locale(localeCode);
}
const { prefixCls, style, className, fullscreen } = props;
const { prefixCls, style, className, fullscreen, dateFullCellRender, monthFullCellRender } = props;
const type = (mode === 'year') ? 'month' : 'date';
const locale = getComponentLocale(props, context, 'Calendar', () => require('./locale/zh_CN'));
@ -160,6 +187,9 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
cls += (` ${prefixCls}-fullscreen`);
}
const monthCellRender = monthFullCellRender || this.monthCellRender;
const dateCellRender = dateFullCellRender || this.dateCellRender;
return (
<div className={cls} style={style}>
<Header
@ -168,8 +198,8 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
value={value}
locale={locale.lang}
prefixCls={prefixCls}
onTypeChange={this.setType}
onValueChange={this.setValue}
onTypeChange={this.onHeaderTypeChange}
onValueChange={this.onHeaderValueChange}
/>
<FullCalendar
{...props}
@ -179,8 +209,9 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
prefixCls={prefixCls}
showHeader={false}
value={value}
monthCellRender={this.monthCellRender}
dateCellRender={this.dateCellRender}
monthCellRender={monthCellRender}
dateCellRender={dateCellRender}
onSelect={this.onSelect}
/>
</div>
);

View File

@ -28,6 +28,7 @@ moment.locale('zh-cn');
dateCellRender={dateCellRender}
monthCellRender={monthCellRender}
onPanelChange={onPanelChange}
onSelect={onSelect}
/>
```
@ -37,7 +38,10 @@ moment.locale('zh-cn');
| defaultValue | 默认展示的日期 | [moment](http://momentjs.com/) | 默认日期 |
| mode | 初始模式,`month/year` | string | month |
| fullscreen | 是否全屏显示 | boolean | true |
| dateCellRender | 自定义渲染日期单元格| function(date: moment): ReactNode | 无 |
| monthCellRender | 自定义渲染月单元格 | function(date: moment): ReactNode | 无 |
| dateCellRender | 自定义渲染日期单元格,返回内容会被追加到单元格| function(date: moment): ReactNode | 无 |
| monthCellRender | 自定义渲染月单元格,返回内容会被追加到单元格 | function(date: moment): ReactNode | 无 |
| dateFullCellRender | 自定义渲染日期单元格,返回内容覆盖单元格| function(date: moment): ReactNode | 无 |
| monthFullCellRender | 自定义渲染月单元格,返回内容覆盖单元格 | function(date: moment): ReactNode | 无 |
| locale | 国际化配置 | object | [默认配置](https://github.com/ant-design/ant-design/blob/master/components/date-picker/locale/example.json) |
| onPanelChange| 日期面板变化回调 | function(date: moment, mode: string) | 无 |
| onSelect | 点击选择日期回调 | function(date: moment | 无 |

View File

@ -0,0 +1,2 @@
import ca_ES from '../../date-picker/locale/ca_ES';
export default ca_ES;

View File

@ -0,0 +1,2 @@
import cs_CZ from '../../date-picker/locale/cs_CZ';
export default cs_CZ;

View File

@ -0,0 +1,2 @@
import ko_KR from '../../date-picker/locale/ko_KR';
export default ko_KR;

View File

@ -0,0 +1,2 @@
import nl_NL from '../../date-picker/locale/nl_NL';
export default nl_NL;

View File

@ -84,23 +84,30 @@
&-month,
&-date {
text-align: center;
transition: all .3s;
}
&-value {
display: block;
margin: 0 auto;
color: @text-color;
border-radius: 4px;
border-radius: @border-radius-base;
width: 22px;
height: 22px;
padding: 0;
background: transparent;
line-height: 22px;
transition: all .3s;
&:hover {
background: @primary-1;
cursor: pointer;
}
&:active {
background: @primary-color;
color: #fff;
}
}
&-month-panel-cell &-value {
@ -109,30 +116,35 @@
&-today &-value,
&-month-panel-current-cell &-value {
box-shadow: 0 0 0 1px @primary-color;
}
&-selected-day &-value,
&-month-panel-selected-cell &-value {
background: @primary-color;
color: #fff;
}
&-disabled-cell &-value {
cursor: not-allowed;
color: #bcbcbc;
background: #f3f3f3;
color: @disabled-color;
background: @background-color-base;
border-radius: 0;
width: auto;
&:hover {
background: #f3f3f3;
background: @background-color-base;
}
}
&-disabled-cell-first-of-row &-value {
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
border-top-left-radius: @border-radius-base;
border-bottom-left-radius: @border-radius-base;
}
&-disabled-cell-last-of-row &-value {
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
border-top-right-radius: @border-radius-base;
border-bottom-right-radius: @border-radius-base;
}
&-last-month-cell &-value,
@ -166,8 +178,8 @@
margin-left: 16px;
}
label.@{ant-prefix}-radio-button {
height: 28px;
line-height: 26px;
height: @input-height-base;
line-height: @input-height-base - 2px;
}
}
@ -180,12 +192,16 @@
height: 116px;
padding: 4px 8px;
border-top: 2px solid @border-color-split;
transition: background 0.3s ease;
transition: background .3s;
&:hover {
background: @primary-1;
cursor: pointer;
}
&:active {
background: @primary-2;
}
}
&-fullscreen &-column-header {
@ -207,12 +223,24 @@
&-fullscreen &-month-panel-current-cell &-month,
&-fullscreen &-today &-date {
border-top-color: @primary-color;
background-color: @primary-1;
color: @primary-color;
}
&-fullscreen &-month-panel-current-cell &-value {
background: transparent;
}
&-fullscreen &-month-panel-current-cell &-value,
&-fullscreen &-today &-value {
box-shadow: none;
}
&-fullscreen &-month-panel-selected-cell &-month,
&-fullscreen &-selected-day &-date {
background: @primary-1;
}
&-fullscreen &-month-panel-selected-cell &-value,
&-fullscreen &-selected-day &-value {
color: @primary-color;
}
&-fullscreen &-last-month-cell &-date,
&-fullscreen &-next-month-btn-day &-date {
color: @disabled-color;

View File

@ -0,0 +1,17 @@
import CalendarLocale from 'rc-calendar/lib/locale/ca_ES';
import TimePickerLocale from '../../time-picker/locale/ca_ES';
import assign from 'object-assign';
// 统一合并为完整的 Locale
const locale = {
lang: assign({
placeholder: 'Seleccionar data',
rangePlaceholder: ['Data inicial', 'Data final'],
}, CalendarLocale),
timePickerLocale: assign({}, TimePickerLocale),
};
// All settings at:
// https://github.com/ant-design/ant-design/blob/master/components/date-picker/locale/example.json
export default locale;

View File

@ -0,0 +1,17 @@
import CalendarLocale from 'rc-calendar/lib/locale/cs_CZ';
import TimePickerLocale from '../../time-picker/locale/cs_CZ';
import assign from 'object-assign';
// 统一合并为完整的 Locale
const locale = {
lang: assign({
placeholder: 'Vybrat datum',
rangePlaceholder: ['Od', 'Do'],
}, CalendarLocale),
timePickerLocale: assign({}, TimePickerLocale),
};
// All settings at:
// https://github.com/ant-design/ant-design/blob/master/components/date-picker/locale/example.json
export default locale;

View File

@ -0,0 +1,17 @@
import CalendarLocale from 'rc-calendar/lib/locale/ko_KR';
import TimePickerLocale from '../../time-picker/locale/ko_KR';
import assign from 'object-assign';
// 统一合并为完整的 Locale
const locale = {
lang: assign({
placeholder: '날짜 선택',
rangePlaceholder: ['시작일', '종료일'],
}, CalendarLocale),
timePickerLocale: assign({}, TimePickerLocale),
};
// All settings at:
// https://github.com/ant-design/ant-design/blob/master/components/date-picker/locale/example.json
export default locale;

View File

@ -0,0 +1,17 @@
import CalendarLocale from 'rc-calendar/lib/locale/nl_NL';
import TimePickerLocale from '../../time-picker/locale/nl_NL';
import assign from 'object-assign';
// 统一合并为完整的 Locale
const locale = {
lang: assign({
placeholder: 'Selecteer datum',
rangePlaceholder: ['Begin datum', 'Eind datum'],
}, CalendarLocale),
timePickerLocale: assign({}, TimePickerLocale),
};
// All settings at:
// https://github.com/ant-design/ant-design/issues/424
export default locale;

View File

@ -17,6 +17,7 @@ export interface FormCreateOption {
}
export interface FormProps {
layout?: 'horizontal' | 'inline' | 'vertical';
horizontal?: boolean;
inline?: boolean;
vertical?: boolean;
@ -87,6 +88,7 @@ export interface ComponentDecorator {
export default class Form extends React.Component<FormProps, any> {
static defaultProps = {
prefixCls: 'ant-form',
layout: 'horizontal',
hideRequiredMark: false,
onSubmit(e) {
e.preventDefault();
@ -94,13 +96,11 @@ export default class Form extends React.Component<FormProps, any> {
};
static propTypes = {
prefixCls: React.PropTypes.string,
vertical: React.PropTypes.bool,
horizontal: React.PropTypes.bool,
inline: React.PropTypes.bool,
children: React.PropTypes.any,
onSubmit: React.PropTypes.func,
hideRequiredMark: React.PropTypes.bool,
prefixCls: PropTypes.string,
layout: PropTypes.oneOf(['horizontal', 'inline', 'vertical']),
children: PropTypes.any,
onSubmit: PropTypes.func,
hideRequiredMark: PropTypes.bool,
};
static childContextTypes = {
@ -163,23 +163,33 @@ export default class Form extends React.Component<FormProps, any> {
}
getChildContext() {
const { layout, vertical } = this.props;
return {
vertical: this.props.vertical,
vertical: layout === 'vertical' || vertical,
};
}
render() {
const { prefixCls, hideRequiredMark, className = '', inline, horizontal, vertical } = this.props;
const {
prefixCls, hideRequiredMark, className = '', layout,
// @deprecated
inline, horizontal, vertical,
} = this.props;
warning(
!inline && !horizontal && !vertical,
'`Form[inline|horizontal|vertical]` is deprecated, please use `Form[layout]` instead.'
);
const formClassName = classNames(prefixCls, {
[`${prefixCls}-horizontal`]: horizontal,
[`${prefixCls}-vertical`]: vertical,
[`${prefixCls}-inline`]: inline,
[`${prefixCls}-horizontal`]: (!inline && !vertical && layout === 'horizontal') || horizontal,
[`${prefixCls}-vertical`]: layout === 'vertical' || vertical,
[`${prefixCls}-inline`]: layout === 'inline' || inline,
[`${prefixCls}-hide-required-mark`]: hideRequiredMark,
}, className);
const formProps = omit(this.props, [
'prefixCls',
'className',
'layout',
'inline',
'horizontal',
'vertical',

View File

@ -1,7 +1,7 @@
exports[`test renders ./components/form/demo/advanced-search.md correctly 1`] = `
<div>
<form
class="ant-form ant-advanced-search-form">
class="ant-form ant-form-horizontal ant-advanced-search-form">
<div
class="ant-row"
style="margin-left:-20px;margin-right:-20px;">
@ -218,7 +218,7 @@ exports[`test renders ./components/form/demo/advanced-search.md correctly 1`] =
exports[`test renders ./components/form/demo/coordinated.md correctly 1`] = `
<form
class="ant-form">
class="ant-form ant-form-horizontal">
<div
class="ant-row ant-form-item">
<div
@ -291,7 +291,7 @@ exports[`test renders ./components/form/demo/coordinated.md correctly 1`] = `
<div
class="ant-row ant-form-item">
<div
class="ant-col-8 ant-col-offset-4 ant-form-item-control-wrapper">
class="ant-form-item-control-wrapper ant-col-xs-8 ant-col-xs-offset-0 ant-col-sm-8 ant-col-sm-offset-4">
<div
class="ant-form-item-control ">
<button
@ -384,11 +384,11 @@ exports[`test renders ./components/form/demo/customized-form-controls.md correct
exports[`test renders ./components/form/demo/dynamic-form-item.md correctly 1`] = `
<form
class="ant-form">
class="ant-form ant-form-horizontal">
<div
class="ant-row ant-form-item">
<div
class="ant-col-20 ant-col-offset-4 ant-form-item-control-wrapper">
class="ant-form-item-control-wrapper ant-col-xs-20 ant-col-xs-offset-0 ant-col-sm-20 ant-col-sm-offset-4">
<div
class="ant-form-item-control ">
<button
@ -407,7 +407,7 @@ exports[`test renders ./components/form/demo/dynamic-form-item.md correctly 1`]
<div
class="ant-row ant-form-item">
<div
class="ant-col-20 ant-col-offset-4 ant-form-item-control-wrapper">
class="ant-form-item-control-wrapper ant-col-xs-20 ant-col-xs-offset-0 ant-col-sm-20 ant-col-sm-offset-4">
<div
class="ant-form-item-control ">
<button
@ -657,7 +657,7 @@ exports[`test renders ./components/form/demo/layout.md correctly 1`] = `
<div
class="ant-row ant-form-item">
<div
class="ant-col-14 ant-col-offset-4 ant-form-item-control-wrapper">
class="ant-form-item-control-wrapper ant-col-xs-14 ant-col-xs-offset-0 ant-col-sm-14 ant-col-sm-offset-4">
<div
class="ant-form-item-control ">
<button
@ -676,7 +676,7 @@ exports[`test renders ./components/form/demo/layout.md correctly 1`] = `
exports[`test renders ./components/form/demo/normal-login.md correctly 1`] = `
<form
class="ant-form login-form">
class="ant-form ant-form-horizontal login-form">
<div
class="ant-row ant-form-item">
<div
@ -769,7 +769,7 @@ exports[`test renders ./components/form/demo/normal-login.md correctly 1`] = `
exports[`test renders ./components/form/demo/register.md correctly 1`] = `
<form
class="ant-form">
class="ant-form ant-form-horizontal">
<div
class="ant-row ant-form-item">
<div
@ -1016,7 +1016,7 @@ exports[`test renders ./components/form/demo/register.md correctly 1`] = `
class="ant-row ant-form-item"
style="margin-bottom:8px;">
<div
class="ant-col-14 ant-col-offset-6 ant-form-item-control-wrapper">
class="ant-form-item-control-wrapper ant-col-xs-14 ant-col-xs-offset-0 ant-col-sm-14 ant-col-sm-offset-6">
<div
class="ant-form-item-control ">
<label
@ -1042,7 +1042,7 @@ exports[`test renders ./components/form/demo/register.md correctly 1`] = `
<div
class="ant-row ant-form-item">
<div
class="ant-col-14 ant-col-offset-6 ant-form-item-control-wrapper">
class="ant-form-item-control-wrapper ant-col-xs-14 ant-col-xs-offset-0 ant-col-sm-14 ant-col-sm-offset-6">
<div
class="ant-form-item-control ">
<button
@ -1060,7 +1060,7 @@ exports[`test renders ./components/form/demo/register.md correctly 1`] = `
exports[`test renders ./components/form/demo/time-related-controls.md correctly 1`] = `
<form
class="ant-form">
class="ant-form ant-form-horizontal">
<div
class="ant-row ant-form-item">
<div
@ -1265,7 +1265,7 @@ exports[`test renders ./components/form/demo/time-related-controls.md correctly
<div
class="ant-row ant-form-item">
<div
class="ant-col-16 ant-col-offset-8 ant-form-item-control-wrapper">
class="ant-form-item-control-wrapper ant-col-xs-16 ant-col-xs-offset-0 ant-col-sm-16 ant-col-sm-offset-8">
<div
class="ant-form-item-control ">
<button
@ -1283,7 +1283,7 @@ exports[`test renders ./components/form/demo/time-related-controls.md correctly
exports[`test renders ./components/form/demo/validate-other.md correctly 1`] = `
<form
class="ant-form">
class="ant-form ant-form-horizontal">
<div
class="ant-row ant-form-item">
<div
@ -1720,7 +1720,7 @@ exports[`test renders ./components/form/demo/validate-other.md correctly 1`] = `
<div
class="ant-row ant-form-item">
<div
class="ant-col-12 ant-col-offset-6 ant-form-item-control-wrapper">
class="ant-form-item-control-wrapper ant-col-xs-12 ant-col-xs-offset-0 ant-col-sm-12 ant-col-sm-offset-6">
<div
class="ant-form-item-control ">
<button
@ -1738,7 +1738,7 @@ exports[`test renders ./components/form/demo/validate-other.md correctly 1`] = `
exports[`test renders ./components/form/demo/validate-static.md correctly 1`] = `
<form
class="ant-form">
class="ant-form ant-form-horizontal">
<div
class="ant-row ant-form-item ant-form-item-with-help">
<div
@ -1966,7 +1966,7 @@ exports[`test renders ./components/form/demo/validate-static.md correctly 1`] =
exports[`test renders ./components/form/demo/without-form-create.md correctly 1`] = `
<form
class="ant-form">
class="ant-form ant-form-horizontal">
<div
class="ant-row ant-form-item ant-form-item-with-help">
<div

View File

@ -27,7 +27,7 @@ describe('Form', () => {
it('should not remove duplicated user input colon when layout is vertical', () => {
const wrapper = mount(
<Form vertical>
<Form layout="vertical">
<Form.Item label="label:">input</Form.Item>
<Form.Item label="label">input</Form.Item>
</Form>

View File

@ -63,7 +63,12 @@ class App extends React.Component {
</Select>
)}
</FormItem>
<FormItem wrapperCol={{ span: 8, offset: 4 }}>
<FormItem
wrapperCol={{
xs: { span: 8, offset: 0 },
sm: { span: 8, offset: 4 },
}}
>
<Button type="primary" htmlType="submit">
Submit
</Button>

View File

@ -108,7 +108,7 @@ class Demo extends React.Component {
render() {
const { getFieldDecorator } = this.props.form;
return (
<Form inline onSubmit={this.handleSubmit}>
<Form layout="inline" onSubmit={this.handleSubmit}>
<FormItem label="Price">
{getFieldDecorator('price', {
initialValue: { number: 0, currency: 'rmb' },

View File

@ -63,7 +63,10 @@ class DynamicFieldSet extends React.Component {
wrapperCol: { span: 20 },
};
const formItemLayoutWithOutLabel = {
wrapperCol: { span: 20, offset: 4 },
wrapperCol: {
xs: { span: 20, offset: 0 },
sm: { span: 20, offset: 4 },
},
};
getFieldDecorator('keys', { initialValue: [] });
const keys = getFieldValue('keys');

View File

@ -29,7 +29,7 @@ const CollectionCreateForm = Form.create()(
onCancel={onCancel}
onOk={onCreate}
>
<Form vertical>
<Form layout="vertical">
<FormItem label="Title">
{getFieldDecorator('title', {
rules: [{ required: true, message: 'Please input the title of collection!' }],

View File

@ -35,7 +35,7 @@ const CustomizedForm = Form.create({
})((props) => {
const { getFieldDecorator } = props.form;
return (
<Form inline>
<Form layout="inline">
<FormItem label="Username">
{getFieldDecorator('username', {
rules: [{ required: true, message: 'Username is required!' }],

View File

@ -41,7 +41,7 @@ class HorizontalLoginForm extends React.Component {
const userNameError = isFieldTouched('userName') && getFieldError('userName');
const passwordError = isFieldTouched('password') && getFieldError('password');
return (
<Form inline onSubmit={this.handleSubmit}>
<Form layout="inline" onSubmit={this.handleSubmit}>
<FormItem
validateStatus={userNameError ? 'error' : ''}
help={userNameError || ''}

View File

@ -34,12 +34,14 @@ class FormLayoutDemo extends React.Component {
wrapperCol: { span: 14 },
} : null;
const buttonItemLayout = formLayout === 'horizontal' ? {
wrapperCol: { span: 14, offset: 4 },
wrapperCol: {
xs: { span: 14, offset: 0 },
sm: { span: 14, offset: 4 },
},
} : null;
const layoutProps = { [formLayout]: true };
return (
<div>
<Form {...layoutProps}>
<Form layout={formLayout}>
<FormItem
label="Form Layout"
{...formItemLayout}

View File

@ -81,8 +81,14 @@ class RegistrationForm extends React.Component {
};
const tailFormItemLayout = {
wrapperCol: {
span: 14,
offset: 6,
xs: {
span: 14,
offset: 0,
},
sm: {
span: 14,
offset: 6,
},
},
};
const prefixSelector = getFieldDecorator('prefix', {

View File

@ -108,7 +108,12 @@ class TimeRelatedForm extends React.Component {
<TimePicker />
)}
</FormItem>
<FormItem wrapperCol={{ span: 16, offset: 8 }}>
<FormItem
wrapperCol={{
xs: { span: 16, offset: 0 },
sm: { span: 16, offset: 8 },
}}
>
<Button type="primary" htmlType="submit" size="large">Submit</Button>
</FormItem>
</Form>

View File

@ -157,7 +157,12 @@ class Demo extends React.Component {
)}
</FormItem>
<FormItem wrapperCol={{ span: 12, offset: 6 }}>
<FormItem
wrapperCol={{
xs: { span: 12, offset: 0 },
sm: { span: 12, offset: 6 },
}}
>
<Button type="primary" htmlType="submit">Submit</Button>
</FormItem>
</Form>

View File

@ -37,8 +37,10 @@ A form field is defined using `<Form.Item />`.
| Property | Description | Type | Default Value |
|-----------|------------------------------------------|------------|---------|
| form | Decorated by `Form.create()` will be automatically set `this.props.form` property, so just pass to form, you don't need to set it by yourself after 1.7.0. | object | n/a
| vertical | Use vertical layout. | boolean | false |
| inline | Use inline alignment. | boolean | false |
| layout | Define form layout(Support after 2.8) | 'horizontal'\|'vertical'\|'inline' | 'horizontal' |
| horizontal | Use horizontal layout(Deprecated after 2.8) | boolean | true |
| vertical | Use vertical layout(Deprecated after 2.8) | boolean | false |
| inline | Use inline alignment(Deprecated after 2.8) | boolean | false |
| onSubmit | Defines a function will be called if form data validation is successful. | Function(e:Event) | |
| hideRequiredMark | Hide required mark of all form items | Boolean | false |

View File

@ -39,8 +39,10 @@ title: Form
| 参数 | 说明 | 类型 | 默认值 |
|-----------|------------------------------------------|------------|-------|
| form | 经 `Form.create()` 包装过的组件会自带 `this.props.form` 属性,直接传给 Form 即可。1.7.0 之后无需设置 | object | 无 |
| vertical | 垂直排列布局 | boolean | false |
| inline | 行内排列布局 | boolean | false |
| layout | 表单布局(2.8 之后支持) | 'horizontal'\|'vertical'\|'inline' | 'horizontal' |
| horizontal | 水平排列布局(2.8 之后废弃) | boolean | true |
| vertical | 垂直排列布局(2.8 之后废弃) | boolean | false |
| inline | 行内排列布局(2.8 之后废弃) | boolean | false |
| onSubmit | 数据验证成功后回调事件 | Function(e:Event) | |
| hideRequiredMark | 隐藏所有表单项的必选标记 | Boolean | false |

View File

@ -86,33 +86,24 @@ input[type="checkbox"] {
}
&-label {
text-align: right;
display: block;
float: none; // To reset `col-*`
padding: 0 0 8px;
text-align: left;
vertical-align: middle;
line-height: @form-component-height;
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
label {
color: @label-color;
&:after {
content: ":";
margin: 0 8px 0 2px;
position: relative;
top: -0.5px;
}
}
}
.@{ant-prefix}-switch {
margin: 4px 0;
}
&-no-colon &-label label:after {
content: " ";
}
}
.@{form-prefix-cls}-explain {
@ -247,47 +238,59 @@ form {
}
}
// Form layout
//== Vertical Form
.@{form-prefix-cls}-vertical .@{form-prefix-cls}-item-label,
.@{ant-prefix}-col-24.@{form-prefix-cls}-item-label { // when labelCol is 24, it is a vertical form
padding: 0 0 8px;
display: block;
text-align: left;
label:after {
content: '';
}
}
//== Inline Form
.@{form-prefix-cls}-inline {
.@{form-prefix-cls}-item {
display: inline-block;
margin-right: 10px;
margin-bottom: 0;
&-with-help {
margin-bottom: 24px;
}
> div {
@media (min-width: @screen-sm) {
// Form layout
// == common style of Horizontal Form & Inline Form
.@{form-prefix-cls}-horizontal,
.@{form-prefix-cls}-inline {
// when labelCol is 24, it is a vertical form
.@{form-prefix-cls}-item-label:not(.@{ant-prefix}-col-24) {
display: inline-block;
vertical-align: middle;
float: left; // To revert `col-*`
padding: 0;
text-align: right;
label:after {
content: ":";
margin: 0 8px 0 2px;
position: relative;
top: -0.5px;
}
}
.@{form-prefix-cls}-item-no-colon .@{form-prefix-cls}-item-label label:after {
content: " ";
}
}
.@{form-prefix-cls}-text {
display: inline-block;
}
//== Inline Form
.@{form-prefix-cls}-inline {
.@{form-prefix-cls}-item {
display: inline-block;
margin-right: 10px;
margin-bottom: 0;
.has-feedback {
display: inline-block;
}
&-with-help {
margin-bottom: 24px;
}
// Fix https://github.comdesigndesign/issues/1040
.@{form-prefix-cls}-explain {
position: absolute;
> div {
display: inline-block;
vertical-align: middle;
}
}
.@{form-prefix-cls}-text {
display: inline-block;
}
.has-feedback {
display: inline-block;
}
// Fix https://github.comdesigndesign/issues/1040
.@{form-prefix-cls}-explain {
position: absolute;
}
}
}

View File

@ -566,15 +566,15 @@ exports[`test renders ./components/grid/demo/reponsive.md correctly 1`] = `
<div
class="ant-row">
<div
class="ant-col-xs-2 ant-col-sm-4 ant-col-md-6 ant-col-lg-8">
class="ant-col-xs-2 ant-col-sm-4 ant-col-md-6 ant-col-lg-8 ant-col-xl-10">
Col
</div>
<div
class="ant-col-xs-20 ant-col-sm-16 ant-col-md-12 ant-col-lg-8">
class="ant-col-xs-20 ant-col-sm-16 ant-col-md-12 ant-col-lg-8 ant-col-xl-4">
Col
</div>
<div
class="ant-col-xs-2 ant-col-sm-4 ant-col-md-6 ant-col-lg-8">
class="ant-col-xs-2 ant-col-sm-4 ant-col-md-6 ant-col-lg-8 ant-col-xl-10">
Col
</div>
</div>

View File

@ -25,6 +25,7 @@ export interface ColProps {
sm?: number | ColSize;
md?: number | ColSize;
lg?: number | ColSize;
xl?: number | ColSize;
prefixCls?: string;
style?: React.CSSProperties;
}
@ -42,13 +43,14 @@ export default class Col extends React.Component<ColProps, any> {
sm: objectOrNumber,
md: objectOrNumber,
lg: objectOrNumber,
xl: objectOrNumber,
};
render() {
const props = this.props;
const { span, order, offset, push, pull, className, children, prefixCls = 'ant-col', ...others } = props;
let sizeClassObj = {};
['xs', 'sm', 'md', 'lg'].forEach(size => {
['xs', 'sm', 'md', 'lg', 'xl'].forEach(size => {
let sizeProps: ColSize = {};
if (typeof props[size] === 'number') {
sizeProps.span = props[size];

View File

@ -7,13 +7,13 @@ title:
## zh-CN
`span` `pull` `push` `offset` `order` 属性可以通过内嵌到 `xs` `sm` `md` `lg` 属性中来使用。
`span` `pull` `push` `offset` `order` 属性可以通过内嵌到 `xs` `sm` `md` `lg` `xl` 属性中来使用。
其中 `xs={6}` 相当于 `xs={{ span: 6 }}`
## en-US
`Span` `pull` `push` `offset` `order` property can be embedded into `xs` `sm` `md` `lg` properties to use,
`Span` `pull` `push` `offset` `order` property can be embedded into `xs` `sm` `md` `lg` `xl` properties to use,
where `xs = {6}` is equivalent to `xs = {{span: 6}}`.
````jsx

View File

@ -7,20 +7,20 @@ title:
## zh-CN
参照 Bootstrap 的 [响应式设计](http://getbootstrap.com/css/#grid-media-queries),预设个响应尺寸:`xs` `sm` `md` `lg`。
参照 Bootstrap 的 [响应式设计](http://getbootstrap.com/css/#grid-media-queries),预设个响应尺寸:`xs` `sm` `md` `lg` `xl`。
## en-US
Referring to the Bootstrap [responsive design] (http://getbootstrap.com/css/#grid-media-queries), here preset four dimensions: `xs` `sm` `md` `lg`.
Referring to the Bootstrap [responsive design] (http://getbootstrap.com/css/#grid-media-queries), here preset five dimensions: `xs` `sm` `md` `lg` `xl`.
````jsx
import { Row, Col } from 'antd';
ReactDOM.render(
<Row>
<Col xs={2} sm={4} md={6} lg={8}>Col</Col>
<Col xs={20} sm={16} md={12} lg={8}>Col</Col>
<Col xs={2} sm={4} md={6} lg={8}>Col</Col>
<Col xs={2} sm={4} md={6} lg={8} xl={10}>Col</Col>
<Col xs={20} sm={16} md={12} lg={8} xl={4}>Col</Col>
<Col xs={2} sm={4} md={6} lg={8} xl={10}>Col</Col>
</Row>
, mountNode);
````

View File

@ -108,5 +108,6 @@ Ant Design layout component if it can not meet your needs, you can use the excel
| sm | `≥768px`, could be a `span` value or a object contain above props | number\|object | - |
| md | `≥992px`, could be a `span` value or a object contain above props | number\|object | - |
| lg | `≥1200px`, could be a `span` value or a object contain above props | number\|object | - |
| xl | `≥1600px`, could be a `span` value or a object contain above props | number\|object | - |
The breakpoints of responsive grid follow [BootStrap 3 media queries rules](http://getbootstrap.com/css/#grid-media-queries)(not contain `occasionally part`).

View File

@ -107,5 +107,6 @@ Ant Design 的布局组件若不能满足你的需求,你也可以直接使用
| sm | `≥768px` 响应式栅格,可为栅格数或一个包含其他属性的对象 | number\|object | - |
| md | `≥992px` 响应式栅格,可为栅格数或一个包含其他属性的对象 | number\|object | - |
| lg | `≥1200px` 响应式栅格,可为栅格数或一个包含其他属性的对象 | number\|object | - |
| xl | `≥1600px` 响应式栅格,可为栅格数或一个包含其他属性的对象 | number\|object | - |
响应式栅格的断点遵循了 [BootStrap 3 的规则](http://getbootstrap.com/css/#grid-media-queries)(不包含链接里 `occasionally` 的部分)。

View File

@ -98,3 +98,11 @@
@media (min-width: @screen-lg-min) {
.make-grid(-lg);
}
// Extra Large grid
//
// Columns, offsets, pushes, and pulls for the full hd device range.
@media (min-width: @screen-xl-min) {
.make-grid(-xl);
}

View File

@ -51,13 +51,20 @@ import IconSet from 'site/theme/template/IconSet';
ReactDOM.render(<IconSet className="icons" catigory="suggestion" />, mountNode);
```
### Other Icons
### Application Icons
```__react
import IconSet from 'site/theme/template/IconSet';
ReactDOM.render(<IconSet className="icons" catigory="other" />, mountNode);
```
### Brand and Logos
```__react
import IconSet from 'site/theme/template/IconSet';
ReactDOM.render(<IconSet className="icons" catigory="logo" />, mountNode);
```
## Props
| Property | Description | Type | Default |

View File

@ -59,6 +59,13 @@ import IconSet from 'site/theme/template/IconSet';
ReactDOM.render(<IconSet className="icons" catigory="other" />, mountNode);
```
### 品牌和标识
```__react
import IconSet from 'site/theme/template/IconSet';
ReactDOM.render(<IconSet className="icons" catigory="logo" />, mountNode);
```
## Props
| 参数 | 说明 | 类型 | 默认值 |

View File

@ -1,8 +1,32 @@
// matchMedia polyfill for
// https://github.com/WickyNilliams/enquire.js/issues/82
if (typeof window !== 'undefined') {
const matchMediaPolyfill = function matchMediaPolyfill(mediaQuery: string): MediaQueryList {
return {
media: mediaQuery,
matches: false,
addListener() {
},
removeListener() {
},
};
};
window.matchMedia = window.matchMedia || matchMediaPolyfill;
}
import React from 'react';
import classNames from 'classnames';
import omit from 'omit.js';
import Icon from '../icon';
const dimensionMap = {
xs: '480px',
sm: '768px',
md: '992px',
lg: '1200px',
xl: '1600px',
};
export interface SiderProps {
style?: React.CSSProperties;
prefixCls?: string;
@ -11,13 +35,15 @@ export interface SiderProps {
collapsed?: boolean;
defaultCollapsed?: boolean;
reverseArrow?: boolean;
onCollapse?: (collapsed: boolean) => void;
onCollapse?: (collapsed: boolean, type: 'clickTrigger' | 'responsive') => void;
trigger?: React.ReactNode;
width?: number | string;
collapsedWidth?: number;
collapsedWidth?: number | string;
breakpoint?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
}
export default class Sider extends React.Component<SiderProps, any> {
static __ANT_LAYOUT_SIDER: any = true;
static defaultProps = {
prefixCls: 'ant-layout-sider',
@ -27,11 +53,19 @@ export default class Sider extends React.Component<SiderProps, any> {
width: 200,
collapsedWidth: 64,
style: {},
name: 'Sider',
};
private mql: any;
constructor(props) {
super(props);
let matchMedia;
if (typeof window !== 'undefined') {
matchMedia = window.matchMedia;
}
if (matchMedia && props.breakpoint && props.breakpoint in dimensionMap) {
this.mql = matchMedia(`(max-width: ${dimensionMap[props.breakpoint]})`);
}
let collapsed;
if ('collapsed' in props) {
collapsed = props.collapsed;
@ -40,6 +74,7 @@ export default class Sider extends React.Component<SiderProps, any> {
}
this.state = {
collapsed,
below: false,
};
}
@ -51,7 +86,27 @@ export default class Sider extends React.Component<SiderProps, any> {
}
}
setCollapsed = (collapsed) => {
componentDidMount() {
if (this.mql) {
this.mql.addListener(this.responsiveHandler);
this.responsiveHandler(this.mql);
}
}
componentWillUnmount() {
if (this.mql) {
this.mql.removeListener(this.responsiveHandler);
}
}
responsiveHandler = (mql) => {
this.setState({ below: mql.matches });
if (this.state.collapsed !== mql.matches) {
this.setCollapsed(mql.matches, 'responsive');
}
}
setCollapsed = (collapsed, type) => {
if (!('collapsed' in this.props)) {
this.setState({
collapsed,
@ -59,30 +114,32 @@ export default class Sider extends React.Component<SiderProps, any> {
}
const { onCollapse } = this.props;
if (onCollapse) {
onCollapse(collapsed);
onCollapse(collapsed, type);
}
}
toggle = () => {
const collapsed = !this.state.collapsed;
this.setCollapsed(collapsed);
this.setCollapsed(collapsed, 'clickTrigger');
}
belowShowChange = () => {
this.setState({ belowShow: !this.state.belowShow });
}
render() {
const {
prefixCls, className, collapsible, reverseArrow, trigger, style, width, collapsedWidth,
const { prefixCls, className,
collapsible, reverseArrow, trigger, style, width, collapsedWidth,
...others,
} = this.props;
const divProps = omit(others, ['collapsed', 'defaultCollapsed', 'onCollapse', 'name']);
const siderCls = classNames(className, prefixCls, {
[`${prefixCls}-collapsed`]: !!this.state.collapsed,
[`${prefixCls}-has-trigger`]: !!trigger,
});
const divStyle = {
...style,
flex: `0 0 ${this.state.collapsed ? collapsedWidth : width}px`,
width: `${this.state.collapsed ? collapsedWidth : width}px`,
};
const divProps = omit(others, ['collapsed',
'defaultCollapsed', 'onCollapse', 'breakpoint']);
const siderWidth = this.state.collapsed ? collapsedWidth : width;
// special trigger when collapsedWidth == 0
const zeroWidthTrigger = collapsedWidth === 0 || collapsedWidth === '0' ?
(<span onClick={this.toggle} className={`${prefixCls}-zero-width-trigger`}>
<Icon type="bars" />
</span>) : null;
const iconObj = {
'expanded': reverseArrow ? <Icon type="right" /> : <Icon type="left" />,
'collapsed': reverseArrow ? <Icon type="left" /> : <Icon type="right" />,
@ -91,15 +148,27 @@ export default class Sider extends React.Component<SiderProps, any> {
const defaultTrigger = iconObj[status];
const triggerDom = (
trigger !== null ?
zeroWidthTrigger ||
(<div className={`${prefixCls}-trigger`} onClick={this.toggle}>
{trigger || defaultTrigger}
</div>)
: null
);
const divStyle = {
...style,
flex: `0 0 ${siderWidth}px`,
width: `${siderWidth}px`,
};
const siderCls = classNames(className, prefixCls, {
[`${prefixCls}-collapsed`]: !!this.state.collapsed,
[`${prefixCls}-has-trigger`]: !!trigger,
[`${prefixCls}-below`]: !!this.state.below,
[`${prefixCls}-zero-width`]: siderWidth === 0 || siderWidth === '0',
});
return (
<div className={siderCls} {...divProps} style={divStyle}>
{this.props.children}
{collapsible && triggerDom}
{collapsible || (this.state.below && zeroWidthTrigger) ? triggerDom : null}
</div>
);
}

View File

@ -155,6 +155,173 @@ exports[`test renders ./components/layout/demo/custom-trigger.md correctly 1`] =
</div>
`;
exports[`test renders ./components/layout/demo/fixed.md correctly 1`] = `
<div
class="ant-layout">
<div
class="header ant-layout-header">
<div
class="logo" />
<ul
aria-activedescendant=""
class="ant-menu ant-menu-horizontal ant-menu-dark ant-menu-root"
role="menu"
style="line-height:64px;"
tabindex="0">
<li
aria-selected="false"
class="ant-menu-item"
role="menuitem">
nav 1
</li>
<li
aria-selected="true"
class="ant-menu-item-selected ant-menu-item"
role="menuitem">
nav 2
</li>
<li
aria-selected="false"
class="ant-menu-item"
role="menuitem">
nav 3
</li>
</ul>
</div>
<div
class="content ant-layout-content">
<div
class="ant-breadcrumb"
style="margin:12px 0;">
<span>
<span
class="ant-breadcrumb-link">
Home
</span>
<span
class="ant-breadcrumb-separator">
/
</span>
</span>
<span>
<span
class="ant-breadcrumb-link">
List
</span>
<span
class="ant-breadcrumb-separator">
/
</span>
</span>
<span>
<span
class="ant-breadcrumb-link">
App
</span>
<span
class="ant-breadcrumb-separator">
/
</span>
</span>
</div>
<div
style="background:#fff;padding:24px;min-height:280px;">
Content
</div>
</div>
<div
class="ant-layout-footer"
style="text-align:center;">
Ant Design ©2016 Created by Ant UED
</div>
</div>
`;
exports[`test renders ./components/layout/demo/responsive.md correctly 1`] = `
<div
class="ant-layout ant-layout-has-sider">
<div
class="ant-layout-sider"
style="flex:0 0 200px;width:200px;">
<div
class="logo" />
<ul
aria-activedescendant=""
class="ant-menu ant-menu-inline ant-menu-dark ant-menu-root"
role="menu"
tabindex="0">
<li
aria-selected="false"
class="ant-menu-item"
role="menuitem"
style="padding-left:24px;">
<i
class="anticon anticon-user" />
<span
class="nav-text">
nav 1
</span>
</li>
<li
aria-selected="false"
class="ant-menu-item"
role="menuitem"
style="padding-left:24px;">
<i
class="anticon anticon-video-camera" />
<span
class="nav-text">
nav 2
</span>
</li>
<li
aria-selected="false"
class="ant-menu-item"
role="menuitem"
style="padding-left:24px;">
<i
class="anticon anticon-upload" />
<span
class="nav-text">
nav 3
</span>
</li>
<li
aria-selected="true"
class="ant-menu-item-selected ant-menu-item"
role="menuitem"
style="padding-left:24px;">
<i
class="anticon anticon-user" />
<span
class="nav-text">
nav 4
</span>
</li>
</ul>
</div>
<div
class="ant-layout">
<div
class="ant-layout-header"
style="background:#fff;padding:0;" />
<div
class="ant-layout-content"
style="margin:24px 16px 0;">
<div
style="padding:24px;background:#fff;min-height:360px;">
content
</div>
</div>
<div
class="ant-layout-footer"
style="text-align:center;">
Ant Design ©2016 Created by Ant UED
</div>
</div>
</div>
`;
exports[`test renders ./components/layout/demo/side.md correctly 1`] = `
<div
class="ant-layout ant-layout-has-sider">

View File

@ -0,0 +1,71 @@
---
order: 5
title:
zh-CN: 响应式布局
en-US: Responsive
---
## zh-CN
Layout.Sider 支持响应式布局。
> 说明:配置 `breakpoint` 属性即生效,视窗宽度小于 `breakpoint` 时 Sider 缩小为 `collapsedWidth` 宽度,若将 `collapsedWidth` 设置为零,会出现特殊 trigger。
## en-US
Layout.Sider supports responsive layout.
> Note: You can get a responsive layout by setting `breakpoint`, the Sider will collapse to the width of `collapsedWidth` when window width is below the `breakpoint`. And a special trigger will appear if the `collapsedWidth` is set to `0`.
````jsx
import { Layout, Menu, Icon } from 'antd';
const { Header, Content, Footer, Sider } = Layout;
ReactDOM.render(<Layout>
<Sider
breakpoint="lg"
collapsedWidth="0"
onCollapse={(collapsed, type) => { console.log(collapsed, type); }}
>
<div className="logo" />
<Menu theme="dark" mode="inline" defaultSelectedKeys={['4']}>
<Menu.Item key="1">
<Icon type="user" />
<span className="nav-text">nav 1</span>
</Menu.Item>
<Menu.Item key="2">
<Icon type="video-camera" />
<span className="nav-text">nav 2</span>
</Menu.Item>
<Menu.Item key="3">
<Icon type="upload" />
<span className="nav-text">nav 3</span>
</Menu.Item>
<Menu.Item key="4">
<Icon type="user" />
<span className="nav-text">nav 4</span>
</Menu.Item>
</Menu>
</Sider>
<Layout>
<Header style={{ background: '#fff', padding: 0 }} />
<Content style={{ margin: '24px 16px 0' }}>
<div style={{ padding: 24, background: '#fff', minHeight: 360 }}>
content
</div>
</Content>
<Footer style={{ textAlign: 'center' }}>
Ant Design ©2016 Created by Ant UED
</Footer>
</Layout>
</Layout>, mountNode);
````
````css
#components-layout-demo-responsive .logo {
height: 32px;
background: #333;
border-radius: 6px;
margin: 16px;
}
````

View File

@ -51,9 +51,19 @@ Property | Description | Type | Default
collapsible | whether can be collapsed | boolean | false
defaultCollapsed | to set the initial status | boolean | false |
collapsed | to set the current status | boolean | -
onCollapse | the callback function, can be executed when you switch the sidebar, available only `collapsible: true` | (collapsed) => {} | -
onCollapse | the callback function, can be executed by clicking the trigger or activating the responsive layout | (collapsed, type) => {} | -
trigger | specify the customized trigger, set to null to hide the trigger | string\|ReactNode| - |
width | width of the sidebar | number\|string | 200
collapsedWidth | width of the collapsed sidebar, available only `collapsible: true` | number | 64
collapsedWidth | width of the collapsed sidebar, by setting to `0` a special trigger will appear | number | 64
breakpoint | breakpoint of the responsive layout | Enum { 'xs', 'sm', 'md', 'lg', 'xl' } | - |
style | to custom the styles | object | -
className | container className | string | -
> Note: If you want to wrap the `Sider`, do not forget to add this setting to the customized component: `__ANT_LAYOUT_SIDER = true`. e.g.
```jsx
const CustomizedSider = (props) => <Sider {...props} />
CustomizedSider.__ANT_LAYOUT_SIDER = true;
...
<CustomizedSider>Sider Content</CustomizedSider>
```

View File

@ -52,9 +52,19 @@ title: Layout
| collapsible | 是否可收起 | boolean | false |
| defaultCollapsed | 是否默认收起 | boolean | false |
| collapsed | 当前收起状态 | boolean | - |
| onCollapse | 展开-收起时的回调函数,仅当 `collapsible:true` 时生效 | (collapsed) => {} | - |
| onCollapse | 展开-收起时的回调函数,有点击 trigger 以及响应式反馈两种方式可以触发 | (collapsed, type) => {} | - |
| trigger | 自定义 trigger设置为 null 时隐藏 trigger | string\|ReactNode | - |
| width | 宽度 | number\|string | 200 |
| collapsedWidth | 收缩宽度,仅当 `collapsible:true` 时生效 | number | 64 |
| collapsedWidth | 收缩宽度,设置为 0 会出现特殊 trigger | number | 64 |
| breakpoint | 触发响应式布局的断点 | Enum { 'xs', 'sm', 'md', 'lg', 'xl' } | - |
| style | 指定样式 | object | - |
| className | 容器 className | string | - |
> 注意:如果你想在 `Sider` 基础上进行包装,需要给自定义组件加上 `__ANT_LAYOUT_SIDER = true` 设置,例如:
```jsx
const CustomizedSider = (props) => <Sider {...props} />
CustomizedSider.__ANT_LAYOUT_SIDER = true;
...
<CustomizedSider>Sider Content</CustomizedSider>
```

View File

@ -5,7 +5,6 @@ export interface BasicProps {
style?: React.CSSProperties;
prefixCls?: string;
className?: string;
name: string;
}
function generator(props) {
@ -17,7 +16,7 @@ function generator(props) {
static Sider: any;
render() {
const { prefixCls } = props;
return <Basic prefixCls={prefixCls} name={props.name} {...this.props}/>;
return <Basic prefixCls={prefixCls} {...this.props}/>;
}
};
};
@ -25,15 +24,13 @@ function generator(props) {
class Basic extends React.Component<BasicProps, any> {
render() {
const { prefixCls, className, children, name, ...others } = this.props;
const { prefixCls, className, children, ...others } = this.props;
let hasSider;
if (name === 'Layout') {
React.Children.forEach(children, (ele: React.ReactElement<any>) => {
if (ele && ele.props && ele.props.name === 'Sider') {
hasSider = true;
}
});
}
React.Children.forEach(children, (element: any) => {
if (element && element.type && element.type.__ANT_LAYOUT_SIDER) {
hasSider = true;
}
});
const divCls = classNames(className, prefixCls, {
[`${prefixCls}-has-sider`]: hasSider,
});
@ -45,22 +42,18 @@ class Basic extends React.Component<BasicProps, any> {
const Layout = generator({
prefixCls: 'ant-layout',
name: 'Layout',
})(Basic);
const Header = generator({
prefixCls: 'ant-layout-header',
name: 'Header',
})(Basic);
const Footer = generator({
prefixCls: 'ant-layout-footer',
name: 'Footer',
})(Basic);
const Content = generator({
prefixCls: 'ant-layout-content',
name: 'Content',
})(Basic);
Layout.Header = Header;

View File

@ -60,5 +60,31 @@
background: tint(@heading-color, 20%);
color: #fff;
}
&-zero-width {
& > * {
overflow: hidden;
}
&-trigger {
position: absolute;
top: @layout-header-height;
right: -@layout-zero-trigger-width;
text-align: center;
width: @layout-zero-trigger-width;
height: @layout-zero-trigger-height;
line-height: @layout-zero-trigger-height;
background: @layout-sider-background;
color: #fff;
font-size: @layout-zero-trigger-width / 2;
border-radius: 0 @border-radius-base @border-radius-base 0;
cursor: pointer;
transition: background .3s ease;
&:hover {
background: tint(@layout-sider-background, 10%);
}
}
}
}
}

View File

@ -10,6 +10,10 @@ import esES from '../es_ES';
import svSE from '../sv_SE';
import frBE from '../fr_BE';
import deDE from '../de_DE';
import nlNL from '../nl_NL';
import caES from '../ca_ES';
import csCZ from '../cs_CZ';
import koKR from '../ko_KR';
const Option = Select.Option;
const RangePicker = DatePicker.RangePicker;
@ -55,7 +59,7 @@ const App = () => (
describe('Locale Provider', () => {
it('should display the text as locale changed', () => {
[enUS, ptBR, ruRU, esES, svSE, frBE, deDE].forEach((locale) => {
[enUS, ptBR, ruRU, esES, svSE, frBE, deDE, nlNL, caES, csCZ, koKR].forEach((locale) => {
const wrapper = mount(
<LocaleProvider locale={locale}>
<App />

View File

@ -0,0 +1,39 @@
import moment from 'moment';
moment.locale('ca');
import Pagination from 'rc-pagination/lib/locale/ca_ES';
import DatePicker from '../date-picker/locale/ca_ES';
import TimePicker from '../time-picker/locale/ca_ES';
import Calendar from '../calendar/locale/ca_ES';
export default {
locale: 'ca',
Pagination,
DatePicker,
TimePicker,
Calendar,
Table: {
filterTitle: 'Filtrar Menu',
filterConfirm: 'OK',
filterReset: 'Restablir',
emptyText: 'Sense dades',
},
Modal: {
okText: 'OK',
cancelText: 'Cancel·lar',
justOkText: 'OK',
},
Popconfirm: {
okText: 'OK',
cancelText: 'Cancel·lar',
},
Transfer: {
notFoundContent: 'No trobat',
searchPlaceholder: 'Cercar aquí',
itemUnit: 'item',
itemsUnit: 'items',
},
Select: {
notFoundContent: 'No trobat',
},
};

View File

@ -0,0 +1,45 @@
import moment from 'moment';
moment.locale('cs');
import Pagination from 'rc-pagination/lib/locale/cs_CZ';
import DatePicker from '../date-picker/locale/cs_CZ';
import TimePicker from '../time-picker/locale/cs_CZ';
import Calendar from '../calendar/locale/cs_CZ';
export default {
locale: 'cs',
Pagination,
DatePicker,
TimePicker,
Calendar,
Table: {
filterTitle: 'Filtr',
filterConfirm: 'Potvrdit',
filterReset: 'Obnovit',
emptyText: 'Žádná data',
},
Modal: {
okText: 'Ok',
cancelText: 'Storno',
justOkText: 'Ok',
},
Popconfirm: {
okText: 'Ok',
cancelText: 'Storno',
},
Transfer: {
notFoundContent: 'Nenalezeno',
searchPlaceholder: 'Vyhledávání',
itemUnit: 'položka',
itemsUnit: 'položek',
},
Select: {
notFoundContent: 'Nenalezeno',
},
Upload: {
uploading: 'Nahrávání...',
removeFile: 'Odstranit soubor',
uploadError: 'Chyba při nahrávání',
previewFile: 'Zobrazit soubor',
},
};

View File

@ -36,4 +36,10 @@ export default {
Select: {
notFoundContent: 'Not Found',
},
Upload: {
uploading: 'Uploading...',
removeFile: 'Remove file',
uploadError: 'Upload error',
previewFile: 'Preview file',
},
};

View File

@ -0,0 +1,45 @@
import moment from 'moment';
moment.locale('ko');
import Pagination from 'rc-pagination/lib/locale/ko_KR';
import DatePicker from '../date-picker/locale/ko_KR';
import TimePicker from '../time-picker/locale/ko_KR';
import Calendar from '../calendar/locale/ko_KR';
export default {
locale: 'ko',
Pagination,
DatePicker,
TimePicker,
Calendar,
Table: {
filterTitle: '필터 메뉴',
filterConfirm: '확인',
filterReset: '초기화',
emptyText: '데이터 없음',
},
Modal: {
okText: '확인',
cancelText: '취소',
justOkText: '확인',
},
Popconfirm: {
okText: '확인',
cancelText: '취소',
},
Transfer: {
notFoundContent: '데이터 없음',
searchPlaceholder: '여기에 검색하세요',
itemUnit: '개',
itemsUnit: '개',
},
Select: {
notFoundContent: '데이터 없음',
},
Upload: {
uploading: '업로드 중...',
removeFile: '파일 삭제',
uploadError: '업로드 실패',
previewFile: '파일 미리보기',
},
};

View File

@ -0,0 +1,39 @@
import moment from 'moment';
moment.locale('nl');
import Pagination from 'rc-pagination/lib/locale/nl_NL';
import DatePicker from '../date-picker/locale/nl_NL';
import TimePicker from '../time-picker/locale/nl_NL';
import Calendar from '../calendar/locale/nl_NL';
export default {
locale: 'nl',
Pagination,
DatePicker,
TimePicker,
Calendar,
Table: {
filterTitle: 'Filter Menu',
filterConfirm: 'OK',
filterReset: 'Reset',
emptyText: 'Geen gegevens',
},
Modal: {
okText: 'OK',
cancelText: 'Annuleren',
justOkText: 'OK',
},
Popconfirm: {
okText: 'OK',
cancelText: 'Annuleren',
},
Transfer: {
notFoundContent: 'Niet gevonden',
searchPlaceholder: 'Zoeken',
itemUnit: 'item',
itemsUnit: 'items',
},
Select: {
notFoundContent: 'Niet gevonden',
},
};

View File

@ -0,0 +1,63 @@
---
order: 8
title:
zh-CN: 自定义触发字符
en-US: Customize Trigger Token
---
## zh-CN
通过 `prefix` 属性自定义触发字符。默认为 `@`, 可以定义为数组。
## en-US
Customize Trigger Token by `prefix` props. Default to `@`, `Array<string>` also supported.
````jsx
import { Mention } from 'antd';
const { toString } = Mention;
function onChange(editorState) {
console.log(toString(editorState));
}
function onSelect(suggestion) {
console.log('onSelect', suggestion);
}
const users = ['afc163', 'benjycui', 'yiminghe', 'jljsj33', 'dqaria', 'RaoHai'];
const tags = ['1.0', '2.0', '3.0'];
class App extends React.Component {
constructor() {
super();
this.state = {
suggestions: [],
};
}
onSearchChange = (value, trigger) => {
console.log('onSearchChange', value, trigger);
const dataSource = trigger === '@' ? users : tags;
this.setState({
suggestions: dataSource.filter(item => item.indexOf(value) !== -1),
});
}
render() {
return (
<Mention
style={{ width: '100%', height: 100 }}
onChange={onChange}
placeholder="input @ to mention people, # to mention tag"
prefix={['@', '#']}
onSearchChange={this.onSearchChange}
suggestions={this.state.suggestions}
onSelect={onSelect}
/>
);
}
}
ReactDOM.render(
<App />
, mountNode);
````

View File

@ -34,13 +34,13 @@ When need to mention someone or something.
|----------|---------------|----------|--------------|
| suggestions | suggestion content | Array<string\|Mention.Nav> | [] |
| suggestionStyle | style of suggestion container | object | {} |
| onSearchChange | Callback function called when search content changes | function(value:string) | [] |
| onSearchChange | Callback function called when search content changes | function(value:string, trigger: string) | [] |
| onChange | Callback function called when content of input changes | function(editorState: EditorState) | null |
| onSelect | Callback function called when select from suggestions | function(suggestion: string, data?: any) | null |
| notFoundContent| suggestion when suggestions empty | string | '无匹配结果,轻敲空格完成输入' |
| loading | loading mode | boolean | false |
| multiLines | multilines mode | boolean | false |
| prefix | character which will trigger Mention to show mention list | string | '@' |
| prefix | character which will trigger Mention to show mention list | string or Array<string> | '@' |
| defaultValue | default value | EditorState, you can use `Mention.toEditorState` to convert text to `EditorState` | null |
| value | core state of mention | EditorState | null |
| placeholder | placeholder of input | string | null |

View File

@ -56,9 +56,9 @@ export default class Mention extends React.Component<MentionProps, MentionState>
}
}
onSearchChange = (value) => {
onSearchChange = (value, prefix) => {
if (this.props.onSearchChange) {
return this.props.onSearchChange(value);
return this.props.onSearchChange(value, prefix);
}
return this.defaultSearchChange(value);
}

View File

@ -34,13 +34,13 @@ title: Mention
|----------|---------------|----------|--------------|
| suggestions | 建议内容 | Array<string\|Mention.Nav> | [] |
| suggestionStyle | 弹出下拉框样式 | object | {} |
| onSearchChange | 输入框中 @ 变化时回调 | function(value:string) | [] |
| onSearchChange | 输入框中 @ 变化时回调 | function(value:string, trigger: string) | [] |
| onChange | 输入框内容变化时回调 | function(editorState: EditorState) | null |
| onSelect | 下拉框选择建议时回调 | function(suggestion: string, data?: any) | null |
| notFoundContent| 未找到时的内容 | string | '无匹配结果,轻敲空格完成输入' |
| loading | 加载中 | boolean | false |
| multiLines | 多行模式 | boolean | false |
| prefix | 触发弹出下拉框的字符 | string | '@' |
| prefix | 触发弹出下拉框的字符 | string or Array<string> | '@' |
| placeholder | 输入框默认文字 | string | null |
| defaultValue | 默认值 | EditorState, 可以用 Mention.toEditorState(text) 把文字转换成 EditorState | null |
| value | 值 | EditorState | null |

View File

@ -28,6 +28,7 @@ This components provides 4 static methods, with arguments as following:
| content | content of the message | string\|ReactNode | - |
| duration | time before auto-dismiss,in seconds | number | 1.5 |
| onClose | Specify a function that will be called after the message closed| Function | - |
| getContainer | specify render container | () => HTMLElement | () => document.body |
Methods for global configuration and destruction are also provided:

View File

@ -61,6 +61,7 @@ export interface ConfigOptions {
top?: number;
duration?: number;
prefixCls?: string;
getContainer?: () => HTMLElement;
}
export default {

View File

@ -29,7 +29,7 @@ title: Message
| content | 提示内容 | string\|ReactNode | - |
| duration | 自动关闭的延时,单位秒 | number | 1.5 |
| onClose | 关闭时触发的回调函数 | Function | - |
| getContainer | 配置渲染节点的输出位置 | () => HTMLElement | () => document.body |
还提供了全局配置和全局销毁方法:

View File

@ -38,7 +38,7 @@ The properties of config are as follows:
| onClose | Specify a function that will be called after clicking the default close button | Function | - |
| duration | A notification box is closed after 4.5s by default. When specifying `duration` to null or 0, it will never be closed automatically | number | 4.5 |
| placement | To set the position, which can be one of `topLeft` `topRight` `bottomLeft` `bottomRight` | string | topRight |
| getContainer | specify render container | () => HTMLNode | () => document.body |
`notification` also provide a global `config()` method that can be used for specifying the default options. Once this method is used, all the notification boxes
will take into account these globally defined options before displaying.

View File

@ -53,6 +53,7 @@ export interface ArgsProps {
duration?: number;
icon?: React.ReactNode;
placement?: notificationPlacement;
getContainer?: () => HTMLElement;
}
export interface ConfigProps {

View File

@ -38,6 +38,7 @@ config 参数如下:
| onClose | 点击默认关闭按钮时触发的回调函数 | Function | - |
| duration | 默认 4.5 秒后自动关闭,配置为 null 则不自动关闭 | number | 4.5 |
| placement | 弹出位置,可选 `topLeft` `topRight` `bottomLeft` `bottomRight` | string | topRight |
| getContainer | 配置渲染节点的输出位置 | () => HTMLNode | () => document.body |
还提供了一个全局配置方法,在调用前提前配置,全局一次生效。

View File

@ -1,92 +1,404 @@
exports[`test renders ./components/rate/demo/basic.md correctly 1`] = `
<ul
class="ant-rate ">
class="ant-rate">
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-content" />
class="ant-rate-star-first">
<i
class="anticon anticon-star" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-star" />
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-content" />
class="ant-rate-star-first">
<i
class="anticon anticon-star" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-star" />
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-content" />
class="ant-rate-star-first">
<i
class="anticon anticon-star" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-star" />
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-content" />
class="ant-rate-star-first">
<i
class="anticon anticon-star" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-star" />
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-content" />
class="ant-rate-star-first">
<i
class="anticon anticon-star" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-star" />
</div>
</li>
</ul>
`;
exports[`test renders ./components/rate/demo/charactor.md correctly 1`] = `
<div>
<ul
class="ant-rate">
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-first">
<i
class="anticon anticon-heart" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-heart" />
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-first">
<i
class="anticon anticon-heart" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-heart" />
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-first">
<i
class="anticon anticon-heart" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-heart" />
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-first">
<i
class="anticon anticon-heart" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-heart" />
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-first">
<i
class="anticon anticon-heart" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-heart" />
</div>
</li>
</ul>
<br />
<ul
class="ant-rate"
style="font-size:36px;">
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-first">
A
</div>
<div
class="ant-rate-star-second">
A
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-first">
A
</div>
<div
class="ant-rate-star-second">
A
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-first">
A
</div>
<div
class="ant-rate-star-second">
A
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-first">
A
</div>
<div
class="ant-rate-star-second">
A
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-first">
A
</div>
<div
class="ant-rate-star-second">
A
</div>
</li>
</ul>
<br />
<ul
class="ant-rate">
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-first">
</div>
<div
class="ant-rate-star-second">
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-first">
</div>
<div
class="ant-rate-star-second">
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-first">
</div>
<div
class="ant-rate-star-second">
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-first">
</div>
<div
class="ant-rate-star-second">
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-first">
</div>
<div
class="ant-rate-star-second">
</div>
</li>
</ul>
</div>
`;
exports[`test renders ./components/rate/demo/disabled.md correctly 1`] = `
<ul
class="ant-rate ant-rate-disabled">
<li
class="ant-rate-star ant-rate-star-full">
<div
class="ant-rate-star-content" />
class="ant-rate-star-first">
<i
class="anticon anticon-star" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-star" />
</div>
</li>
<li
class="ant-rate-star ant-rate-star-full">
<div
class="ant-rate-star-content" />
class="ant-rate-star-first">
<i
class="anticon anticon-star" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-star" />
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-content" />
class="ant-rate-star-first">
<i
class="anticon anticon-star" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-star" />
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-content" />
class="ant-rate-star-first">
<i
class="anticon anticon-star" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-star" />
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-content" />
class="ant-rate-star-first">
<i
class="anticon anticon-star" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-star" />
</div>
</li>
</ul>
`;
exports[`test renders ./components/rate/demo/half.md correctly 1`] = `
<ul
class="ant-rate ">
class="ant-rate">
<li
class="ant-rate-star ant-rate-star-full">
<div
class="ant-rate-star-content" />
class="ant-rate-star-first">
<i
class="anticon anticon-star" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-star" />
</div>
</li>
<li
class="ant-rate-star ant-rate-star-full">
<div
class="ant-rate-star-content" />
class="ant-rate-star-first">
<i
class="anticon anticon-star" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-star" />
</div>
</li>
<li
class="ant-rate-star ant-rate-star-half ant-rate-star-active">
<div
class="ant-rate-star-content" />
class="ant-rate-star-first">
<i
class="anticon anticon-star" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-star" />
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-content" />
class="ant-rate-star-first">
<i
class="anticon anticon-star" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-star" />
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-content" />
class="ant-rate-star-first">
<i
class="anticon anticon-star" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-star" />
</div>
</li>
</ul>
`;
@ -94,31 +406,71 @@ exports[`test renders ./components/rate/demo/half.md correctly 1`] = `
exports[`test renders ./components/rate/demo/text.md correctly 1`] = `
<span>
<ul
class="ant-rate ">
class="ant-rate">
<li
class="ant-rate-star ant-rate-star-full">
<div
class="ant-rate-star-content" />
class="ant-rate-star-first">
<i
class="anticon anticon-star" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-star" />
</div>
</li>
<li
class="ant-rate-star ant-rate-star-full">
<div
class="ant-rate-star-content" />
class="ant-rate-star-first">
<i
class="anticon anticon-star" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-star" />
</div>
</li>
<li
class="ant-rate-star ant-rate-star-full">
<div
class="ant-rate-star-content" />
class="ant-rate-star-first">
<i
class="anticon anticon-star" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-star" />
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-content" />
class="ant-rate-star-first">
<i
class="anticon anticon-star" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-star" />
</div>
</li>
<li
class="ant-rate-star ant-rate-star-zero">
<div
class="ant-rate-star-content" />
class="ant-rate-star-first">
<i
class="anticon anticon-star" />
</div>
<div
class="ant-rate-star-second">
<i
class="anticon anticon-star" />
</div>
</li>
</ul>
<span

View File

@ -0,0 +1,28 @@
---
order: 4
title:
zh-CN: 其他字符
en-US: Other Charactor
---
## zh-CN
可以将星星替换为其他字符,比如字母,数字,字体图标甚至中文。
## en-US
Replace the default star to other charactor like alphabet, digit, iconfont or even Chinese word.
````jsx
import { Rate, Icon } from 'antd';
ReactDOM.render(
<div>
<Rate charactor={<Icon type="heart" />} allowHalf />
<br />
<Rate charactor="A" allowHalf style={{ fontSize: 36 }} />
<br />
<Rate charactor="好" allowHalf />
</div>
, mountNode);
````

View File

@ -18,6 +18,10 @@ Rate component.
| count | star count | number | 5 |
| value | current value | number | - |
| defaultValue | default value | number | 0 |
| onChange | callback | Function(value: number) | - |
| onChange | callback when select value | Function(value: number) | - |
| onHoverChange | callback when hover item | Function(value: number) | - |
| allowHalf | whether to allow semi selection | boolean | false |
| disabled | read only, unable to interact | boolean | false |
| charactor | custom charactor of rate | ReactNode | `<Icon type="star" />` |
| className | custom class name of rate | string | - |
| style | custom style object of rate | object | - |

View File

@ -1,6 +1,7 @@
import React from 'react';
import { PropTypes } from 'react';
import RcRate from 'rc-rate';
import Icon from '../icon';
export interface RateProps {
prefixCls?: string;
@ -10,14 +11,20 @@ export interface RateProps {
allowHalf?: boolean;
disabled?: boolean;
onChange?: (value: number) => any;
onHoverChange?: (value: number) => any;
charactor?: React.ReactNode;
className?: string;
style?: React.CSSProperties;
}
export default class Rate extends React.Component<RateProps, any> {
static propTypes = {
prefixCls: PropTypes.string,
charactor: PropTypes.node,
};
static defaultProps = {
prefixCls: 'ant-rate',
charactor: <Icon type="star" />,
};
render() {
return <RcRate {...this.props} />;

View File

@ -19,6 +19,10 @@ title: Rate
| count | star 总数 | number | 5 |
| value | 当前数,受控值 | number | - |
| defaultValue | 默认值 | number | 0 |
| onChange | 回调 | Function(value: number) | - |
| onChange | 选择时的回调 | Function(value: number) | - |
| onHoverChange | 鼠标经过时数值变化的回调 | Function(value: number) | - |
| allowHalf | 是否允许半选 | boolean | false |
| disabled | 只读,无法进行交互 | boolean | false |
| charactor | 自定义字符 | ReactNode | `<Icon type="star" />` |
| className | 自定义样式类名 | string | - |
| style | 自定义样式对象 | object | - |

View File

@ -3,6 +3,7 @@
@rate-prefix-cls: ~"@{ant-prefix}-rate";
@rate-star-color: #f5a623;
@rate-star-bg: #e9e9e9;
.@{rate-prefix-cls} {
margin: 0;
@ -11,15 +12,9 @@
font-size: 20px;
display: inline-block;
vertical-align: middle;
font-family: 'anticon';
font-weight: normal;
font-style: normal;
&-disabled &-star {
&:before,
&-content:before {
cursor: default;
}
cursor: not-allowed;
&:hover {
transform: scale(1);
}
@ -31,40 +26,42 @@
display: inline-block;
margin-right: 8px;
position: relative;
transition: all 0.3s ease;
transition: all .3s;
color: @rate-star-bg;
cursor: pointer;
&-first,
&-second {
user-select: none;
transition: all .3s;
}
&:hover {
transform: scale(1.1);
}
&:before,
&-content:before {
color: #e9e9e9;
cursor: pointer;
content: "\e660";
transition: all 0.3s ease;
display: block;
}
&-content {
&-first {
position: absolute;
left: 0;
top: 0;
width: 50%;
height: 100%;
overflow: hidden;
&:before {
color: transparent;
}
opacity: 0;
}
&-half &-content:before,
&-full:before {
&-half &-first,
&-half &-second {
opacity: 1;
}
&-half &-first,
&-full &-second {
color: @rate-star-color;
}
&-half:hover &-content:before,
&-full:hover:before {
&-half:hover &-first,
&-full:hover &-second {
color: tint(@rate-star-color, 20%);
}
}

View File

@ -250,7 +250,45 @@
.@{iconfont-css-prefix}-rocket:before { content: "\e90f"; }
.@{iconfont-css-prefix}-dingding:before { content: "\e923"; }
.@{iconfont-css-prefix}-dingding-o:before { content: "\e925"; }
.@{iconfont-css-prefix}-bell:before { content: "\e64e"; }
.@{iconfont-css-prefix}-disconnect:before { content: "\e64f"; }
.@{iconfont-css-prefix}-database:before { content: "\e650"; }
.@{iconfont-css-prefix}-compass:before { content: "\e6db"; }
.@{iconfont-css-prefix}-barcode:before { content: "\e652"; }
.@{iconfont-css-prefix}-hourglass:before { content: "\e653"; }
.@{iconfont-css-prefix}-key:before { content: "\e654"; }
.@{iconfont-css-prefix}-flag:before { content: "\e655"; }
.@{iconfont-css-prefix}-layout:before { content: "\e656"; }
.@{iconfont-css-prefix}-login:before { content: "\e657"; }
.@{iconfont-css-prefix}-printer:before { content: "\e673"; }
.@{iconfont-css-prefix}-sound:before { content: "\e6e9"; }
.@{iconfont-css-prefix}-usb:before { content: "\e6d7"; }
.@{iconfont-css-prefix}-skin:before { content: "\e6d8"; }
.@{iconfont-css-prefix}-tool:before { content: "\e6d9"; }
.@{iconfont-css-prefix}-sync:before { content: "\e6da"; }
.@{iconfont-css-prefix}-wifi:before { content: "\e6d6"; }
.@{iconfont-css-prefix}-car:before { content: "\e6dc"; }
.@{iconfont-css-prefix}-copyright:before { content: "\e6de"; }
.@{iconfont-css-prefix}-schedule:before { content: "\e6df"; }
.@{iconfont-css-prefix}-user-add:before { content: "\e6ed"; }
.@{iconfont-css-prefix}-user-delete:before { content: "\e6e0"; }
.@{iconfont-css-prefix}-usergroup-add:before { content: "\e6dd"; }
.@{iconfont-css-prefix}-usergroup-delete:before { content: "\e6e1"; }
.@{iconfont-css-prefix}-man:before { content: "\e6e2"; }
.@{iconfont-css-prefix}-woman:before { content: "\e6ec"; }
.@{iconfont-css-prefix}-shop:before { content: "\e6e3"; }
.@{iconfont-css-prefix}-gift:before { content: "\e6e4"; }
.@{iconfont-css-prefix}-idcard:before { content: "\e6e5"; }
.@{iconfont-css-prefix}-medicine-box:before { content: "\e6e6"; }
.@{iconfont-css-prefix}-red-envelope:before { content: "\e6e7"; }
.@{iconfont-css-prefix}-coffee:before { content: "\e6e8"; }
.@{iconfont-css-prefix}-trademark:before { content: "\e651"; }
.@{iconfont-css-prefix}-safety:before { content: "\e6ea"; }
.@{iconfont-css-prefix}-wallet:before { content: "\e6eb"; }
.@{iconfont-css-prefix}-bank:before { content: "\e6ee"; }
.@{iconfont-css-prefix}-trophy:before { content: "\e6ef"; }
.@{iconfont-css-prefix}-contacts:before { content: "\e6f0"; }
.@{iconfont-css-prefix}-global:before { content: "\e6f1"; }
.@{iconfont-css-prefix}-spin:before {
display: inline-block;
animation: loadingCircle 1s infinite linear;

View File

@ -43,7 +43,7 @@
// ICONFONT
@iconfont-css-prefix : anticon;
@icon-url : "https://at.alicdn.com/t/font_r5u29ls31bgldi";
@icon-url : "https://at.alicdn.com/t/font_smk8xgfen9xxn7b9";
// LINK
@link-color : @primary-color;
@ -122,35 +122,43 @@
// Extra small screen / phone
@screen-xs : 480px;
@screen-xs-min : @screen-xs;
@screen-xs-max : (@screen-xs-min - 1);
// Small screen / tablet
@screen-sm : 768px;
@screen-sm-min : @screen-sm;
@screen-sm-max : (@screen-sm-min - 1);
// Medium screen / desktop
@screen-md : 992px;
@screen-md-min : @screen-md;
@screen-md-max : (@screen-md-min - 1);
// Large screen / wide desktop
@screen-lg : 1200px;
@screen-lg-min : @screen-lg;
@screen-lg-max : (@screen-lg-min - 1);
// Extra Large screen / full hd
@screen-xl : 1600px;
@screen-xl-min : @screen-xl;
// provide a maximum
@screen-xs-max : (@screen-sm-min - 1);
@screen-sm-max : (@screen-md-min - 1);
@screen-md-max : (@screen-lg-min - 1);
@screen-lg-max : (@screen-xl-min - 1);
// Grid system
@grid-columns : 24;
@grid-gutter-width : 0;
// Layout
@layout-body-background : #ececec;
@layout-header-background : #404040;
@layout-header-height : 64px;
@layout-header-padding : 0 50px;
@layout-footer-padding : 24px 50px;
@layout-sider-background : @layout-header-background;
@layout-trigger-height : 48px;
@layout-body-background : #ececec;
@layout-header-background : #404040;
@layout-header-height : 64px;
@layout-header-padding : 0 50px;
@layout-footer-padding : 24px 50px;
@layout-sider-background : @layout-header-background;
@layout-trigger-height : 48px;
@layout-zero-trigger-width : 36px;
@layout-zero-trigger-height : 42px;
// z-index list
@zindex-affix : 10;

View File

@ -1,6 +1,15 @@
import React from 'react';
import Checkbox from '../checkbox';
import { Store } from './createStore';
import Dropdown from '../dropdown';
import Menu from '../menu';
import Icon from '../icon';
export interface SelectionDecorator {
key: string;
text: React.ReactNode;
onSelect: (changeableRowKeys: string[]) => void;
}
export interface SelectionCheckboxAllProps {
store: Store;
@ -8,9 +17,21 @@ export interface SelectionCheckboxAllProps {
getCheckboxPropsByItem: (item, index) => any;
getRecordKey: (record, index?) => string;
data: any[];
onChange: (e) => void;
prefixCls: string | undefined;
onSelect: (key: string, index: number, selectFunc: any) => void;
selections: SelectionDecorator[];
}
const defaultSelections: SelectionDecorator[] = [{
key: 'all',
text: '全选',
onSelect: () => {},
}, {
key: 'invert',
text: '反选',
onSelect: () => {},
}];
export default class SelectionCheckboxAll extends React.Component<SelectionCheckboxAllProps, any> {
unsubscribe: () => void;
@ -106,17 +127,61 @@ export default class SelectionCheckboxAll extends React.Component<SelectionCheck
return indeterminate;
}
handleSelectAllChagne = (e) => {
let checked = e.target.checked;
this.props.onSelect(checked ? 'all' : 'removeAll', 0, null);
}
renderMenus(selections: SelectionDecorator[]) {
return selections.map((selection, index) => {
return (
<Menu.Item
key={selection.key || index}
>
<div
onClick={() => {this.props.onSelect(selection.key, index, selection.onSelect);}}
>
{selection.text}
</div>
</Menu.Item>
);
});
}
render() {
const { disabled, onChange } = this.props;
const { disabled, prefixCls } = this.props;
const { checked, indeterminate } = this.state;
let selectionPrefixCls = `${prefixCls}-selection`;
let selections = defaultSelections.concat(this.props.selections || []);
let menu = (
<Menu
className={`${selectionPrefixCls}-menu`}
>
{this.renderMenus(selections)}
</Menu>
);
return (
<Checkbox
checked={checked}
indeterminate={indeterminate}
disabled={disabled}
onChange={onChange}
/>
<div className={selectionPrefixCls}>
<Checkbox
checked={checked}
indeterminate={indeterminate}
disabled={disabled}
onChange={this.handleSelectAllChagne}
/>
<Dropdown
overlay={menu}
>
<div className={`${selectionPrefixCls}-down`}>
<span>
<Icon type="down" />
</span>
</div>
</Dropdown>
</div>
);
}
}

View File

@ -10,9 +10,10 @@ import assign from 'object-assign';
import warning from '../_util/warning';
import createStore, { Store } from './createStore';
import SelectionBox from './SelectionBox';
import SelectionCheckboxAll from './SelectionCheckboxAll';
import SelectionCheckboxAll, { SelectionDecorator } from './SelectionCheckboxAll';
import Column, { ColumnProps } from './Column';
import ColumnGroup from './ColumnGroup';
import { SpinProps } from '../spin';
function noop() {
}
@ -45,6 +46,8 @@ export interface TableRowSelection<T> {
getCheckboxProps?: (record: T) => Object;
onSelect?: (record: T, selected: boolean, selectedRows: Object[]) => any;
onSelectAll?: (selected: boolean, selectedRows: Object[], changeRows: Object[]) => any;
onSelectInvert?: (selectedRows: Object[]) => any;
selections?: SelectionDecorator[];
}
export interface TableProps<T> {
@ -65,7 +68,7 @@ export interface TableProps<T> {
onExpandedRowsChange?: (expandedRowKeys: string[]) => void;
onExpand?: (expanded: boolean, record: T) => void;
onChange?: (pagination: PaginationProps | boolean, filters: string[], sorter: Object) => any;
loading?: boolean;
loading?: boolean | SpinProps ;
locale?: Object;
indentSize?: number;
onRowClick?: (record: T, index: number) => any;
@ -99,7 +102,10 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
rowSelection: React.PropTypes.object,
className: React.PropTypes.string,
size: React.PropTypes.string,
loading: React.PropTypes.bool,
loading: React.PropTypes.oneOfType([
React.PropTypes.bool,
React.PropTypes.object,
]),
bordered: React.PropTypes.bool,
onChange: React.PropTypes.func,
locale: React.PropTypes.object,
@ -268,6 +274,8 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
(row, i) => changeRowKeys.indexOf(this.getRecordKey(row, i)) >= 0
);
rowSelection.onSelectAll(checked, selectedRows, changeRows);
} else if (selectWay === 'onSelectInvert' && rowSelection.onSelectInvert) {
rowSelection.onSelectInvert(selectedRowKeys);
}
}
@ -466,37 +474,63 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
});
}
handleSelectAllRow = (e) => {
const checked = e.target.checked;
handleSelectRow = (selectionKey, index, onSelectFunc) => {
const data = this.getFlatCurrentPageData();
const defaultSelection = this.store.getState().selectionDirty ? [] : this.getDefaultSelection();
const selectedRowKeys = this.store.getState().selectedRowKeys.concat(defaultSelection);
const changableRowKeys = data
const changeableRowKeys = data
.filter((item, i) => !this.getCheckboxPropsByItem(item, i).disabled)
.map((item, i) => this.getRecordKey(item, i));
// 记录变化的列
const changeRowKeys: string[] = [];
if (checked) {
changableRowKeys.forEach(key => {
if (selectedRowKeys.indexOf(key) < 0) {
selectedRowKeys.push(key);
let changeRowKeys: string[] = [];
let selectWay = '';
let checked;
// handle default selection
switch (selectionKey) {
case 'all':
changeableRowKeys.forEach(key => {
if (selectedRowKeys.indexOf(key) < 0) {
selectedRowKeys.push(key);
changeRowKeys.push(key);
}
});
selectWay = 'onSelectAll';
checked = true;
break;
case 'removeAll':
changeableRowKeys.forEach(key => {
if (selectedRowKeys.indexOf(key) >= 0) {
selectedRowKeys.splice(selectedRowKeys.indexOf(key), 1);
changeRowKeys.push(key);
}
});
selectWay = 'onSelectAll';
checked = false;
break;
case 'invert':
changeableRowKeys.forEach(key => {
if (selectedRowKeys.indexOf(key) < 0) {
selectedRowKeys.push(key);
}else {
selectedRowKeys.splice(selectedRowKeys.indexOf(key), 1);
}
changeRowKeys.push(key);
}
});
} else {
changableRowKeys.forEach(key => {
if (selectedRowKeys.indexOf(key) >= 0) {
selectedRowKeys.splice(selectedRowKeys.indexOf(key), 1);
changeRowKeys.push(key);
}
});
selectWay = 'onSelectInvert';
});
break;
default:
break;
}
this.store.setState({
selectionDirty: true,
});
// when select custom selection, callback selections[n].onSelect
if (index > 1 && typeof onSelectFunc === 'function') {
return onSelectFunc(changeableRowKeys);
}
this.setSelectedRowKeys(selectedRowKeys, {
selectWay: 'onSelectAll',
selectWay: selectWay,
checked,
changeRowKeys,
});
@ -597,7 +631,9 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
getCheckboxPropsByItem={this.getCheckboxPropsByItem}
getRecordKey={this.getRecordKey}
disabled={checkboxAllDisabled}
onChange={this.handleSelectAllRow}
prefixCls={prefixCls}
onSelect={this.handleSelectRow}
selections={rowSelection.selections || []}
/>
);
}
@ -837,7 +873,7 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
}
render() {
const { style, className, prefixCls, showHeader, loading, ...restProps } = this.props;
const { style, className, prefixCls, showHeader, ...restProps } = this.props;
const data = this.getCurrentPageData();
let columns = this.renderRowSelection();
const expandIconAsCell = this.props.expandedRowRender && this.props.expandIconAsCell !== false;
@ -882,10 +918,17 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
const paginationPatchClass = (this.hasPagination() && data && data.length !== 0)
? `${prefixCls}-with-pagination` : `${prefixCls}-without-pagination`;
let loading = this.props.loading;
if (typeof loading === 'boolean') {
loading = {
spinning: loading,
};
}
return (
<div className={classNames(`${prefixCls}-wrapper`, className)} style={style}>
<Spin
spinning={loading}
{...loading}
className={loading ? `${paginationPatchClass} ${prefixCls}-spin-holder` : ''}
>
{table}

View File

@ -1,5 +1,6 @@
import React from 'react';
import { mount } from 'enzyme';
import { mount, render } from 'enzyme';
import { renderToJson } from 'enzyme-to-json';
import Table from '..';
describe('Table.rowSelection', () => {
@ -166,6 +167,68 @@ describe('Table.rowSelection', () => {
wrapper.find('input').first().simulate('change', { target: { checked: true } });
expect(handleSelectAll).toBeCalledWith(true, data, data);
wrapper.find('input').first().simulate('change', { target: { checked: false } });
expect(handleSelectAll).toBeCalledWith(false, [], data);
});
it('render selection correctly', () => {
const wrapper = mount(createTable());
const dropdownWrapper = render(wrapper.find('Trigger').node.getComponent());
expect(renderToJson(dropdownWrapper)).toMatchSnapshot();
});
it('click select all selection', () => {
const handleSelectAll = jest.fn();
const rowSelection = {
onSelectAll: handleSelectAll,
};
const wrapper = mount(createTable({ rowSelection }));
const dropdownWrapper = mount(wrapper.find('Trigger').node.getComponent());
dropdownWrapper.find('.ant-dropdown-menu-item > div').first().simulate('click');
expect(handleSelectAll).toBeCalledWith(true, data, data);
});
it('fires selectInvert event', () => {
const handleSelectInvert = jest.fn();
const rowSelection = {
onSelectInvert: handleSelectInvert,
};
const wrapper = mount(createTable({ rowSelection }));
const checkboxes = wrapper.find('input');
checkboxes.at(1).simulate('change', { target: { checked: true } });
const dropdownWrapper = mount(wrapper.find('Trigger').node.getComponent());
dropdownWrapper.find('.ant-dropdown-menu-item > div').last().simulate('click');
expect(handleSelectInvert).toBeCalledWith([1, 2, 3]);
});
it('fires selection event', () => {
const handleSelectOdd = jest.fn();
const handleSelectEven = jest.fn();
const rowSelection = {
selections: [{
key: 'odd',
text: '奇数项',
onSelect: handleSelectOdd,
}, {
key: 'even',
text: '偶数项',
onSelect: handleSelectEven,
}],
};
const wrapper = mount(createTable({ rowSelection }));
const dropdownWrapper = mount(wrapper.find('Trigger').node.getComponent());
dropdownWrapper.find('.ant-dropdown-menu-item > div').at(2).simulate('click');
expect(handleSelectOdd).toBeCalledWith([0, 1, 2, 3]);
dropdownWrapper.find('.ant-dropdown-menu-item > div').at(3).simulate('click');
expect(handleSelectEven).toBeCalledWith([0, 1, 2, 3]);
});
// https://github.com/ant-design/ant-design/issues/4245

View File

@ -1,5 +1,5 @@
import React from 'react';
import { render, shallow } from 'enzyme';
import { render, shallow, mount } from 'enzyme';
import { renderToJson } from 'enzyme-to-json';
import Table from '..';
@ -60,4 +60,20 @@ describe('Table', () => {
expect(wrapper.instance().columns).toBe(newColumns);
});
it('loading with Spin', async () => {
const loading = {
spinning: false,
delay: 500,
};
const wrapper = mount(<Table loading={loading} />);
expect(wrapper.find('.ant-spin')).toHaveLength(0);
loading.spinning = true;
wrapper.setProps({ loading });
expect(wrapper.find('.ant-spin')).toHaveLength(0);
await new Promise(resolve => setTimeout(resolve, 500));
expect(wrapper.find('.ant-spin')).toHaveLength(1);
});
});

View File

@ -0,0 +1,29 @@
exports[`Table.rowSelection render selection correctly 1`] = `
<div>
<div
class="ant-dropdown ant-dropdown-placement-bottomLeft ant-dropdown-hidden">
<ul
aria-activedescendant=""
class="ant-dropdown-menu ant-dropdown-menu-vertical ant-table-selection-menu ant-dropdown-menu-light ant-dropdown-menu-root"
role="menu"
tabindex="0">
<li
aria-selected="false"
class="ant-dropdown-menu-item"
role="menuitem">
<div>
全选
</div>
</li>
<li
aria-selected="false"
class="ant-dropdown-menu-item"
role="menuitem">
<div>
反选
</div>
</li>
</ul>
</div>
</div>
`;

View File

@ -1132,17 +1132,27 @@ exports[`test renders ./components/table/demo/dynamic-settings.md correctly 1`]
<th
class="ant-table-selection-column">
<span>
<label
class="ant-checkbox-wrapper">
<span
class="ant-checkbox">
<input
class="ant-checkbox-input"
type="checkbox" />
<div
class="ant-table-selection">
<label
class="ant-checkbox-wrapper">
<span
class="ant-checkbox-inner" />
</span>
</label>
class="ant-checkbox">
<input
class="ant-checkbox-input"
type="checkbox" />
<span
class="ant-checkbox-inner" />
</span>
</label>
<div
class="ant-table-selection-down ant-dropdown-trigger">
<span>
<i
class="anticon anticon-down" />
</span>
</div>
</div>
</span>
</th>
<th
@ -2322,17 +2332,27 @@ exports[`test renders ./components/table/demo/expand-children.md correctly 1`] =
<th
class="ant-table-selection-column">
<span>
<label
class="ant-checkbox-wrapper">
<span
class="ant-checkbox">
<input
class="ant-checkbox-input"
type="checkbox" />
<div
class="ant-table-selection">
<label
class="ant-checkbox-wrapper">
<span
class="ant-checkbox-inner" />
</span>
</label>
class="ant-checkbox">
<input
class="ant-checkbox-input"
type="checkbox" />
<span
class="ant-checkbox-inner" />
</span>
</label>
<div
class="ant-table-selection-down ant-dropdown-trigger">
<span>
<i
class="anticon anticon-down" />
</span>
</div>
</div>
</span>
</th>
<th
@ -6964,17 +6984,27 @@ exports[`test renders ./components/table/demo/row-selection.md correctly 1`] = `
<th
class="ant-table-selection-column">
<span>
<label
class="ant-checkbox-wrapper">
<span
class="ant-checkbox">
<input
class="ant-checkbox-input"
type="checkbox" />
<div
class="ant-table-selection">
<label
class="ant-checkbox-wrapper">
<span
class="ant-checkbox-inner" />
</span>
</label>
class="ant-checkbox">
<input
class="ant-checkbox-input"
type="checkbox" />
<span
class="ant-checkbox-inner" />
</span>
</label>
<div
class="ant-table-selection-down ant-dropdown-trigger">
<span>
<i
class="anticon anticon-down" />
</span>
</div>
</div>
</span>
</th>
<th
@ -7220,17 +7250,27 @@ exports[`test renders ./components/table/demo/row-selection-and-operation.md cor
<th
class="ant-table-selection-column">
<span>
<label
class="ant-checkbox-wrapper">
<span
class="ant-checkbox">
<input
class="ant-checkbox-input"
type="checkbox" />
<div
class="ant-table-selection">
<label
class="ant-checkbox-wrapper">
<span
class="ant-checkbox-inner" />
</span>
</label>
class="ant-checkbox">
<input
class="ant-checkbox-input"
type="checkbox" />
<span
class="ant-checkbox-inner" />
</span>
</label>
<div
class="ant-table-selection-down ant-dropdown-trigger">
<span>
<i
class="anticon anticon-down" />
</span>
</div>
</div>
</span>
</th>
<th
@ -7655,6 +7695,477 @@ exports[`test renders ./components/table/demo/row-selection-and-operation.md cor
</div>
`;
exports[`test renders ./components/table/demo/row-selection-custom.md correctly 1`] = `
<div
class="ant-table-wrapper">
<div
class="ant-spin-nested-loading">
<div
class="ant-spin-container">
<div
class="ant-table ant-table-large ant-table-scroll-position-left">
<div
class="ant-table-content">
<div
class="ant-table-body">
<table
class="">
<colgroup>
<col />
<col />
<col />
<col />
</colgroup>
<thead
class="ant-table-thead">
<tr>
<th
class="ant-table-selection-column">
<span>
<div
class="ant-table-selection">
<label
class="ant-checkbox-wrapper">
<span
class="ant-checkbox">
<input
class="ant-checkbox-input"
type="checkbox" />
<span
class="ant-checkbox-inner" />
</span>
</label>
<div
class="ant-table-selection-down ant-dropdown-trigger">
<span>
<i
class="anticon anticon-down" />
</span>
</div>
</div>
</span>
</th>
<th
class="">
<span>
Name
</span>
</th>
<th
class="">
<span>
Age
</span>
</th>
<th
class="">
<span>
Address
</span>
</th>
</tr>
</thead>
<tbody
class="ant-table-tbody">
<tr
class="ant-table-row ant-table-row-level-0">
<td
class="ant-table-selection-column">
<span>
<label
class="ant-checkbox-wrapper">
<span
class="ant-checkbox">
<input
class="ant-checkbox-input"
type="checkbox" />
<span
class="ant-checkbox-inner" />
</span>
</label>
</span>
</td>
<td
class="">
<span
class="ant-table-row-indent indent-level-0"
style="padding-left:0px;" />
Edward King 0
</td>
<td
class="">
32
</td>
<td
class="">
London, Park Lane no. 0
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0">
<td
class="ant-table-selection-column">
<span>
<label
class="ant-checkbox-wrapper">
<span
class="ant-checkbox">
<input
class="ant-checkbox-input"
type="checkbox" />
<span
class="ant-checkbox-inner" />
</span>
</label>
</span>
</td>
<td
class="">
<span
class="ant-table-row-indent indent-level-0"
style="padding-left:0px;" />
Edward King 1
</td>
<td
class="">
32
</td>
<td
class="">
London, Park Lane no. 1
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0">
<td
class="ant-table-selection-column">
<span>
<label
class="ant-checkbox-wrapper">
<span
class="ant-checkbox">
<input
class="ant-checkbox-input"
type="checkbox" />
<span
class="ant-checkbox-inner" />
</span>
</label>
</span>
</td>
<td
class="">
<span
class="ant-table-row-indent indent-level-0"
style="padding-left:0px;" />
Edward King 2
</td>
<td
class="">
32
</td>
<td
class="">
London, Park Lane no. 2
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0">
<td
class="ant-table-selection-column">
<span>
<label
class="ant-checkbox-wrapper">
<span
class="ant-checkbox">
<input
class="ant-checkbox-input"
type="checkbox" />
<span
class="ant-checkbox-inner" />
</span>
</label>
</span>
</td>
<td
class="">
<span
class="ant-table-row-indent indent-level-0"
style="padding-left:0px;" />
Edward King 3
</td>
<td
class="">
32
</td>
<td
class="">
London, Park Lane no. 3
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0">
<td
class="ant-table-selection-column">
<span>
<label
class="ant-checkbox-wrapper">
<span
class="ant-checkbox">
<input
class="ant-checkbox-input"
type="checkbox" />
<span
class="ant-checkbox-inner" />
</span>
</label>
</span>
</td>
<td
class="">
<span
class="ant-table-row-indent indent-level-0"
style="padding-left:0px;" />
Edward King 4
</td>
<td
class="">
32
</td>
<td
class="">
London, Park Lane no. 4
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0">
<td
class="ant-table-selection-column">
<span>
<label
class="ant-checkbox-wrapper">
<span
class="ant-checkbox">
<input
class="ant-checkbox-input"
type="checkbox" />
<span
class="ant-checkbox-inner" />
</span>
</label>
</span>
</td>
<td
class="">
<span
class="ant-table-row-indent indent-level-0"
style="padding-left:0px;" />
Edward King 5
</td>
<td
class="">
32
</td>
<td
class="">
London, Park Lane no. 5
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0">
<td
class="ant-table-selection-column">
<span>
<label
class="ant-checkbox-wrapper">
<span
class="ant-checkbox">
<input
class="ant-checkbox-input"
type="checkbox" />
<span
class="ant-checkbox-inner" />
</span>
</label>
</span>
</td>
<td
class="">
<span
class="ant-table-row-indent indent-level-0"
style="padding-left:0px;" />
Edward King 6
</td>
<td
class="">
32
</td>
<td
class="">
London, Park Lane no. 6
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0">
<td
class="ant-table-selection-column">
<span>
<label
class="ant-checkbox-wrapper">
<span
class="ant-checkbox">
<input
class="ant-checkbox-input"
type="checkbox" />
<span
class="ant-checkbox-inner" />
</span>
</label>
</span>
</td>
<td
class="">
<span
class="ant-table-row-indent indent-level-0"
style="padding-left:0px;" />
Edward King 7
</td>
<td
class="">
32
</td>
<td
class="">
London, Park Lane no. 7
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0">
<td
class="ant-table-selection-column">
<span>
<label
class="ant-checkbox-wrapper">
<span
class="ant-checkbox">
<input
class="ant-checkbox-input"
type="checkbox" />
<span
class="ant-checkbox-inner" />
</span>
</label>
</span>
</td>
<td
class="">
<span
class="ant-table-row-indent indent-level-0"
style="padding-left:0px;" />
Edward King 8
</td>
<td
class="">
32
</td>
<td
class="">
London, Park Lane no. 8
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0">
<td
class="ant-table-selection-column">
<span>
<label
class="ant-checkbox-wrapper">
<span
class="ant-checkbox">
<input
class="ant-checkbox-input"
type="checkbox" />
<span
class="ant-checkbox-inner" />
</span>
</label>
</span>
</td>
<td
class="">
<span
class="ant-table-row-indent indent-level-0"
style="padding-left:0px;" />
Edward King 9
</td>
<td
class="">
32
</td>
<td
class="">
London, Park Lane no. 9
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<ul
class="ant-pagination ant-table-pagination"
unselectable="unselectable">
<li
class="ant-pagination-disabled ant-pagination-prev"
title="上一页">
<a />
</li>
<li
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
title="1">
<a>
1
</a>
</li>
<li
class="ant-pagination-item ant-pagination-item-2"
title="2">
<a>
2
</a>
</li>
<li
class="ant-pagination-item ant-pagination-item-3"
title="3">
<a>
3
</a>
</li>
<li
class="ant-pagination-item ant-pagination-item-4"
title="4">
<a>
4
</a>
</li>
<li
class="ant-pagination-item ant-pagination-item-5"
title="5">
<a>
5
</a>
</li>
<li
class=" ant-pagination-next"
title="下一页">
<a />
</li>
</ul>
</div>
</div>
</div>
`;
exports[`test renders ./components/table/demo/size.md correctly 1`] = `
<div>
<h4>

View File

@ -119,7 +119,7 @@ class Demo extends React.Component {
return (
<div>
<div className="components-table-demo-control-bar">
<Form inline>
<Form layout="inline">
<FormItem label="Bordered">
<Switch checked={state.bordered} onChange={this.handleToggle('bordered')} />
</FormItem>

View File

@ -0,0 +1,90 @@
---
order: 4
title:
en-US: Custom selection
zh-CN: 自定义选择项
---
## zh-CN
默认有全选和反选,通过 `rowSelection.selections` 自定义选择项。
## en-US
Default selection is select all and select invert, Use `rowSelection.selections` custom selections.
````jsx
import { Table } from 'antd';
const columns = [{
title: 'Name',
dataIndex: 'name',
}, {
title: 'Age',
dataIndex: 'age',
}, {
title: 'Address',
dataIndex: 'address',
}];
const data = [];
for (let i = 0; i < 46; i++) {
data.push({
key: i,
name: `Edward King ${i}`,
age: 32,
address: `London, Park Lane no. ${i}`,
});
}
class App extends React.Component {
state = {
selectedRowKeys: [], // Check here to configure the default column
};
onSelectChange = (selectedRowKeys) => {
console.log('selectedRowKeys changed: ', selectedRowKeys);
this.setState({ selectedRowKeys });
}
render() {
const { selectedRowKeys } = this.state;
const rowSelection = {
selectedRowKeys,
onChange: this.onSelectChange,
selections: [{
key: 'odd',
text: '奇数项',
onSelect: (changableRowKeys) => {
let newSelectedRowKeys = [];
newSelectedRowKeys = changableRowKeys.filter((key, index) => {
if (index % 2 !== 0) {
return false;
}
return true;
});
this.setState({ selectedRowKeys: newSelectedRowKeys });
},
}, {
key: 'even',
text: '偶数项',
onSelect: (changableRowKeys) => {
let newSelectedRowKeys = [];
newSelectedRowKeys = changableRowKeys.filter((key, index) => {
if (index % 2 !== 0) {
return true;
}
return false;
});
this.setState({ selectedRowKeys: newSelectedRowKeys });
},
}],
onSelection: this.onSelection,
};
return (
<Table rowSelection={rowSelection} columns={columns} dataSource={data} />
);
}
}
ReactDOM.render(<App />, mountNode);
````

View File

@ -119,6 +119,16 @@ Properties for selection.
| getCheckboxProps | get Checkbox or Radio props | Function(record) | - |
| onSelect | callback that is called when select/deselect one row | Function(record, selected, selectedRows) | - |
| onSelectAll | callback that is called when select/deselect all | Function(selected, selectedRows, changeRows) | - |
| onSelectInvert | callback that is called when select invert | Function(selectedRows) | - |
| selections | custom selection, [config](#rowSelection) | object[] | - |
### selection
| Property | Description | Type | Default |
|---------------|--------------------------|-----------------|--------------|
| key | key of this selection | string | - |
| text | display text is this selection | string\|React.ReactNode | - |
| onSelect | callback when click this selection | Function(changeableRowKeys) | - |
## Using in TypeScript

View File

@ -120,6 +120,16 @@ const columns = [{
| getCheckboxProps | 选择框的默认属性配置 | Function(record) | - |
| onSelect | 用户手动选择/取消选择某列的回调 | Function(record, selected, selectedRows) | - |
| onSelectAll | 用户手动选择/取消选择所有列的回调 | Function(selected, selectedRows, changeRows) | - |
| onSelectInvert | 用户手动选择反选的回调 | Function(selectedRows) | - |
| selections | 自定义选择项, [配置项](#selection) | object[] | - |
### selection
| 参数 | 说明 | 类型 | 默认值 |
|------------------|--------------------------|-----------------|---------------------|---------|
| key | React 需要的 key建议设置 | string | - |
| text | 选择项显示的文字 | string\|React.ReactNode | - |
| onSelect | 选择项点击回调 | Function(changeableRowKeys) | - |
## 在 TypeScript 中使用

View File

@ -434,6 +434,36 @@
}
}
&-selection {
width: 40px;
.@{iconfont-css-prefix}-down {
color: #aaa;
}
&-menu {
min-width: 96px;
margin-top: 5px;
margin-left: -30px;
background: @component-background;
border-radius: @border-radius-base;
box-shadow: @box-shadow-base;
.@{ant-prefix}-dropdown-menu-item {
color: black;
}
.@{ant-prefix}-action-down {
color: #aaa;
}
}
&-down {
padding: 0;
display: inline-block;
}
}
&-row {
&-expand-icon {
cursor: pointer;

View File

@ -362,10 +362,9 @@ exports[`test renders ./components/tabs/demo/editable-card.md correctly 1`] = `
aria-selected="true"
class="ant-tabs-tab-active ant-tabs-tab"
role="tab">
<div>
<div
class="ant-tabs-tab-unclosable">
Tab 1
<i
class="anticon anticon-close" />
</div>
</div>
<div

View File

@ -8,10 +8,12 @@ title:
## zh-CN
只有卡片样式的页签支持新增和关闭选项。
使用 `closable={false}` 禁止关闭。
## en-US
Only card type Tabs support adding & closeable.
Only card type Tabs support adding & closable.
+Use `closable={false}` to disable close.
````jsx
import { Tabs } from 'antd';
@ -23,7 +25,7 @@ class Demo extends React.Component {
super(props);
this.newTabIndex = 0;
const panes = [
{ title: 'Tab 1', content: 'Content of Tab 1', key: '1' },
{ title: 'Tab 1', content: 'Content of Tab 1', key: '1', closable: false },
{ title: 'Tab 2', content: 'Content of Tab 2', key: '2' },
];
this.state = {
@ -66,7 +68,7 @@ class Demo extends React.Component {
type="editable-card"
onEdit={this.onEdit}
>
{this.state.panes.map(pane => <TabPane tab={pane.title} key={pane.key}>{pane.content}</TabPane>)}
{this.state.panes.map(pane => <TabPane tab={pane.title} key={pane.key} closable={pane.closable}>{pane.content}</TabPane>)}
</Tabs>
);
}

View File

@ -25,6 +25,7 @@ Ant Design has 3 types Tabs for different situation.
| onChange | Callback when tab is switched | Function | - |
| onTabClick | Callback when tab is clicked | Function | - |
| tabBarExtraContent | Extra element in tab bar | React.ReactNode | - |
| tabBarStyle | tar bar style object | object | - |
| type | Basic style of tabs. Options: line, card & editable-card | string | line |
| size | Tab bar size. Options: default, small. Only works while `type="line"`. | string | default |
| tabPosition | Position of tabs. Options: top, right, bottom & left | string | top |

View File

@ -19,6 +19,7 @@ export interface TabsProps {
onChange?: (activeKey: string) => void;
onTabClick?: Function;
tabBarExtraContent?: React.ReactNode | null;
tabBarStyle?: React.CSSProperties;
type?: TabsType;
tabPosition?: TabsPosition;
onEdit?: (targetKey: string, action: any) => void;
@ -34,6 +35,7 @@ export interface TabPaneProps {
/** 选项卡头显示文字 */
tab?: React.ReactNode | string;
style?: React.CSSProperties;
closable?: boolean;
className?: string;
disabled?: boolean;
}
@ -90,6 +92,7 @@ export default class Tabs extends React.Component<TabsProps, any> {
tabPosition,
children,
tabBarExtraContent,
tabBarStyle,
hideAdd,
onTabClick,
animated,
@ -110,11 +113,19 @@ export default class Tabs extends React.Component<TabsProps, any> {
if (type === 'editable-card') {
childrenWithClose = [];
React.Children.forEach(children as React.ReactNode, (child: React.ReactElement<any>, index) => {
let closable = child.props.closable;
closable = typeof closable === 'undefined' ? true : closable;
const closeIcon = closable ? (
<Icon
type="close"
onClick={e => this.removeTab(child.key, e)}
/>
) : null;
childrenWithClose.push(cloneElement(child, {
tab: (
<div>
<div className={closable ? undefined : `${prefixCls}-tab-unclosable`}>
{child.props.tab}
<Icon type="close" onClick={(e) => this.removeTab(child.key, e)} />
{closeIcon}
</div>
),
key: child.key || index,
@ -141,6 +152,7 @@ export default class Tabs extends React.Component<TabsProps, any> {
<ScrollableInkTabBar
extraContent={tabBarExtraContent}
onTabClick={onTabClick}
style={tabBarStyle}
/>
);

View File

@ -28,6 +28,7 @@ Ant Design 依次提供了三级选项卡,分别用于不同的场景。
| onChange | 切换面板的回调 | Function | 无 |
| onTabClick | tab 被点击的回调 | Function | 无 |
| tabBarExtraContent | tab bar 上额外的元素 | React.ReactNode | 无 |
| tabBarStyle | tar bar 的样式对象 | object | - |
| type | 页签的基本样式,可选 `line`、`card` `editable-card` 类型 | string | 'line' |
| size | 大小,提供 `default``small` 两种大小,仅当 `type="line"` 时生效。 | string | 'default' |
| tabPosition | 页签位置,可选值有 `top` `right` `bottom` `left` | string | 'top' |

View File

@ -52,9 +52,12 @@
}
}
&&-editable-card > &-bar &-tab:not(&-tab-active):hover {
padding-left: 8px;
padding-right: 8px;
&&-editable-card > &-bar &-tab > div {
transition: all 0.3s @ease-in-out;
}
&&-editable-card > &-bar &-tab:not(&-tab-active):hover > div:not(&-tab-unclosable) {
margin-left: -8px;
margin-right: -8px;
}
&&-card > &-bar &-tab-active .@{iconfont-css-prefix}-close,

View File

@ -0,0 +1,5 @@
const locale = {
placeholder: 'Seleccionar hora',
};
export default locale;

View File

@ -0,0 +1,5 @@
const locale = {
placeholder: 'Vybrat čas',
};
export default locale;

View File

@ -0,0 +1,5 @@
const locale = {
placeholder: '날짜 선택',
};
export default locale;

View File

@ -0,0 +1,5 @@
const locale = {
placeholder: 'Selecteer tijd',
};
export default locale;

Some files were not shown because too many files have changed in this diff Show More