Merge from "master"

This commit is contained in:
afc163 2017-02-20 12:18:41 +08:00
commit a9a564779e
139 changed files with 7871 additions and 7637 deletions

View File

@ -3,4 +3,3 @@ components/**/*.jsx
!.eslintrc.js !.eslintrc.js
!components/*/__tests__/* !components/*/__tests__/*
!components/*/demo/* !components/*/demo/*
!components/*/style/*

View File

@ -3,6 +3,7 @@
"rules": { "rules": {
"at-rule-empty-line-before": null, "at-rule-empty-line-before": null,
"at-rule-name-space-after": null, "at-rule-name-space-after": null,
"comment-empty-line-before": null,
"declaration-bang-space-before": null, "declaration-bang-space-before": null,
"declaration-empty-line-before": null, "declaration-empty-line-before": null,
"function-comma-newline-after": null, "function-comma-newline-after": null,

View File

@ -17,6 +17,36 @@ If you want to read change logs before `2.0.0`, please visit [GitHub](https://gi
--- ---
## 2.7.2
`2017-02-17`
- Fix that `antd.version` doesn't work as expected. [#4844](https://github.com/ant-design/ant-design/issues/4844)
- Fix that dist files don't include locales. [#4910](https://github.com/ant-design/ant-design/pull/4910)
- Fix that disabled option is selectable in search mode of Cascader. [#4699](https://github.com/ant-design/ant-design/issues/4699)
- **Button**
- Fix click animation of `Button[type=danger]`.
- Fix broken style with `loading`. [#4875](https://github.com/ant-design/ant-design/issues/4875)
- **Menu**
- Fix that `openKeys` should be controlled property in `vertical` mode. [#4876](https://github.com/ant-design/ant-design/issues/4876)
- Fix selected animation of Menu.Item.
- Fix broken style of Menu.SubMenu. [#4906](https://github.com/ant-design/ant-design/issues/4906)
- **Table**
- Fix broken style of table which use small size and fixed header. [#4850](https://github.com/ant-design/ant-design/issues/4850)
- Fix placeholder style. [#4851](https://github.com/ant-design/ant-design/pull/4851)
- Simplify DOM structure. [#4868](https://github.com/ant-design/ant-design/issues/4868)
- Fix that Radio should support number `0` as children. [#4874](https://github.com/ant-design/ant-design/issues/4874) [@HQidea](https://github.com/HQidea)
- Fix that RangePicker should work with `style.width` which is small than 300. [#4920](https://github.com/ant-design/ant-design/issues/4920)
- Fix CSS compile error caused by Spin. [#4915](https://github.com/ant-design/ant-design/issues/4915)
- Fix that Tooltip should work with disabled button in Chrome. [#4865](https://github.com/ant-design/ant-design/pull/4865)
- Fix UX of Tree while dragging. [#4858](https://github.com/ant-design/ant-design/issues/4858)
- Fix failed style of Upload. [#4810](https://github.com/ant-design/ant-design/issues/4810)
- Fix that `Menu[vertical]`'s SubMenu cannot popup in Layout.Sider. [#4890](https://github.com/ant-design/ant-design/issues/4890)
- Improve animation of Button and `Badge[status=processing]`.
![Badge animation](https://camo.githubusercontent.com/6874b2333f2fac3fac346404c6e70684e4dafc1a/68747470733a2f2f7a6f732e616c697061796f626a656374732e636f6d2f726d73706f7274616c2f73516b72756c716346734b4e54785158615971512e676966)
![Button animation](https://camo.githubusercontent.com/3963d12b45de4f522c2799361dbc3177e7bd93d1/68747470733a2f2f7a6f732e616c697061796f626a656374732e636f6d2f726d73706f7274616c2f46624b776d636f766d795364666c557468494e522e676966)
## 2.7.1 ## 2.7.1
`2017-02-10` `2017-02-10`

View File

@ -10,13 +10,43 @@ timeline: true
#### 发布周期 #### 发布周期
* patch 版本:每周末会进行日常 bugfix 更新。(如果有紧急的 bugfix则任何时候都可发布 * patch 版本:每周末会进行日常 bugfix 更新。(如果有紧急的 bugfix则任何时候都可发布
* minor 版本:每月发布一个带有新特性的版本。 * minor 版本:每月发布一个带有新特性的向下兼容的版本。
* 大版本号:含有破坏性更新和新特性,不在发布周期内。 * 大版本号:含有破坏性更新和新特性,不在发布周期内。
如果需要查看 `2.0.0` 之前的更新日志,请移步 [GitHub](https://github.com/ant-design/ant-design/blob/1.x-stable/CHANGELOG.md)。 如果需要查看 `2.0.0` 之前的更新日志,请移步 [GitHub](https://github.com/ant-design/ant-design/blob/1.x-stable/CHANGELOG.md)。
--- ---
## 2.7.2
`2017-02-17`
- 修复 `antd.version` 无法正常使用的问题。 [#4844](https://github.com/ant-design/ant-design/issues/4844)
- 修复 dist 文件没有 locales 的问题。 [#4910](https://github.com/ant-design/ant-design/pull/4910)
- 修复 Cascader 搜索模式下可以选择已禁用选项的问题。 [#4699](https://github.com/ant-design/ant-design/issues/4699)
- **Button**
- 修复 `Button[type=danger]` 的点击动画。
- 修复设置 `loading` 时的样式问题。 [#4875](https://github.com/ant-design/ant-design/issues/4875)
- **Menu**
- 修复 `vertical` 模式下 `openKeys` 为受控属性。 [#4876](https://github.com/ant-design/ant-design/issues/4876)
- 修复 Menu.Item 选中时的动画问题。
- 修复 Menu.SubMenu 的样式问题。 [#4906](https://github.com/ant-design/ant-design/issues/4906)
- **Table**
- 修复在混合使用固定表头和小尺寸时的样式问题。 [#4850](https://github.com/ant-design/ant-design/issues/4850)
- 修复无数据时的占位符样式问题。 [#4851](https://github.com/ant-design/ant-design/pull/4851)
- 精简了 DOM 结构。 [#4868](https://github.com/ant-design/ant-design/issues/4868)
- 修复 Radio 组件 children 无法为数字 `0` 的问题。 [#4874](https://github.com/ant-design/ant-design/issues/4874) [@HQidea](https://github.com/HQidea)
- 修复 RangePicker `style.width` 无法小于 300 的问题。 [#4920](https://github.com/ant-design/ant-design/issues/4920)
- 修复 Spin 样式在打包时会导致编译错误的问题。 [#4915](https://github.com/ant-design/ant-design/issues/4915)
- 修复 Chrome 下 Tooltip 无法在 disabled 的按钮上使用的问题。 [#4865](https://github.com/ant-design/ant-design/pull/4865)
- 修复 Tree 节点在拖动时会导致整棵树抖动的问题。 [#4858](https://github.com/ant-design/ant-design/issues/4858)
- 修复 Upload 上传失败的样式问题。 [#4810](https://github.com/ant-design/ant-design/issues/4810)
- 修复 `Menu[vertical]` 和 Layout.Sider 配合使用时二级菜单无法弹出的问题。 [#4890](https://github.com/ant-design/ant-design/issues/4890)
- 优化 Button、`Badge[status=processing]` 的动画。
![Badge animation](https://camo.githubusercontent.com/6874b2333f2fac3fac346404c6e70684e4dafc1a/68747470733a2f2f7a6f732e616c697061796f626a656374732e636f6d2f726d73706f7274616c2f73516b72756c716346734b4e54785158615971512e676966)
![Button animation](https://camo.githubusercontent.com/3963d12b45de4f522c2799361dbc3177e7bd93d1/68747470733a2f2f7a6f732e616c697061796f626a656374732e636f6d2f726d73706f7274616c2f46624b776d636f766d795364666c557468494e522e676966)
## 2.7.1 ## 2.7.1
`2017-02-10` `2017-02-10`

View File

@ -81,7 +81,7 @@ tsconfig.json
- [React 基础组件](http://react-component.github.io/) - [React 基础组件](http://react-component.github.io/)
- [移动端组件](http://mobile.ant.design) - [移动端组件](http://mobile.ant.design)
- [动效](https://motion.ant.design) - [动效](https://motion.ant.design)
- [设计规范速查手册](https://os.alipayobjects.com/rmsportal/HTXUgPGkyyxEivE.png) - [设计规范速查手册](https://github.com/ant-design/ant-design/wiki/Ant-Design-%E8%AE%BE%E8%AE%A1%E5%9F%BA%E7%A1%80%E7%AE%80%E7%89%88)
- [开发者说明](https://github.com/ant-design/ant-design/wiki/Development) - [开发者说明](https://github.com/ant-design/ant-design/wiki/Development)
- [版本发布规则](https://github.com/ant-design/ant-design/wiki/%E8%BD%AE%E5%80%BC%E8%A7%84%E5%88%99%E5%92%8C%E7%89%88%E6%9C%AC%E5%8F%91%E5%B8%83%E6%B5%81%E7%A8%8B) - [版本发布规则](https://github.com/ant-design/ant-design/wiki/%E8%BD%AE%E5%80%BC%E8%A7%84%E5%88%99%E5%92%8C%E7%89%88%E6%9C%AC%E5%8F%91%E5%B8%83%E6%B5%81%E7%A8%8B)
- [社区贡献脚手架和范例](https://github.com/ant-design/ant-design/issues/129) - [社区贡献脚手架和范例](https://github.com/ant-design/ant-design/issues/129)

View File

@ -20,13 +20,12 @@ function onSelect(value) {
console.log('onSelect', value); console.log('onSelect', value);
} }
const Complete = React.createClass({ class Complete extends React.Component {
getInitialState() { state = {
return { dataSource: [],
dataSource: [], }
};
}, handleChange = (value) => {
handleChange(value) {
this.setState({ this.setState({
dataSource: !value ? [] : [ dataSource: !value ? [] : [
value, value,
@ -34,10 +33,12 @@ const Complete = React.createClass({
value + value + value, value + value + value,
], ],
}); });
}, }
handleKeyPress(ev) {
handleKeyPress = (ev) => {
console.log('handleKeyPress', ev); console.log('handleKeyPress', ev);
}, }
render() { render() {
const { dataSource } = this.state; const { dataSource } = this.state;
return ( return (
@ -51,8 +52,8 @@ const Complete = React.createClass({
<textarea onKeyPress={this.handleKeyPress} style={{ height: 50 }} /> <textarea onKeyPress={this.handleKeyPress} style={{ height: 50 }} />
</AutoComplete> </AutoComplete>
); );
}, }
}); }
ReactDOM.render(<Complete />, mountNode); ReactDOM.render(<Complete />, mountNode);
```` ````

View File

@ -20,13 +20,12 @@ function onSelect(value) {
console.log('onSelect', value); console.log('onSelect', value);
} }
const Complete = React.createClass({ class Complete extends React.Component {
getInitialState() { state = {
return { dataSource: [],
dataSource: [], }
};
}, handleChange = (value) => {
handleChange(value) {
this.setState({ this.setState({
dataSource: !value ? [] : [ dataSource: !value ? [] : [
value, value,
@ -34,7 +33,8 @@ const Complete = React.createClass({
value + value + value, value + value + value,
], ],
}); });
}, }
render() { render() {
const { dataSource } = this.state; const { dataSource } = this.state;
return ( return (
@ -46,8 +46,8 @@ const Complete = React.createClass({
placeholder="input here" placeholder="input here"
/> />
); );
}, }
}); }
ReactDOM.render(<Complete />, mountNode); ReactDOM.render(<Complete />, mountNode);
```` ````

View File

@ -19,12 +19,14 @@ import { AutoComplete } from 'antd';
const dataSource = ['Burns Bay Road', 'Downing Street', 'Wall Street']; const dataSource = ['Burns Bay Road', 'Downing Street', 'Wall Street'];
function Complete() { function Complete() {
return (<AutoComplete return (
style={{ width: 200 }} <AutoComplete
dataSource={dataSource} style={{ width: 200 }}
placeholder="try to type `b`" dataSource={dataSource}
filterOption={(inputValue, option) => option.props.children.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1} placeholder="try to type `b`"
/>); filterOption={(inputValue, option) => option.props.children.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1}
/>
);
} }
ReactDOM.render(<Complete />, mountNode); ReactDOM.render(<Complete />, mountNode);

View File

@ -18,13 +18,12 @@ import { AutoComplete } from 'antd';
const Option = AutoComplete.Option; const Option = AutoComplete.Option;
const Complete = React.createClass({ class Complete extends React.Component {
getInitialState() { state = {
return { result: [],
result: [], }
};
}, handleChange = (value) => {
handleChange(value) {
let result; let result;
if (!value || value.indexOf('@') >= 0) { if (!value || value.indexOf('@') >= 0) {
result = []; result = [];
@ -32,7 +31,8 @@ const Complete = React.createClass({
result = ['gmail.com', '163.com', 'qq.com'].map(domain => `${value}@${domain}`); result = ['gmail.com', '163.com', 'qq.com'].map(domain => `${value}@${domain}`);
} }
this.setState({ result }); this.setState({ result });
}, }
render() { render() {
const { result } = this.state; const { result } = this.state;
const children = result.map((email) => { const children = result.map((email) => {
@ -47,8 +47,8 @@ const Complete = React.createClass({
{children} {children}
</AutoComplete> </AutoComplete>
); );
}, }
}); }
ReactDOM.render(<Complete />, mountNode); ReactDOM.render(<Complete />, mountNode);
```` ````

View File

@ -47,24 +47,24 @@ function renderOption(item) {
{item.category} {item.category}
</a> </a>
区块中 区块中
<span style={{ float: 'right' }}>约 {item.count} 个结果</span> <span className="global-search-item-count">约 {item.count} 个结果</span>
</Option> </Option>
); );
} }
const Complete = React.createClass({ class Complete extends React.Component {
getInitialState() { state = {
return { dataSource: [],
dataSource: [], }
};
}, handleChange = (value) => {
handleChange(value) {
if (value) { if (value) {
this.setState({ this.setState({
dataSource: searchResult(value), dataSource: searchResult(value),
}); });
} }
}, }
render() { render() {
const { dataSource } = this.state; const { dataSource } = this.state;
return ( return (
@ -89,8 +89,8 @@ const Complete = React.createClass({
</AutoComplete> </AutoComplete>
</div> </div>
); );
}, }
}); }
ReactDOM.render(<Complete />, mountNode); ReactDOM.render(<Complete />, mountNode);
```` ````
@ -128,11 +128,17 @@ ReactDOM.render(<Complete />, mountNode);
} }
.global-search.ant-select-auto-complete .ant-input-preSuffix-wrapper .ant-input-suffix { .global-search.ant-select-auto-complete .ant-input-preSuffix-wrapper .ant-input-suffix {
right: 0; right: 1px;
} }
.global-search.ant-select-auto-complete .ant-input-preSuffix-wrapper .ant-input-suffix button { .global-search.ant-select-auto-complete .ant-input-preSuffix-wrapper .ant-input-suffix button {
border-radius: 3px;
border-top-left-radius: 0; border-top-left-radius: 0;
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
} }
.global-search-item-count {
position: absolute;
right: 16px;
}
```` ````

View File

@ -1,7 +1,7 @@
--- ---
category: Components category: Components
type: Data Entry type: Data Entry
cols: 1 cols: 2
title: AutoComplete title: AutoComplete
--- ---

View File

@ -39,17 +39,18 @@ export interface AutoCompleteProps extends AbstractSelectProps {
} }
class InputElement extends React.Component<any, any> { class InputElement extends React.Component<any, any> {
private ele: Element; private ele: HTMLInputElement;
focus = () => { focus = () => {
(findDOMNode(this.ele) as HTMLInputElement).focus(); this.ele.focus ? this.ele.focus() : (findDOMNode(this.ele) as HTMLInputElement).focus();
} }
blur = () => { blur = () => {
(findDOMNode(this.ele) as HTMLInputElement).blur(); this.ele.blur ? this.ele.blur() : (findDOMNode(this.ele) as HTMLInputElement).blur();
} }
render() { render() {
return React.cloneElement(this.props.children, { return React.cloneElement(this.props.children, {
...this.props, ...this.props,
ref: ele => this.ele = ele, ref: ele => this.ele = (ele as HTMLInputElement),
}, null); }, null);
} }
} }

View File

@ -17,27 +17,29 @@ The count will be animated as it changes.
import { Badge, Button, Icon, Switch } from 'antd'; import { Badge, Button, Icon, Switch } from 'antd';
const ButtonGroup = Button.Group; const ButtonGroup = Button.Group;
const Test = React.createClass({ class Demo extends React.Component {
getInitialState() { state = {
return { count: 5,
count: 5, show: true,
show: true, }
};
}, increase = () => {
increase() {
const count = this.state.count + 1; const count = this.state.count + 1;
this.setState({ count }); this.setState({ count });
}, }
decline() {
decline = () => {
let count = this.state.count - 1; let count = this.state.count - 1;
if (count < 0) { if (count < 0) {
count = 0; count = 0;
} }
this.setState({ count }); this.setState({ count });
}, }
onChange(show) {
onChange = (show) => {
this.setState({ show }); this.setState({ show });
}, }
render() { render() {
return ( return (
<div> <div>
@ -62,8 +64,8 @@ const Test = React.createClass({
</div> </div>
</div> </div>
); );
}, }
}); }
ReactDOM.render(<Test />, mountNode); ReactDOM.render(<Demo />, mountNode);
```` ````

View File

@ -16,14 +16,16 @@ This will simply display a red badge, without a specific count.
````jsx ````jsx
import { Badge, Icon } from 'antd'; import { Badge, Icon } from 'antd';
ReactDOM.render(<div> ReactDOM.render(
<Badge dot> <div>
<Icon type="notification" /> <Badge dot>
</Badge> <Icon type="notification" />
<Badge dot> </Badge>
<a href="#">Link something</a> <Badge dot>
</Badge> <a href="#">Link something</a>
</div>, mountNode); </Badge>
</div>
, mountNode);
```` ````
<style> <style>

View File

@ -18,9 +18,11 @@ Used in standalone when children is empty.
````jsx ````jsx
import { Badge } from 'antd'; import { Badge } from 'antd';
ReactDOM.render(<div> ReactDOM.render(
<Badge count={25} /> <div>
<Badge count={4} style={{ backgroundColor: '#fff', color: '#999', boxShadow: '0 0 0 1px #d9d9d9 inset' }} /> <Badge count={25} />
<Badge count={109} style={{ backgroundColor: '#87d068' }} /> <Badge count={4} style={{ backgroundColor: '#fff', color: '#999', boxShadow: '0 0 0 1px #d9d9d9 inset' }} />
</div>, mountNode); <Badge count={109} style={{ backgroundColor: '#87d068' }} />
</div>
, mountNode);
```` ````

View File

@ -16,18 +16,20 @@ title:
````jsx ````jsx
import { Badge } from 'antd'; import { Badge } from 'antd';
ReactDOM.render(<div> ReactDOM.render(
<Badge count={99}> <div>
<a href="#" className="head-example" /> <Badge count={99}>
</Badge> <a href="#" className="head-example" />
<Badge count={100}> </Badge>
<a href="#" className="head-example" /> <Badge count={100}>
</Badge> <a href="#" className="head-example" />
<Badge count={99} overflowCount={10}> </Badge>
<a href="#" className="head-example" /> <Badge count={99} overflowCount={10}>
</Badge> <a href="#" className="head-example" />
<Badge count={1000} overflowCount={999}> </Badge>
<a href="#" className="head-example" /> <Badge count={1000} overflowCount={999}>
</Badge> <a href="#" className="head-example" />
</div>, mountNode); </Badge>
</div>
, mountNode);
```` ````

View File

@ -68,7 +68,7 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 50%; border-radius: 50%;
background-color: @primary-color; border: 1px solid @primary-color;
content: ''; content: '';
animation: antStatusProcessing 1.2s infinite ease-in-out; animation: antStatusProcessing 1.2s infinite ease-in-out;
} }

View File

@ -351,6 +351,19 @@ exports[`test renders ./components/button/demo/loading.md correctly 1`] = `
Click me! Click me!
</span> </span>
</button> </button>
<br />
<button
class="ant-btn ant-btn-circle ant-btn-loading"
type="button">
<i
class="anticon anticon-spin anticon-loading" />
</button>
<button
class="ant-btn ant-btn-primary ant-btn-circle ant-btn-loading"
type="button">
<i
class="anticon anticon-spin anticon-loading" />
</button>
</div> </div>
`; `;

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { render } from 'enzyme'; import { render, mount } from 'enzyme';
import { renderToJson } from 'enzyme-to-json'; import { renderToJson } from 'enzyme-to-json';
import Button from '..'; import Button from '..';
@ -17,4 +17,12 @@ describe('Button', () => {
); );
expect(renderToJson(wrapper)).toMatchSnapshot(); expect(renderToJson(wrapper)).toMatchSnapshot();
}); });
it('have static perperty for type detecting', () => {
const wrapper = mount(
<Button>Button Text</Button>
);
// eslint-disable-next-line
expect(wrapper.type().__ANT_BUTTON).toBe(true);
});
}); });

View File

@ -46,6 +46,7 @@ export interface ButtonProps {
export default class Button extends React.Component<ButtonProps, any> { export default class Button extends React.Component<ButtonProps, any> {
static Group: any; static Group: any;
static __ANT_BUTTON = true;
static defaultProps = { static defaultProps = {
prefixCls: 'ant-btn', prefixCls: 'ant-btn',

View File

@ -16,19 +16,20 @@ A loading indicator can be added to a button by setting the `loading` property o
````jsx ````jsx
import { Button } from 'antd'; import { Button } from 'antd';
const App = React.createClass({ class App extends React.Component {
getInitialState() { state = {
return { loading: false,
loading: false, iconLoading: false,
iconLoading: false, }
};
}, enterLoading = () => {
enterLoading() {
this.setState({ loading: true }); this.setState({ loading: true });
}, }
enterIconLoading() {
enterIconLoading = () => {
this.setState({ iconLoading: true }); this.setState({ iconLoading: true });
}, }
render() { render() {
return ( return (
<div> <div>
@ -45,10 +46,13 @@ const App = React.createClass({
<Button type="primary" icon="poweroff" loading={this.state.iconLoading} onClick={this.enterIconLoading}> <Button type="primary" icon="poweroff" loading={this.state.iconLoading} onClick={this.enterIconLoading}>
Click me! Click me!
</Button> </Button>
<br />
<Button shape="circle" loading />
<Button type="primary" shape="circle" loading />
</div> </div>
); );
}, }
}); }
ReactDOM.render(<App />, mountNode); ReactDOM.render(<App />, mountNode);
```` ````

View File

@ -78,20 +78,24 @@
display: none; display: none;
} }
&&-loading { &&-loading:before {
display: block;
}
.@{iconfont-css-prefix} {
transition: all .3s @ease-in-out;
}
&&-loading:not(&-circle):not(&-circle-outline) {
padding-left: 29px; padding-left: 29px;
pointer-events: none; pointer-events: none;
position: relative; position: relative;
.@{iconfont-css-prefix} { .@{iconfont-css-prefix} {
margin-left: -14px; margin-left: -14px;
transition: all .3s @ease-in-out;
}
&:before {
display: block;
} }
} }
&-sm&-loading { &-sm&-loading:not(&-circle):not(&-circle-outline) {
padding-left: 24px; padding-left: 24px;
.@{iconfont-css-prefix} { .@{iconfont-css-prefix} {
margin-left: -17px; margin-left: -17px;
@ -121,12 +125,16 @@
bottom: -1px; bottom: -1px;
right: -1px; right: -1px;
border-radius: inherit; border-radius: inherit;
border: 0 solid @primary-color; border: 1.5px solid @primary-color;
opacity: 0.4; opacity: 0.4;
animation: buttonEffect 0.36s ease-out forwards; animation: buttonEffect 0.4s ease-in-out forwards;
display: block; display: block;
} }
&-danger&-clicked:after {
border-color: @btn-danger-bg;
}
&-background-ghost { &-background-ghost {
background: transparent!important; background: transparent!important;
border-color: #fff; border-color: #fff;
@ -145,6 +153,5 @@
left: -6px; left: -6px;
bottom: -6px; bottom: -6px;
right: -6px; right: -6px;
border-width: 6px;
} }
} }

View File

@ -32,17 +32,16 @@ const options = [{
}], }],
}]; }];
const CitySwitcher = React.createClass({ class CitySwitcher extends React.Component {
getInitialState() { state = {
return { text: 'Unselect',
text: 'Unselect', };
};
}, onChange = (value, selectedOptions) => {
onChange(value, selectedOptions) {
this.setState({ this.setState({
text: selectedOptions.map(o => o.label).join(', '), text: selectedOptions.map(o => o.label).join(', '),
}); });
}, }
render() { render() {
return ( return (
<span> <span>
@ -53,8 +52,8 @@ const CitySwitcher = React.createClass({
</Cascader> </Cascader>
</span> </span>
); );
}, }
}); }
ReactDOM.render(<CitySwitcher />, mountNode); ReactDOM.render(<CitySwitcher />, mountNode);
```` ````

View File

@ -20,14 +20,12 @@ const CheckboxGroup = Checkbox.Group;
const plainOptions = ['Apple', 'Pear', 'Orange']; const plainOptions = ['Apple', 'Pear', 'Orange'];
const defaultCheckedList = ['Apple', 'Orange']; const defaultCheckedList = ['Apple', 'Orange'];
const App = React.createClass({ class App extends React.Component {
getInitialState() { state = {
return { checkedList: defaultCheckedList,
checkedList: defaultCheckedList, indeterminate: true,
indeterminate: true, checkAll: false,
checkAll: false, };
};
},
render() { render() {
return ( return (
<div> <div>
@ -44,22 +42,22 @@ const App = React.createClass({
<CheckboxGroup options={plainOptions} value={this.state.checkedList} onChange={this.onChange} /> <CheckboxGroup options={plainOptions} value={this.state.checkedList} onChange={this.onChange} />
</div> </div>
); );
}, }
onChange(checkedList) { onChange = (checkedList) => {
this.setState({ this.setState({
checkedList, checkedList,
indeterminate: !!checkedList.length && (checkedList.length < plainOptions.length), indeterminate: !!checkedList.length && (checkedList.length < plainOptions.length),
checkAll: checkedList.length === plainOptions.length, checkAll: checkedList.length === plainOptions.length,
}); });
}, }
onCheckAllChange(e) { onCheckAllChange = (e) => {
this.setState({ this.setState({
checkedList: e.target.checked ? plainOptions : [], checkedList: e.target.checked ? plainOptions : [],
indeterminate: false, indeterminate: false,
checkAll: e.target.checked, checkAll: e.target.checked,
}); });
}, }
}); }
ReactDOM.render(<App />, mountNode); ReactDOM.render(<App />, mountNode);
```` ````

View File

@ -16,13 +16,11 @@ Communicated with other components.
````jsx ````jsx
import { Checkbox, Button } from 'antd'; import { Checkbox, Button } from 'antd';
const App = React.createClass({ class App extends React.Component {
getInitialState() { state = {
return { checked: true,
checked: true, disabled: false,
disabled: false, };
};
},
render() { render() {
const label = `${this.state.checked ? 'Checked' : 'Unchecked'}-${this.state.disabled ? 'Disabled' : 'Enabled'}`; const label = `${this.state.checked ? 'Checked' : 'Unchecked'}-${this.state.disabled ? 'Disabled' : 'Enabled'}`;
return ( return (
@ -50,20 +48,20 @@ const App = React.createClass({
</p> </p>
</div> </div>
); );
}, }
toggleChecked() { toggleChecked = () => {
this.setState({ checked: !this.state.checked }); this.setState({ checked: !this.state.checked });
}, }
toggleDisable() { toggleDisable = () => {
this.setState({ disabled: !this.state.disabled }); this.setState({ disabled: !this.state.disabled });
}, }
onChange(e) { onChange = (e) => {
console.log('checked = ', e.target.checked); console.log('checked = ', e.target.checked);
this.setState({ this.setState({
checked: e.target.checked, checked: e.target.checked,
}); });
}, }
}); }
ReactDOM.render(<App />, mountNode); ReactDOM.render(<App />, mountNode);
```` ````

View File

@ -16,9 +16,11 @@ Disabled checkbox.
````jsx ````jsx
import { Checkbox } from 'antd'; import { Checkbox } from 'antd';
ReactDOM.render(<div> ReactDOM.render(
<Checkbox defaultChecked={false} disabled /> <div>
<br /> <Checkbox defaultChecked={false} disabled />
<Checkbox defaultChecked disabled /> <br />
</div>, mountNode); <Checkbox defaultChecked disabled />
</div>
, mountNode);
```` ````

View File

@ -4,15 +4,15 @@
@collapse-prefix-cls: ~"@{ant-prefix}-collapse"; @collapse-prefix-cls: ~"@{ant-prefix}-collapse";
.collapse-close() { .collapse-close() {
.iconfont-size-under-12px(7px, 270deg); .iconfont-size-under-12px(9px, 0);
} }
.collapse-open() { .collapse-open() {
.iconfont-size-under-12px(7px, 360deg); .iconfont-size-under-12px(9px, 90deg);
} }
.@{collapse-prefix-cls} { .@{collapse-prefix-cls} {
background-color: @background-color-base; background-color: @background-color-base;
border-radius: 3px; border-radius: @border-radius-base;
border: @border-width-base @border-style-base @border-color-base; border: @border-width-base @border-style-base @border-color-base;
border-bottom: 0; border-bottom: 0;
@ -25,18 +25,22 @@
color: @text-color; color: @text-color;
cursor: pointer; cursor: pointer;
position: relative; position: relative;
transition: all .3s;
&:active {
background-color: #eee!important;
}
.arrow { .arrow {
.collapse-close(); .collapse-close();
.iconfont-mixin(); .iconfont-mixin();
position: absolute; position: absolute;
color: @text-color; color: @text-color-secondary;
display: inline-block; display: inline-block;
margin-right: 8px; font-weight: bold;
line-height: 40px; line-height: 40px;
content: "\e606";
vertical-align: middle; vertical-align: middle;
transition: transform 0.24s ease; transition: transform 0.24s;
top: 0; top: 0;
left: 16px; left: 16px;
/* stylelint-disable declaration-block-no-duplicate-properties */ /* stylelint-disable declaration-block-no-duplicate-properties */
@ -44,7 +48,7 @@
left: ~"0 \9"; left: ~"0 \9";
/* stylelint-enable declaration-block-no-duplicate-properties */ /* stylelint-enable declaration-block-no-duplicate-properties */
&:before { &:before {
content: "\e606"; content: "\E61F";
} }
} }
} }
@ -72,7 +76,7 @@
&-item:last-child { &-item:last-child {
> .@{collapse-prefix-cls}-content { > .@{collapse-prefix-cls}-content {
border-radius: 0 0 3px 3px; border-radius: 0 0 @border-radius-base @border-radius-base;
} }
} }

View File

@ -165,7 +165,7 @@ export default class RangePicker extends React.Component<any, any> {
// default width for showTime // default width for showTime
const pickerStyle = {} as any; const pickerStyle = {} as any;
if (props.showTime) { if (props.showTime) {
pickerStyle.minWidth = 300; pickerStyle.width = (style && style.width) || 300;
} }
const clearIcon = (!props.disabled && props.allowClear && value && (value[0] || value[1])) const clearIcon = (!props.disabled && props.allowClear && value && (value[0] || value[1]))

View File

@ -18,7 +18,7 @@ exports[`test renders ./components/date-picker/demo/basic.md correctly 1`] = `
<span> <span>
<input <input
class="ant-calendar-picker-input ant-input" class="ant-calendar-picker-input ant-input"
placeholder="请选择日期" placeholder="Select month"
readonly="" readonly=""
value="" /> value="" />
<span <span
@ -113,7 +113,7 @@ exports[`test renders ./components/date-picker/demo/disabled-date.md correctly 1
<div> <div>
<span <span
class="ant-calendar-picker" class="ant-calendar-picker"
style="min-width:154px;"> style="width:154px;">
<span> <span>
<input <input
class="ant-calendar-picker-input ant-input" class="ant-calendar-picker-input ant-input"
@ -127,7 +127,7 @@ exports[`test renders ./components/date-picker/demo/disabled-date.md correctly 1
<br /> <br />
<span <span
class="ant-calendar-picker" class="ant-calendar-picker"
style="min-width:300px;"> style="width:300px;">
<span <span
class="ant-calendar-picker-input ant-input"> class="ant-calendar-picker-input ant-input">
<input <input
@ -213,7 +213,7 @@ exports[`test renders ./components/date-picker/demo/format.md correctly 1`] = `
exports[`test renders ./components/date-picker/demo/locale.md correctly 1`] = ` exports[`test renders ./components/date-picker/demo/locale.md correctly 1`] = `
<span <span
class="ant-calendar-picker" class="ant-calendar-picker"
style="min-width:154px;"> style="width:154px;">
<span> <span>
<input <input
class="ant-calendar-picker-input ant-input" class="ant-calendar-picker-input ant-input"
@ -255,7 +255,7 @@ exports[`test renders ./components/date-picker/demo/presetted-ranges.md correctl
<br /> <br />
<span <span
class="ant-calendar-picker" class="ant-calendar-picker"
style="min-width:300px;"> style="width:300px;">
<span <span
class="ant-calendar-picker-input ant-input"> class="ant-calendar-picker-input ant-input">
<input <input
@ -384,7 +384,7 @@ exports[`test renders ./components/date-picker/demo/start-end.md correctly 1`] =
<div> <div>
<span <span
class="ant-calendar-picker" class="ant-calendar-picker"
style="min-width:154px;"> style="width:154px;">
<span> <span>
<input <input
class="ant-calendar-picker-input ant-input" class="ant-calendar-picker-input ant-input"
@ -397,7 +397,7 @@ exports[`test renders ./components/date-picker/demo/start-end.md correctly 1`] =
</span> </span>
<span <span
class="ant-calendar-picker" class="ant-calendar-picker"
style="min-width:154px;"> style="width:154px;">
<span> <span>
<input <input
class="ant-calendar-picker-input ant-input" class="ant-calendar-picker-input ant-input"
@ -415,7 +415,7 @@ exports[`test renders ./components/date-picker/demo/time.md correctly 1`] = `
<div> <div>
<span <span
class="ant-calendar-picker" class="ant-calendar-picker"
style="min-width:154px;"> style="width:154px;">
<span> <span>
<input <input
class="ant-calendar-picker-input ant-input" class="ant-calendar-picker-input ant-input"
@ -429,7 +429,7 @@ exports[`test renders ./components/date-picker/demo/time.md correctly 1`] = `
<br /> <br />
<span <span
class="ant-calendar-picker" class="ant-calendar-picker"
style="min-width:300px;"> style="width:300px;">
<span <span
class="ant-calendar-picker-input ant-input"> class="ant-calendar-picker-input ant-input">
<input <input

View File

@ -143,7 +143,7 @@ export default function createPicker(TheCalendar) {
// default width for showTime // default width for showTime
const pickerStyle = {} as any; const pickerStyle = {} as any;
if (props.showTime) { if (props.showTime) {
pickerStyle.minWidth = 154; pickerStyle.width = (props.style && props.style.width) || 154;
} }
const clearIcon = (!props.disabled && props.allowClear && value) ? const clearIcon = (!props.disabled && props.allowClear && value) ?

View File

@ -25,7 +25,7 @@ ReactDOM.render(
<div> <div>
<DatePicker onChange={onChange} /> <DatePicker onChange={onChange} />
<br /> <br />
<MonthPicker onChange={onChange} /> <MonthPicker onChange={onChange} placeholder="Select month" />
<br /> <br />
<RangePicker onChange={onChange} /> <RangePicker onChange={onChange} />
</div> </div>

View File

@ -16,20 +16,18 @@ The default is to close the menu when you click on menu items, this feature can
````jsx ````jsx
import { Menu, Dropdown, Icon } from 'antd'; import { Menu, Dropdown, Icon } from 'antd';
const OverlayVisible = React.createClass({ class OverlayVisible extends React.Component {
getInitialState() { state = {
return { visible: false,
visible: false, };
}; handleMenuClick = (e) => {
},
handleMenuClick(e) {
if (e.key === '3') { if (e.key === '3') {
this.setState({ visible: false }); this.setState({ visible: false });
} }
}, }
handleVisibleChange(flag) { handleVisibleChange = (flag) => {
this.setState({ visible: flag }); this.setState({ visible: flag });
}, }
render() { render() {
const menu = ( const menu = (
<Menu onClick={this.handleMenuClick}> <Menu onClick={this.handleMenuClick}>
@ -48,8 +46,8 @@ const OverlayVisible = React.createClass({
</a> </a>
</Dropdown> </Dropdown>
); );
}, }
}); }
ReactDOM.render(<OverlayVisible />, mountNode); ReactDOM.render(<OverlayVisible />, mountNode);
```` ````

View File

@ -58,13 +58,14 @@
color: @text-color; color: @text-color;
white-space: nowrap; white-space: nowrap;
cursor: pointer; cursor: pointer;
transition: background 0.3s ease; transition: all .3s;
> a { > a {
color: @text-color; color: @text-color;
display: block; display: block;
padding: 7px 16px; padding: 7px 16px;
margin: -7px -16px; margin: -7px -16px;
transition: all .3s;
} }
&:hover { &:hover {
@ -83,15 +84,18 @@
} }
} }
&:first-child { &:first-child,
&:first-child > a {
border-radius: @border-radius-base @border-radius-base 0 0; border-radius: @border-radius-base @border-radius-base 0 0;
} }
&:last-child { &:last-child,
&:last-child > a {
border-radius: 0 0 @border-radius-base @border-radius-base; border-radius: 0 0 @border-radius-base @border-radius-base;
} }
&:only-child { &:only-child,
&:only-child > a {
border-radius: @border-radius-base; border-radius: @border-radius-base;
} }
@ -182,3 +186,31 @@
.iconfont-size-under-12px(10px); .iconfont-size-under-12px(10px);
} }
} }
// https://github.com/ant-design/ant-design/issues/4903
.@{dropdown-prefix-cls}-menu-dark {
&,
.@{dropdown-prefix-cls}-menu {
background: @menu-dark-bg;
}
.@{dropdown-prefix-cls}-menu-item,
.@{dropdown-prefix-cls}-menu-submenu-title,
.@{dropdown-prefix-cls}-menu-item > a {
color: @text-color-secondary-dark;
&:after {
color: @text-color-secondary-dark;
}
&:hover {
color: #fff;
background: transparent;
}
}
.@{dropdown-prefix-cls}-menu-item-selected {
&,
&:hover,
> a {
background: @primary-color;
color: #fff;
}
}
}

View File

@ -1086,7 +1086,7 @@ exports[`test renders ./components/form/demo/time-related-controls.md correctly
class="ant-form-item-control "> class="ant-form-item-control ">
<span <span
class="ant-calendar-picker" class="ant-calendar-picker"
style="min-width:154px;"> style="width:154px;">
<span> <span>
<input <input
class="ant-calendar-picker-input ant-input ant-input-lg" class="ant-calendar-picker-input ant-input ant-input-lg"
@ -1184,7 +1184,7 @@ exports[`test renders ./components/form/demo/time-related-controls.md correctly
class="ant-form-item-control "> class="ant-form-item-control ">
<span <span
class="ant-calendar-picker" class="ant-calendar-picker"
style="min-width:300px;"> style="width:300px;">
<span <span
class="ant-calendar-picker-input ant-input ant-input-lg"> class="ant-calendar-picker-input ant-input ant-input-lg">
<input <input
@ -1466,7 +1466,7 @@ exports[`test renders ./components/form/demo/validate-other.md correctly 1`] = `
<div <div
class="ant-slider-rail" /> class="ant-slider-rail" />
<div <div
class="ant-slider-track ant-slider-track-1" class="ant-slider-track"
style="visibility:visible;left:0%;width:0%;" /> style="visibility:visible;left:0%;width:0%;" />
<div <div
class="ant-slider-step"> class="ant-slider-step">
@ -1490,7 +1490,7 @@ exports[`test renders ./components/form/demo/validate-other.md correctly 1`] = `
style="left:100%;" /> style="left:100%;" />
</div> </div>
<div <div
class="ant-slider-handle ant-slider-handle-2 ant-slider-handle-upper" class="ant-slider-handle"
style="left:0%;" /> style="left:0%;" />
<div <div
class="ant-slider-mark"> class="ant-slider-mark">

View File

@ -18,21 +18,21 @@ import { Form, Select, Input, Button } from 'antd';
const FormItem = Form.Item; const FormItem = Form.Item;
const Option = Select.Option; const Option = Select.Option;
const App = Form.create()(React.createClass({ class App extends React.Component {
handleSubmit(e) { handleSubmit = (e) => {
e.preventDefault(); e.preventDefault();
this.props.form.validateFields((err, values) => { this.props.form.validateFields((err, values) => {
if (!err) { if (!err) {
console.log('Received values of form: ', values); console.log('Received values of form: ', values);
} }
}); });
}, }
handleSelectChange(value) { handleSelectChange = (value) => {
console.log(value); console.log(value);
this.props.form.setFieldsValue({ this.props.form.setFieldsValue({
note: `Hi, ${value === 'male' ? 'man' : 'lady'}!`, note: `Hi, ${value === 'male' ? 'man' : 'lady'}!`,
}); });
}, }
render() { render() {
const { getFieldDecorator } = this.props.form; const { getFieldDecorator } = this.props.form;
return ( return (
@ -70,8 +70,10 @@ const App = Form.create()(React.createClass({
</FormItem> </FormItem>
</Form> </Form>
); );
}, }
})); }
ReactDOM.render(<App />, mountNode); const WrappedApp = Form.create()(App);
ReactDOM.render(<WrappedApp />, mountNode);
```` ````

View File

@ -23,22 +23,24 @@ import { Form, Input, Select, Button } from 'antd';
const FormItem = Form.Item; const FormItem = Form.Item;
const Option = Select.Option; const Option = Select.Option;
const PriceInput = React.createClass({ class PriceInput extends React.Component {
getInitialState() { constructor(props) {
super(props);
const value = this.props.value || {}; const value = this.props.value || {};
return { this.state = {
number: value.number || 0, number: value.number || 0,
currency: value.currency || 'rmb', currency: value.currency || 'rmb',
}; };
}, }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
// Should be a controlled component. // Should be a controlled component.
if ('value' in nextProps) { if ('value' in nextProps) {
const value = nextProps.value; const value = nextProps.value;
this.setState(value); this.setState(value);
} }
}, }
handleNumberChange(e) { handleNumberChange = (e) => {
const number = parseInt(e.target.value || 0, 10); const number = parseInt(e.target.value || 0, 10);
if (isNaN(number)) { if (isNaN(number)) {
return; return;
@ -47,20 +49,20 @@ const PriceInput = React.createClass({
this.setState({ number }); this.setState({ number });
} }
this.triggerChange({ number }); this.triggerChange({ number });
}, }
handleCurrencyChange(currency) { handleCurrencyChange = (currency) => {
if (!('value' in this.props)) { if (!('value' in this.props)) {
this.setState({ currency }); this.setState({ currency });
} }
this.triggerChange({ currency }); this.triggerChange({ currency });
}, }
triggerChange(changedValue) { triggerChange = (changedValue) => {
// Should provide an event to pass value to Form. // Should provide an event to pass value to Form.
const onChange = this.props.onChange; const onChange = this.props.onChange;
if (onChange) { if (onChange) {
onChange(Object.assign({}, this.state, changedValue)); onChange(Object.assign({}, this.state, changedValue));
} }
}, }
render() { render() {
const { size } = this.props; const { size } = this.props;
const state = this.state; const state = this.state;
@ -84,25 +86,25 @@ const PriceInput = React.createClass({
</Select> </Select>
</span> </span>
); );
}, }
}); }
const Demo = Form.create()(React.createClass({ class Demo extends React.Component {
handleSubmit(e) { handleSubmit = (e) => {
e.preventDefault(); e.preventDefault();
this.props.form.validateFields((err, values) => { this.props.form.validateFields((err, values) => {
if (!err) { if (!err) {
console.log('Received values of form: ', values); console.log('Received values of form: ', values);
} }
}); });
}, }
checkPrice(rule, value, callback) { checkPrice = (rule, value, callback) => {
if (value.number > 0) { if (value.number > 0) {
callback(); callback();
return; return;
} }
callback('Price must greater than zero!'); callback('Price must greater than zero!');
}, }
render() { render() {
const { getFieldDecorator } = this.props.form; const { getFieldDecorator } = this.props.form;
return ( return (
@ -118,8 +120,10 @@ const Demo = Form.create()(React.createClass({
</FormItem> </FormItem>
</Form> </Form>
); );
}, }
})); }
ReactDOM.render(<Demo />, mountNode); const WrappedDemo = Form.create()(Demo);
ReactDOM.render(<WrappedDemo />, mountNode);
```` ````

View File

@ -56,17 +56,17 @@ const CollectionCreateForm = Form.create()(
} }
); );
const CollectionsPage = React.createClass({ class CollectionsPage extends React.Component {
getInitialState() { state = {
return { visible: false }; visible: false,
}, };
showModal() { showModal = () => {
this.setState({ visible: true }); this.setState({ visible: true });
}, }
handleCancel() { handleCancel = () => {
this.setState({ visible: false }); this.setState({ visible: false });
}, }
handleCreate() { handleCreate = () => {
const form = this.form; const form = this.form;
form.validateFields((err, values) => { form.validateFields((err, values) => {
if (err) { if (err) {
@ -77,10 +77,10 @@ const CollectionsPage = React.createClass({
form.resetFields(); form.resetFields();
this.setState({ visible: false }); this.setState({ visible: false });
}); });
}, }
saveFormRef(form) { saveFormRef = (form) => {
this.form = form; this.form = form;
}, }
render() { render() {
return ( return (
<div> <div>
@ -93,8 +93,8 @@ const CollectionsPage = React.createClass({
/> />
</div> </div>
); );
}, }
}); }
ReactDOM.render(<CollectionsPage />, mountNode); ReactDOM.render(<CollectionsPage />, mountNode);
```` ````

View File

@ -45,21 +45,19 @@ const CustomizedForm = Form.create({
); );
}); });
const Demo = React.createClass({ class Demo extends React.Component {
getInitialState() { state = {
return { fields: {
fields: { username: {
username: { value: 'benjycui',
value: 'benjycui',
},
}, },
}; },
}, };
handleFormChange(changedFields) { handleFormChange = (changedFields) => {
this.setState({ this.setState({
fields: { ...this.state.fields, ...changedFields }, fields: { ...this.state.fields, ...changedFields },
}); });
}, }
render() { render() {
const fields = this.state.fields; const fields = this.state.fields;
return ( return (
@ -70,8 +68,8 @@ const Demo = React.createClass({
</pre> </pre>
</div> </div>
); );
}, }
}); }
ReactDOM.render(<Demo />, mountNode); ReactDOM.render(<Demo />, mountNode);
```` ````

View File

@ -21,19 +21,19 @@ function hasErrors(fieldsError) {
return Object.keys(fieldsError).some(field => fieldsError[field]); return Object.keys(fieldsError).some(field => fieldsError[field]);
} }
const HorizontalLoginForm = Form.create()(React.createClass({ class HorizontalLoginForm extends React.Component {
componentDidMount() { componentDidMount() {
// To disabled submit button at the beginning. // To disabled submit button at the beginning.
this.props.form.validateFields(); this.props.form.validateFields();
}, }
handleSubmit(e) { handleSubmit = (e) => {
e.preventDefault(); e.preventDefault();
this.props.form.validateFields((err, values) => { this.props.form.validateFields((err, values) => {
if (!err) { if (!err) {
console.log('Received values of form: ', values); console.log('Received values of form: ', values);
} }
}); });
}, }
render() { render() {
const { getFieldDecorator, getFieldsError, getFieldError, isFieldTouched } = this.props.form; const { getFieldDecorator, getFieldsError, getFieldError, isFieldTouched } = this.props.form;
@ -73,8 +73,10 @@ const HorizontalLoginForm = Form.create()(React.createClass({
</FormItem> </FormItem>
</Form> </Form>
); );
}, }
})); }
ReactDOM.render(<HorizontalLoginForm />, mountNode); const WrappedHorizontalLoginForm = Form.create()(HorizontalLoginForm);
ReactDOM.render(<WrappedHorizontalLoginForm />, mountNode);
```` ````

View File

@ -17,15 +17,15 @@ Normal login form which can contain more elements.
import { Form, Icon, Input, Button, Checkbox } from 'antd'; import { Form, Icon, Input, Button, Checkbox } from 'antd';
const FormItem = Form.Item; const FormItem = Form.Item;
const NormalLoginForm = Form.create()(React.createClass({ class NormalLoginForm extends React.Component {
handleSubmit(e) { handleSubmit = (e) => {
e.preventDefault(); e.preventDefault();
this.props.form.validateFields((err, values) => { this.props.form.validateFields((err, values) => {
if (!err) { if (!err) {
console.log('Received values of form: ', values); console.log('Received values of form: ', values);
} }
}); });
}, }
render() { render() {
const { getFieldDecorator } = this.props.form; const { getFieldDecorator } = this.props.form;
return ( return (
@ -59,10 +59,12 @@ const NormalLoginForm = Form.create()(React.createClass({
</FormItem> </FormItem>
</Form> </Form>
); );
}, }
})); }
ReactDOM.render(<NormalLoginForm />, mountNode); const WrappedNormalLoginForm = Form.create()(NormalLoginForm);
ReactDOM.render(<WrappedNormalLoginForm />, mountNode);
```` ````
```css ```css

View File

@ -42,39 +42,37 @@ const residences = [{
}], }],
}]; }];
const RegistrationForm = Form.create()(React.createClass({ class RegistrationForm extends React.Component {
getInitialState() { state = {
return { passwordDirty: false,
passwordDirty: false, };
}; handleSubmit = (e) => {
},
handleSubmit(e) {
e.preventDefault(); e.preventDefault();
this.props.form.validateFieldsAndScroll((err, values) => { this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) { if (!err) {
console.log('Received values of form: ', values); console.log('Received values of form: ', values);
} }
}); });
}, }
handlePasswordBlur(e) { handlePasswordBlur = (e) => {
const value = e.target.value; const value = e.target.value;
this.setState({ passwordDirty: this.state.passwordDirty || !!value }); this.setState({ passwordDirty: this.state.passwordDirty || !!value });
}, }
checkPassword(rule, value, callback) { checkPassword = (rule, value, callback) => {
const form = this.props.form; const form = this.props.form;
if (value && value !== form.getFieldValue('password')) { if (value && value !== form.getFieldValue('password')) {
callback('Two passwords that you enter is inconsistent!'); callback('Two passwords that you enter is inconsistent!');
} else { } else {
callback(); callback();
} }
}, }
checkConfirm(rule, value, callback) { checkConfirm = (rule, value, callback) => {
const form = this.props.form; const form = this.props.form;
if (value && this.state.passwordDirty) { if (value && this.state.passwordDirty) {
form.validateFields(['confirm'], { force: true }); form.validateFields(['confirm'], { force: true });
} }
callback(); callback();
}, }
render() { render() {
const { getFieldDecorator } = this.props.form; const { getFieldDecorator } = this.props.form;
const formItemLayout = { const formItemLayout = {
@ -210,10 +208,12 @@ const RegistrationForm = Form.create()(React.createClass({
</FormItem> </FormItem>
</Form> </Form>
); );
}, }
})); }
ReactDOM.render(<RegistrationForm />, mountNode); const WrappedRegistrationForm = Form.create()(RegistrationForm);
ReactDOM.render(<WrappedRegistrationForm />, mountNode);
```` ````
````css ````css

View File

@ -19,8 +19,8 @@ const FormItem = Form.Item;
const MonthPicker = DatePicker.MonthPicker; const MonthPicker = DatePicker.MonthPicker;
const RangePicker = DatePicker.RangePicker; const RangePicker = DatePicker.RangePicker;
const TimeRelatedForm = Form.create()(React.createClass({ class TimeRelatedForm extends React.Component {
handleSubmit(e) { handleSubmit = (e) => {
e.preventDefault(); e.preventDefault();
this.props.form.validateFields((err, fieldsValue) => { this.props.form.validateFields((err, fieldsValue) => {
@ -45,7 +45,7 @@ const TimeRelatedForm = Form.create()(React.createClass({
}; };
console.log('Received values of form: ', values); console.log('Received values of form: ', values);
}); });
}, }
render() { render() {
const { getFieldDecorator } = this.props.form; const { getFieldDecorator } = this.props.form;
const formItemLayout = { const formItemLayout = {
@ -113,8 +113,10 @@ const TimeRelatedForm = Form.create()(React.createClass({
</FormItem> </FormItem>
</Form> </Form>
); );
}, }
})); }
ReactDOM.render(<TimeRelatedForm />, mountNode); const WrappedTimeRelatedForm = Form.create()(TimeRelatedForm);
ReactDOM.render(<WrappedTimeRelatedForm />, mountNode);
```` ````

View File

@ -23,23 +23,21 @@ const Option = Select.Option;
const RadioButton = Radio.Button; const RadioButton = Radio.Button;
const RadioGroup = Radio.Group; const RadioGroup = Radio.Group;
const Demo = Form.create()(React.createClass({ class Demo extends React.Component {
handleSubmit(e) { handleSubmit = (e) => {
e.preventDefault(); e.preventDefault();
this.props.form.validateFields((err, values) => { this.props.form.validateFields((err, values) => {
if (!err) { if (!err) {
console.log('Received values of form: ', values); console.log('Received values of form: ', values);
} }
}); });
}, }
normFile = (e) => {
normFile(e) {
if (Array.isArray(e)) { if (Array.isArray(e)) {
return e; return e;
} }
return e && e.fileList; return e && e.fileList;
}, }
render() { render() {
const { getFieldDecorator } = this.props.form; const { getFieldDecorator } = this.props.form;
const formItemLayout = { const formItemLayout = {
@ -149,7 +147,7 @@ const Demo = Form.create()(React.createClass({
> >
{getFieldDecorator('upload', { {getFieldDecorator('upload', {
valuePropName: 'fileList', valuePropName: 'fileList',
normalize: this.normFile, getValueFromEvent: this.normFile,
})( })(
<Upload name="logo" action="/upload.do" listType="picture" onChange={this.handleUpload}> <Upload name="logo" action="/upload.do" listType="picture" onChange={this.handleUpload}>
<Button> <Button>
@ -164,8 +162,10 @@ const Demo = Form.create()(React.createClass({
</FormItem> </FormItem>
</Form> </Form>
); );
}, }
})); }
ReactDOM.render(<Demo />, mountNode); const WrappedDemo = Form.create()(Demo);
ReactDOM.render(<WrappedDemo />, mountNode);
```` ````

View File

@ -1,25 +1,25 @@
--- ---
order: 10 order: 10
title: title:
zh-CN: 校验提示 zh-CN: 自定义校验
en-US: Validation message en-US: Customized Validation
--- ---
## zh-CN ## zh-CN
我们为表单控件定义了三种校验状态,为 `<FormItem>` 定义 `validateStatus` 属性即可 我们提供了 `validateStatus` `help` `hasFeedback` 等属性,你可以不需要使用 `Form.create``getFieldDecorator`,自己定义校验的时机和内容
validateStatus: 'success', 'warning', 'error', 'validating'。 1. `validateStatus`: 校验状态,可选 'success', 'warning', 'error', 'validating'。
2. `hasFeedback`:用于给输入框添加反馈图标。
另外为输入框添加反馈图标,设置 `<FormItem>``hasFeedback` 属性值为 `true` 即可 3. `help`:设置校验文案
## en-US ## en-US
We provide three kinds of validation status for form. You can use it just define `validateStatus` property on `<FormItem>`. We provide properties like `validateStatus` `help` `hasFeedback` to customize your own validate status and message, without using `Form.create` and `getFieldDecorator`.
validateStatus: 'success', 'warning', 'error', 'validating' 1. `validateStatus`: validate status of form components which could be 'success', 'warning', 'error', 'validating'.
2. `hasFeedback`: display feed icon of input control
To set `hasFeedback` property to `true` enable to display feed icon of input control. 3. `help`: display validate message.
````jsx ````jsx
import { Form, Input, DatePicker, Col } from 'antd'; import { Form, Input, DatePicker, Col } from 'antd';

View File

@ -36,7 +36,6 @@ class RawForm extends React.Component {
value: 11, value: 11,
}, },
}; };
handleNumberChange = (value) => { handleNumberChange = (value) => {
this.setState({ this.setState({
number: { number: {
@ -45,7 +44,6 @@ class RawForm extends React.Component {
}, },
}); });
} }
render() { render() {
const formItemLayout = { const formItemLayout = {
labelCol: { span: 7 }, labelCol: { span: 7 },

View File

@ -6,6 +6,7 @@
@import "./mixin"; @import "./mixin";
@form-prefix-cls: ~"@{ant-prefix}-form"; @form-prefix-cls: ~"@{ant-prefix}-form";
@form-component-height: @input-height-lg;
.reset-form(); .reset-form();
@ -75,7 +76,7 @@ input[type="checkbox"] {
} }
&-control { &-control {
line-height: 32px; line-height: @form-component-height;
position: relative; position: relative;
.clearfix; .clearfix;
} }
@ -87,7 +88,7 @@ input[type="checkbox"] {
&-label { &-label {
text-align: right; text-align: right;
vertical-align: middle; vertical-align: middle;
padding: 7px 0; line-height: @form-component-height;
display: inline-block; display: inline-block;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;

View File

@ -385,7 +385,7 @@ exports[`test renders ./components/grid/demo/playground.md correctly 1`] = `
<div <div
class="ant-slider-rail" /> class="ant-slider-rail" />
<div <div
class="ant-slider-track ant-slider-track-1" class="ant-slider-track"
style="visibility:visible;left:0%;width:20%;" /> style="visibility:visible;left:0%;width:20%;" />
<div <div
class="ant-slider-step"> class="ant-slider-step">
@ -409,7 +409,7 @@ exports[`test renders ./components/grid/demo/playground.md correctly 1`] = `
style="left:100%;" /> style="left:100%;" />
</div> </div>
<div <div
class="ant-slider-handle ant-slider-handle-2 ant-slider-handle-upper" class="ant-slider-handle"
style="left:20%;" /> style="left:20%;" />
<div <div
class="ant-slider-mark"> class="ant-slider-mark">
@ -457,7 +457,7 @@ exports[`test renders ./components/grid/demo/playground.md correctly 1`] = `
<div <div
class="ant-slider-rail" /> class="ant-slider-rail" />
<div <div
class="ant-slider-track ant-slider-track-1" class="ant-slider-track"
style="visibility:visible;left:0%;width:40%;" /> style="visibility:visible;left:0%;width:40%;" />
<div <div
class="ant-slider-step"> class="ant-slider-step">
@ -481,7 +481,7 @@ exports[`test renders ./components/grid/demo/playground.md correctly 1`] = `
style="left:100%;" /> style="left:100%;" />
</div> </div>
<div <div
class="ant-slider-handle ant-slider-handle-2 ant-slider-handle-upper" class="ant-slider-handle"
style="left:40%;" /> style="left:40%;" />
<div <div
class="ant-slider-mark"> class="ant-slider-mark">

View File

@ -72,8 +72,8 @@
text-align: left; text-align: left;
outline: 0; outline: 0;
-moz-appearance: textfield; -moz-appearance: textfield;
line-height: 26px; line-height: @input-height-base - 2px;
height: 26px; height: @input-height-base - 2px;
transition: all 0.3s linear; transition: all 0.3s linear;
color: @text-color; color: @text-color;
border: 0; border: 0;
@ -87,48 +87,19 @@
&-lg { &-lg {
padding: 0; padding: 0;
.@{input-number-prefix-cls}-handler {
height: 16px;
}
input { input {
height: 30px; height: @input-height-lg - 2px;
line-height: 30px; line-height: @input-height-lg - 2px;
}
.@{input-number-prefix-cls}-handler-up-inner {
top: 2px;
}
.@{input-number-prefix-cls}-handler-down-inner {
bottom: 2px;
}
.@{input-number-prefix-cls}-handler-up:hover {
height: 18px;
} }
} }
&-sm { &-sm {
padding: 0; padding: 0;
.@{input-number-prefix-cls}-handler {
height: 11px;
}
input { input {
height: 20px; height: @input-height-sm - 2px;
line-height: 20px; line-height: @input-height-sm - 2px;
}
.@{input-number-prefix-cls}-handler-up-inner {
top: -1px;
}
.@{input-number-prefix-cls}-handler-down-inner {
bottom: -1px;
}
.@{input-number-prefix-cls}-handler-up:hover {
height: 13px;
}
.@{input-number-prefix-cls}-handler-down:hover .@{input-number-prefix-cls}-handler-down-inner {
bottom: 4px;
} }
} }
@ -145,6 +116,10 @@
transition: opacity 0.24s linear 0.1s; transition: opacity 0.24s linear 0.1s;
} }
&-handler-wrap:hover &-handler {
height: 40%;
}
&:hover &-handler-wrap { &:hover &-handler-wrap {
opacity: 1; opacity: 1;
} }
@ -152,17 +127,15 @@
&-handler-up { &-handler-up {
cursor: pointer; cursor: pointer;
&-inner { &-inner {
top: 1px; top: 50%;
margin-top: -6px;
&:before { &:before {
text-align: center; text-align: center;
content: "\e61e"; content: "\e61e";
} }
} }
&:hover { &:hover {
height: 16px; height: 60%!important;
}
&:hover &-inner {
margin-top: 2px;
} }
} }
@ -171,14 +144,15 @@
top: -1px; top: -1px;
cursor: pointer; cursor: pointer;
&-inner { &-inner {
top: 50%;
margin-top: -6px;
&:before { &:before {
text-align: center; text-align: center;
content: "\e61d"; content: "\e61d";
} }
} }
&:hover { &:hover {
height: 16px; height: 60%!important;
margin-top: -2px;
} }
} }

View File

@ -405,15 +405,11 @@ exports[`test renders ./components/input/demo/textarea.md correctly 1`] = `
`; `;
exports[`test renders ./components/input/demo/tooltip.md correctly 1`] = ` exports[`test renders ./components/input/demo/tooltip.md correctly 1`] = `
<div <input
class="numeric-input-demo"> class="ant-input"
<div> maxlength="25"
<input placeholder="Input a number"
class="ant-input" style="width:120px;"
maxlength="25" type="text"
placeholder="input a number" value="" />
type="text"
value="" />
</div>
</div>
`; `;

View File

@ -40,41 +40,38 @@ class NumericInput extends React.Component {
this.props.onChange(value); this.props.onChange(value);
} }
} }
// '.' at the end or only '-' in the input box. // '.' at the end or only '-' in the input box.
onBlur = () => { onBlur = () => {
const { value } = this.props; const { value, onBlur, onChange } = this.props;
if (value.charAt(value.length - 1) === '.' || value === '-') { if (value.charAt(value.length - 1) === '.' || value === '-') {
this.props.onChange({ value: value.slice(0, -1) }); onChange({ value: value.slice(0, -1) });
} }
if (this.props.onBlur) { if (onBlur) {
this.props.onBlur(); onBlur();
} }
} }
render() { render() {
const { value } = this.props; const { value } = this.props;
const title = (value ? const title = value ? (
(<span className="numeric-input-title"> <span className="numeric-input-title">
{value !== '-' ? formatNumber(value) : '-'} {value !== '-' ? formatNumber(value) : '-'}
</span>) : ''); </span>
) : 'Input a number';
return ( return (
<div> <Tooltip
<Tooltip trigger={['focus']}
trigger={['focus']} title={title}
title={title} placement="topLeft"
placement="topLeft" overlayClassName="numeric-input"
overlayClassName="numeric-input" >
> <Input
<Input {...this.props}
{...this.props} onChange={this.onChange}
onChange={this.onChange} onBlur={this.onBlur}
onBlur={this.onBlur} placeholder="Input a number"
placeholder="input a number" maxLength="25"
maxLength="25" />
/> </Tooltip>
</Tooltip>
</div>
); );
} }
} }
@ -88,12 +85,7 @@ class NumericInputDemo extends React.Component {
this.setState({ value }); this.setState({ value });
} }
render() { render() {
const { value } = this.state; return <NumericInput style={{ width: 120 }} value={this.state.value} onChange={this.onChange} />;
return (
<div className="numeric-input-demo">
<NumericInput value={value} onChange={this.onChange} />
</div>
);
} }
} }
@ -111,8 +103,4 @@ or the height is not enough when content is empty */
.numeric-input .numeric-input-title { .numeric-input .numeric-input-title {
font-size: 14px; font-size: 14px;
} }
.numeric-input-demo {
width: 120px;
}
```` ````

View File

@ -81,6 +81,7 @@ export default class Sider extends React.Component<SiderProps, any> {
const divStyle = { const divStyle = {
...style, ...style,
flex: `0 0 ${this.state.collapsed ? collapsedWidth : width}px`, flex: `0 0 ${this.state.collapsed ? collapsedWidth : width}px`,
width: `${this.state.collapsed ? collapsedWidth : width}px`,
}; };
const iconObj = { const iconObj = {
'expanded': reverseArrow ? <Icon type="right" /> : <Icon type="left" />, 'expanded': reverseArrow ? <Icon type="right" /> : <Icon type="left" />,

View File

@ -25,7 +25,7 @@ exports[`test renders ./components/layout/demo/basic.md correctly 1`] = `
class="ant-layout ant-layout-has-sider"> class="ant-layout ant-layout-has-sider">
<div <div
class="ant-layout-sider" class="ant-layout-sider"
style="flex:0 0 200px;"> style="flex:0 0 200px;width:200px;">
Sider Sider
</div> </div>
<div <div
@ -52,7 +52,7 @@ exports[`test renders ./components/layout/demo/basic.md correctly 1`] = `
</div> </div>
<div <div
class="ant-layout-sider" class="ant-layout-sider"
style="flex:0 0 200px;"> style="flex:0 0 200px;width:200px;">
Sider Sider
</div> </div>
</div> </div>
@ -65,7 +65,7 @@ exports[`test renders ./components/layout/demo/basic.md correctly 1`] = `
class="ant-layout ant-layout-has-sider"> class="ant-layout ant-layout-has-sider">
<div <div
class="ant-layout-sider" class="ant-layout-sider"
style="flex:0 0 200px;"> style="flex:0 0 200px;width:200px;">
Sider Sider
</div> </div>
<div <div
@ -92,7 +92,7 @@ exports[`test renders ./components/layout/demo/custom-trigger.md correctly 1`] =
class="ant-layout ant-layout-has-sider"> class="ant-layout ant-layout-has-sider">
<div <div
class="ant-layout-sider" class="ant-layout-sider"
style="flex:0 0 200px;"> style="flex:0 0 200px;width:200px;">
<div <div
class="logo" /> class="logo" />
<ul <ul
@ -160,7 +160,7 @@ exports[`test renders ./components/layout/demo/side.md correctly 1`] = `
class="ant-layout ant-layout-has-sider"> class="ant-layout ant-layout-has-sider">
<div <div
class="ant-layout-sider" class="ant-layout-sider"
style="flex:0 0 200px;"> style="flex:0 0 200px;width:200px;">
<div <div
class="logo" /> class="logo" />
<ul <ul
@ -168,76 +168,54 @@ exports[`test renders ./components/layout/demo/side.md correctly 1`] = `
class="ant-menu ant-menu-inline ant-menu-dark ant-menu-root" class="ant-menu ant-menu-inline ant-menu-dark ant-menu-root"
role="menu" role="menu"
tabindex="0"> tabindex="0">
<li
class="ant-menu-submenu-inline ant-menu-submenu">
<div
aria-expanded="false"
aria-haspopup="true"
aria-owns="sub1$Menu"
class="ant-menu-submenu-title"
style="padding-left:24px;">
<span>
<i
class="anticon anticon-user" />
<span
class="nav-text">
User
</span>
</span>
</div>
</li>
<li
class="ant-menu-submenu-inline ant-menu-submenu">
<div
aria-expanded="false"
aria-haspopup="true"
aria-owns="sub2$Menu"
class="ant-menu-submenu-title"
style="padding-left:24px;">
<span>
<i
class="anticon anticon-team" />
<span
class="nav-text">
Team
</span>
</span>
</div>
</li>
<li <li
aria-selected="true" aria-selected="true"
class="ant-menu-item-selected ant-menu-item" class="ant-menu-item-selected ant-menu-item"
role="menuitem" role="menuitem"
style="padding-left:24px;"> style="padding-left:24px;">
<i <span>
class="anticon anticon-user" /> <i
<span class="anticon anticon-file" />
class="nav-text"> <span
nav 1 class="nav-text">
</span> File
</li> </span>
<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="false"
class="ant-menu-item"
role="menuitem"
style="padding-left:24px;">
<i
class="anticon anticon-user" />
<span
class="nav-text">
nav 4
</span>
</li>
<li
aria-selected="false"
class="ant-menu-item"
role="menuitem"
style="padding-left:24px;">
<i
class="anticon anticon-heart-o" />
<span
class="nav-text">
nav 5
</span>
</li>
<li
aria-selected="false"
class="ant-menu-item"
role="menuitem"
style="padding-left:24px;">
<i
class="anticon anticon-team" />
<span
class="nav-text">
nav 6
</span> </span>
</li> </li>
</ul> </ul>
@ -261,7 +239,7 @@ exports[`test renders ./components/layout/demo/side.md correctly 1`] = `
<span> <span>
<span <span
class="ant-breadcrumb-link"> class="ant-breadcrumb-link">
Home User
</span> </span>
<span <span
class="ant-breadcrumb-separator"> class="ant-breadcrumb-separator">
@ -271,17 +249,7 @@ exports[`test renders ./components/layout/demo/side.md correctly 1`] = `
<span> <span>
<span <span
class="ant-breadcrumb-link"> class="ant-breadcrumb-link">
List Bill
</span>
<span
class="ant-breadcrumb-separator">
/
</span>
</span>
<span>
<span
class="ant-breadcrumb-link">
App
</span> </span>
<span <span
class="ant-breadcrumb-separator"> class="ant-breadcrumb-separator">
@ -291,7 +259,7 @@ exports[`test renders ./components/layout/demo/side.md correctly 1`] = `
</div> </div>
<div <div
style="padding:24px;background:#fff;min-height:360px;"> style="padding:24px;background:#fff;min-height:360px;">
content Bill is a cat.
</div> </div>
</div> </div>
<div <div
@ -461,7 +429,7 @@ exports[`test renders ./components/layout/demo/top-side.md correctly 1`] = `
style="padding:24px 0;background:#fff;"> style="padding:24px 0;background:#fff;">
<div <div
class="ant-layout-sider" class="ant-layout-sider"
style="background:#fff;flex:0 0 200px;"> style="background:#fff;flex:0 0 200px;width:200px;">
<ul <ul
aria-activedescendant="" aria-activedescendant=""
class="ant-menu ant-menu-inline ant-menu-light ant-menu-root" class="ant-menu ant-menu-inline ant-menu-light ant-menu-root"

View File

@ -16,14 +16,19 @@ Be used in the two-columns layout.
````jsx ````jsx
import { Layout, Menu, Breadcrumb, Icon } from 'antd'; import { Layout, Menu, Breadcrumb, Icon } from 'antd';
const { Header, Content, Footer, Sider } = Layout; const { Header, Content, Footer, Sider } = Layout;
const SubMenu = Menu.SubMenu;
class SiderDemo extends React.Component { class SiderDemo extends React.Component {
state = { state = {
collapsed: false, collapsed: false,
mode: 'inline',
}; };
onCollapse = (collapsed) => { onCollapse = (collapsed) => {
console.log(collapsed); console.log(collapsed);
this.setState({ collapsed }); this.setState({
collapsed,
mode: collapsed ? 'vertical' : 'inline',
});
} }
render() { render() {
return ( return (
@ -34,30 +39,27 @@ class SiderDemo extends React.Component {
onCollapse={this.onCollapse} onCollapse={this.onCollapse}
> >
<div className="logo" /> <div className="logo" />
<Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}> <Menu theme="dark" mode={this.state.mode} defaultSelectedKeys={['6']}>
<Menu.Item key="1"> <SubMenu
<Icon type="user" /> key="sub1"
<span className="nav-text">nav 1</span> title={<span><Icon type="user" /><span className="nav-text">User</span></span>}
</Menu.Item> >
<Menu.Item key="2"> <Menu.Item key="1">Tom</Menu.Item>
<Icon type="video-camera" /> <Menu.Item key="2">Bill</Menu.Item>
<span className="nav-text">nav 2</span> <Menu.Item key="3">Alex</Menu.Item>
</Menu.Item> </SubMenu>
<Menu.Item key="3"> <SubMenu
<Icon type="upload" /> key="sub2"
<span className="nav-text">nav 3</span> title={<span><Icon type="team" /><span className="nav-text">Team</span></span>}
</Menu.Item> >
<Menu.Item key="4"> <Menu.Item key="4">Team 1</Menu.Item>
<Icon type="user" /> <Menu.Item key="5">Team 2</Menu.Item>
<span className="nav-text">nav 4</span> </SubMenu>
</Menu.Item>
<Menu.Item key="5">
<Icon type="heart-o" />
<span className="nav-text">nav 5</span>
</Menu.Item>
<Menu.Item key="6"> <Menu.Item key="6">
<Icon type="team" /> <span>
<span className="nav-text">nav 6</span> <Icon type="file" />
<span className="nav-text">File</span>
</span>
</Menu.Item> </Menu.Item>
</Menu> </Menu>
</Sider> </Sider>
@ -65,12 +67,11 @@ class SiderDemo extends React.Component {
<Header style={{ background: '#fff', padding: 0 }} /> <Header style={{ background: '#fff', padding: 0 }} />
<Content style={{ margin: '0 16px' }}> <Content style={{ margin: '0 16px' }}>
<Breadcrumb style={{ margin: '12px 0' }}> <Breadcrumb style={{ margin: '12px 0' }}>
<Breadcrumb.Item>Home</Breadcrumb.Item> <Breadcrumb.Item>User</Breadcrumb.Item>
<Breadcrumb.Item>List</Breadcrumb.Item> <Breadcrumb.Item>Bill</Breadcrumb.Item>
<Breadcrumb.Item>App</Breadcrumb.Item>
</Breadcrumb> </Breadcrumb>
<div style={{ padding: 24, background: '#fff', minHeight: 360 }}> <div style={{ padding: 24, background: '#fff', minHeight: 360 }}>
content Bill is a cat.
</div> </div>
</Content> </Content>
<Footer style={{ textAlign: 'center' }}> <Footer style={{ textAlign: 'center' }}>
@ -100,4 +101,8 @@ ReactDOM.render(<SiderDemo />, mountNode);
#components-layout-demo-side .ant-layout-sider-collapsed .nav-text { #components-layout-demo-side .ant-layout-sider-collapsed .nav-text {
display: none; display: none;
} }
#components-layout-demo-side .ant-layout-sider-collapsed .ant-menu-submenu-vertical > .ant-menu-submenu-title:after {
display: none;
}
```` ````

View File

@ -37,8 +37,6 @@
} }
&-sider { &-sider {
flex: 0 0 200px;
overflow: hidden;
transition: all .3s @ease-out; transition: all .3s @ease-out;
position: relative; position: relative;
background: @layout-sider-background; background: @layout-sider-background;
@ -51,10 +49,6 @@
order: 1; order: 1;
} }
&-collapsed {
flex: 0 0 64px;
}
&-trigger { &-trigger {
position: absolute; position: absolute;
text-align: center; text-align: center;

View File

@ -126,7 +126,7 @@ exports[`test renders ./components/locale-provider/demo/all.md correctly 1`] = `
<div <div
class="example"> class="example">
<div <div
class="ant-select-show-search ant-select ant-select-enabled" class="ant-select ant-select-enabled"
style="width:200px;"> style="width:200px;">
<div <div
aria-autocomplete="list" aria-autocomplete="list"
@ -1146,7 +1146,7 @@ exports[`test renders ./components/locale-provider/demo/all.md correctly 1`] = `
<div <div
class="example"> class="example">
<div <div
class=" clearfix"> class="ant-table-wrapper">
<div <div
class="ant-spin-nested-loading"> class="ant-spin-nested-loading">
<div <div
@ -1156,45 +1156,40 @@ exports[`test renders ./components/locale-provider/demo/all.md correctly 1`] = `
<div <div
class="ant-table-content"> class="ant-table-content">
<div <div
class=""> class="ant-table-body">
<span> <table
<div class="">
class="ant-table-body"> <colgroup>
<table <col />
class=""> <col />
<colgroup> </colgroup>
<col /> <thead
<col /> class="ant-table-thead">
</colgroup> <tr>
<thead <th
class="ant-table-thead"> class="">
<tr> <span>
<th Name
class=""> <i
<span> class="anticon anticon-filter ant-dropdown-trigger"
Name title="Filter Menu" />
<i </span>
class="anticon anticon-filter ant-dropdown-trigger" </th>
title="Filter Menu" /> <th
</span> class="">
</th> <span>
<th Age
class=""> </span>
<span> </th>
Age </tr>
</span> </thead>
</th> <tbody
</tr> class="ant-table-tbody" />
</thead> </table>
<tbody </div>
class="ant-table-tbody" /> <div
</table> class="ant-table-placeholder">
</div> No Data
</span>
<div
class="ant-table-placeholder">
No Data
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -46,7 +46,7 @@ describe('Menu', () => {
it('should accept openKeys in mode horizontal', () => { it('should accept openKeys in mode horizontal', () => {
const wrapper = mount( const wrapper = mount(
<Menu openKeys={['1']} mode="horizontal"> <Menu openKeys={['1']} mode="horizontal" openTransitionName="">
<SubMenu key="1" title="submenu1"> <SubMenu key="1" title="submenu1">
<Menu.Item key="submenu1">Option 1</Menu.Item> <Menu.Item key="submenu1">Option 1</Menu.Item>
<Menu.Item key="submenu2">Option 2</Menu.Item> <Menu.Item key="submenu2">Option 2</Menu.Item>
@ -55,11 +55,15 @@ describe('Menu', () => {
</Menu> </Menu>
); );
expect(wrapper.find('.ant-menu-sub').at(0).hasClass('ant-menu-hidden')).not.toBe(true); expect(wrapper.find('.ant-menu-sub').at(0).hasClass('ant-menu-hidden')).not.toBe(true);
wrapper.setProps({ openKeys: [] });
expect(wrapper.find('.ant-menu-sub').at(0).hasClass('ant-menu-hidden')).toBe(true);
wrapper.setProps({ openKeys: ['1'] });
expect(wrapper.find('.ant-menu-sub').at(0).hasClass('ant-menu-hidden')).not.toBe(true);
}); });
it('should accept openKeys in mode inline', () => { it.only('should accept openKeys in mode inline', () => {
const wrapper = mount( const wrapper = mount(
<Menu openKeys={['1']} mode="inline"> <Menu openKeys={['1']} mode="inline" openAnimation="">
<SubMenu key="1" title="submenu1"> <SubMenu key="1" title="submenu1">
<Menu.Item key="submenu1">Option 1</Menu.Item> <Menu.Item key="submenu1">Option 1</Menu.Item>
<Menu.Item key="submenu2">Option 2</Menu.Item> <Menu.Item key="submenu2">Option 2</Menu.Item>
@ -68,11 +72,15 @@ describe('Menu', () => {
</Menu> </Menu>
); );
expect(wrapper.find('.ant-menu-sub').at(0).hasClass('ant-menu-hidden')).not.toBe(true); expect(wrapper.find('.ant-menu-sub').at(0).hasClass('ant-menu-hidden')).not.toBe(true);
wrapper.setProps({ openKeys: [] });
expect(wrapper.find('.ant-menu-sub').at(0).hasClass('ant-menu-hidden')).toBe(true);
wrapper.setProps({ openKeys: ['1'] });
expect(wrapper.find('.ant-menu-sub').at(0).hasClass('ant-menu-hidden')).not.toBe(true);
}); });
it('should accept openKeys in mode vertical', () => { it('should accept openKeys in mode vertical', () => {
const wrapper = mount( const wrapper = mount(
<Menu openKeys={['1']} mode="vertical"> <Menu openKeys={['1']} mode="vertical" openTransitionName="">
<SubMenu key="1" title="submenu1"> <SubMenu key="1" title="submenu1">
<Menu.Item key="submenu1">Option 1</Menu.Item> <Menu.Item key="submenu1">Option 1</Menu.Item>
<Menu.Item key="submenu2">Option 2</Menu.Item> <Menu.Item key="submenu2">Option 2</Menu.Item>
@ -81,6 +89,10 @@ describe('Menu', () => {
</Menu> </Menu>
); );
expect(wrapper.find('.ant-menu-sub').at(0).hasClass('ant-menu-hidden')).not.toBe(true); expect(wrapper.find('.ant-menu-sub').at(0).hasClass('ant-menu-hidden')).not.toBe(true);
wrapper.setProps({ openKeys: [] });
expect(wrapper.find('.ant-menu-sub').at(0).hasClass('ant-menu-hidden')).toBe(true);
wrapper.setProps({ openKeys: ['1'] });
expect(wrapper.find('.ant-menu-sub').at(0).hasClass('ant-menu-hidden')).not.toBe(true);
}); });
// https://github.com/ant-design/ant-design/pulls/4677 // https://github.com/ant-design/ant-design/pulls/4677
@ -98,4 +110,21 @@ describe('Menu', () => {
wrapper.update(); wrapper.update();
// just expect no error emit // just expect no error emit
}); });
it('should always follow openKeys when mode is switched', () => {
const wrapper = mount(
<Menu openKeys={['1']} mode="inline">
<SubMenu key="1" title="submenu1">
<Menu.Item key="submenu1">Option 1</Menu.Item>
<Menu.Item key="submenu2">Option 2</Menu.Item>
</SubMenu>
<Menu.Item key="2">menu2</Menu.Item>
</Menu>
);
expect(wrapper.find('.ant-menu-sub').at(0).hasClass('ant-menu-hidden')).not.toBe(true);
wrapper.setProps({ mode: 'vertical' });
expect(wrapper.find('.ant-menu-sub').at(0).hasClass('ant-menu-hidden')).not.toBe(true);
wrapper.setProps({ mode: 'inline' });
expect(wrapper.find('.ant-menu-sub').at(0).hasClass('ant-menu-hidden')).not.toBe(true);
});
}); });

View File

@ -19,31 +19,24 @@ const SubMenu = Menu.SubMenu;
const MenuItemGroup = Menu.ItemGroup; const MenuItemGroup = Menu.ItemGroup;
const Sider = React.createClass({ const Sider = React.createClass({
getInitialState() {
return {
current: '1',
};
},
handleClick(e) { handleClick(e) {
console.log('click ', e); console.log('click ', e);
this.setState({
current: e.key,
});
}, },
render() { render() {
return ( return (
<Menu onClick={this.handleClick} <Menu
onClick={this.handleClick}
style={{ width: 240 }} style={{ width: 240 }}
defaultSelectedKeys={['1']}
defaultOpenKeys={['sub1']} defaultOpenKeys={['sub1']}
selectedKeys={[this.state.current]}
mode="inline" mode="inline"
> >
<SubMenu key="sub1" title={<span><Icon type="mail" /><span>Navigation One</span></span>}> <SubMenu key="sub1" title={<span><Icon type="mail" /><span>Navigation One</span></span>}>
<MenuItemGroup title="Item 1"> <MenuItemGroup key="g1" title="Item 1">
<Menu.Item key="1">Option 1</Menu.Item> <Menu.Item key="1">Option 1</Menu.Item>
<Menu.Item key="2">Option 2</Menu.Item> <Menu.Item key="2">Option 2</Menu.Item>
</MenuItemGroup> </MenuItemGroup>
<MenuItemGroup title="Item 2"> <MenuItemGroup key="g2" title="Item 2">
<Menu.Item key="3">Option 3</Menu.Item> <Menu.Item key="3">Option 3</Menu.Item>
<Menu.Item key="4">Option 4</Menu.Item> <Menu.Item key="4">Option 4</Menu.Item>
</MenuItemGroup> </MenuItemGroup>
@ -66,5 +59,6 @@ const Sider = React.createClass({
); );
}, },
}); });
ReactDOM.render(<Sider />, mountNode); ReactDOM.render(<Sider />, mountNode);
```` ````

View File

@ -88,7 +88,7 @@ export default class Menu extends React.Component<MenuProps, any> {
this.switchModeFromInline = true; this.switchModeFromInline = true;
} }
if ('openKeys' in nextProps) { if ('openKeys' in nextProps) {
this.setOpenKeys(nextProps.openKeys); this.setState({ openKeys: nextProps.openKeys });
} }
} }
handleClick = (e) => { handleClick = (e) => {
@ -114,7 +114,7 @@ export default class Menu extends React.Component<MenuProps, any> {
} }
render() { render() {
let openAnimation = this.props.openAnimation || this.props.openTransitionName; let openAnimation = this.props.openAnimation || this.props.openTransitionName;
if (!openAnimation) { if (this.props.openAnimation === undefined && this.props.openTransitionName === undefined) {
switch (this.props.mode) { switch (this.props.mode) {
case 'horizontal': case 'horizontal':
openAnimation = 'slide-up'; openAnimation = 'slide-up';

View File

@ -38,6 +38,11 @@
transition: all 0.3s ease; transition: all 0.3s ease;
} }
&-item:active,
&-submenu-title:active {
background: @primary-1;
}
&-submenu &-sub { &-submenu &-sub {
cursor: initial; cursor: initial;
} }
@ -70,6 +75,7 @@
&-item:hover, &-item:hover,
&-item-active, &-item-active,
&-submenu-open,
&-submenu-active, &-submenu-active,
&-submenu-title:hover { &-submenu-title:hover {
color: @primary-color; color: @primary-color;
@ -110,7 +116,7 @@
&-vertical { &-vertical {
border-right: @border-width-base @border-style-base @border-color-split; border-right: @border-width-base @border-style-base @border-color-split;
.@{menu-prefix-cls}-item { .@{menu-prefix-cls}-item {
border-right: @border-width-base @border-style-base @border-color-split; border-right: 3px @border-style-base transparent;
margin-left: -1px; margin-left: -1px;
left: 1px; left: 1px;
position: relative; position: relative;
@ -118,17 +124,26 @@
} }
} }
&-vertical &-sub { &-vertical&-sub {
border-right: 0; border-right: 0;
.@{menu-prefix-cls}-item { .@{menu-prefix-cls}-item {
border-right: 0; border-right: 0;
margin-left: 0;
left: 0;
}
> .@{menu-prefix-cls}-item:first-child {
border-radius: @border-radius-base @border-radius-base 0 0;
}
> .@{menu-prefix-cls}-item:last-child,
> .@{menu-prefix-cls}-item-group:last-child > .@{menu-prefix-cls}-item-group-list:last-child > .@{menu-prefix-cls}-item:last-child {
border-radius: 0 0 @border-radius-base @border-radius-base;
} }
} }
&-inline { &-inline {
.@{menu-prefix-cls}-selected, .@{menu-prefix-cls}-selected,
.@{menu-prefix-cls}-item-selected { .@{menu-prefix-cls}-item-selected {
border-right: 3px solid @primary-color; border-right-color: @primary-color;
transform: translateZ(0); transform: translateZ(0);
} }
} }
@ -247,6 +262,7 @@
&:hover, &:hover,
&-active, &-active,
&-open,
&-selected { &-selected {
border-bottom: 2px solid @primary-color; border-bottom: 2px solid @primary-color;
color: @primary-color; color: @primary-color;
@ -270,11 +286,10 @@
} }
&-vertical, &-vertical,
&-inline, &-inline {
&-item-group-list { .@{menu-prefix-cls}-item,
& > .@{menu-prefix-cls}-item, .@{menu-prefix-cls}-submenu-title {
& > .@{menu-prefix-cls}-submenu > .@{menu-prefix-cls}-submenu-title { padding: 0 16px;
padding: 0 16px 0 28px;
font-size: @font-size-base; font-size: @font-size-base;
line-height: 42px; line-height: 42px;
height: 42px; height: 42px;
@ -283,6 +298,13 @@
} }
} }
&-item-group-list {
.@{menu-prefix-cls}-item,
.@{menu-prefix-cls}-submenu-title {
padding: 0 16px 0 28px;
}
}
&-vertical&-sub { &-vertical&-sub {
padding: 0; padding: 0;
transform-origin: 0 0; transform-origin: 0 0;
@ -373,6 +395,7 @@
&-dark &-item:hover, &-dark &-item:hover,
&-dark &-item-active, &-dark &-item-active,
&-dark &-submenu-active, &-dark &-submenu-active,
&-dark &-submenu-open,
&-dark &-submenu-selected, &-dark &-submenu-selected,
&-dark &-submenu:hover, &-dark &-submenu:hover,
&-dark &-submenu-title:hover { &-dark &-submenu-title:hover {

View File

@ -9,22 +9,23 @@
width: 100%; width: 100%;
top: 16px; top: 16px;
left: 0; left: 0;
pointer-events: none;
&-notice { &-notice {
width: auto; padding: 8px;
vertical-align: middle; text-align: center;
position: absolute; &:first-child {
left: 50%; margin-top: -8px;
}
} }
&-notice-content { &-notice-content {
position: relative;
right: 50%;
padding: 8px 16px; padding: 8px 16px;
border-radius: @border-radius-base; border-radius: @border-radius-base;
box-shadow: @shadow-2; box-shadow: @shadow-2;
background: @component-background; background: @component-background;
display: block; display: inline-block;
pointer-events: all;
} }
&-success .@{iconfont-css-prefix} { &-success .@{iconfont-css-prefix} {
@ -50,4 +51,22 @@
top: 1px; top: 1px;
position: relative; position: relative;
} }
&-notice.move-up-leave.move-up-leave-active {
animation-name: MessageMoveOut;
overflow: hidden;
}
}
@keyframes MessageMoveOut {
0% {
opacity: 1;
max-height: 60px;
padding: 8px;
}
100% {
opacity: 0;
max-height: 0;
padding: 0;
}
} }

View File

@ -2,19 +2,23 @@
order: 2 order: 2
title: title:
zh-CN: 自定义页脚 zh-CN: 自定义页脚
en-US: Customized footer en-US: Customized Footer
--- ---
## zh-CN ## zh-CN
更复杂的例子,自定义了页脚的按钮,点击提交后进入 loading 状态,完成后关闭。 更复杂的例子,自定义了页脚的按钮,点击提交后进入 loading 状态,完成后关闭。
不需要默认确定取消按钮时,你可以把 `footer` 设为 `null`
## en-US ## en-US
A more complex example, as illustrated in this example, we define a customized footer button bar, A more complex example which define a customized footer button bar,
the dialog will change to loading state after clicking submit button , when the loading is over, the dialog will change to loading state after clicking submit button, when the loading is over,
the modal dialog will be closed. the modal dialog will be closed.
You could set `footer` to `null` if you don't need default footer buttons.
````jsx ````jsx
import { Modal, Button } from 'antd'; import { Modal, Button } from 'antd';

View File

@ -25,7 +25,7 @@ and so on.
| onOk | Specify a function that will be called when a user clicked OK button | function | no | | onOk | Specify a function that will be called when a user clicked OK button | function | no |
| onCancel | Specify a function that will be called when a user clicked mask, close button on top right or cancel button | function(e) | no | | onCancel | Specify a function that will be called when a user clicked mask, close button on top right or cancel button | function(e) | no |
| width | Width of a modal dialog | string\|number | 520 | | width | Width of a modal dialog | string\|number | 520 |
| footer | Footer content | string\|ReactNode | OK and cancel button | | footer | Footer content, set as `footer={null}` when you don't need default buttons | string\|ReactNode | OK and cancel button |
| okText | Text of the OK button | string | OK | | okText | Text of the OK button | string | OK |
| cancelText | Text of the Cancel button | string | Cancel | | cancelText | Text of the Cancel button | string | Cancel |
| maskClosable | Determine whether to close the modal dialog when clicked mask of it. | boolean | true | | maskClosable | Determine whether to close the modal dialog when clicked mask of it. | boolean | true |

View File

@ -24,7 +24,7 @@ title: Modal
| onOk | 点击确定回调 | function | 无 | | onOk | 点击确定回调 | function | 无 |
| onCancel | 点击遮罩层或右上角叉或取消按钮的回调 | function(e) | 无 | | onCancel | 点击遮罩层或右上角叉或取消按钮的回调 | function(e) | 无 |
| width | 宽度 | string\|number | 520 | | width | 宽度 | string\|number | 520 |
| footer | 底部内容 | string\|ReactNode | 确定取消按钮 | | footer | 底部内容,当不需要默认底部按钮时,可以设为 `footer={null}` | string\|ReactNode | 确定取消按钮 |
| okText | 确认按钮文字 | string | 确定 | | okText | 确认按钮文字 | string | 确定 |
| cancelText | 取消按钮文字 | string | 取消 | | cancelText | 取消按钮文字 | string | 取消 |
| maskClosable | 点击蒙层是否允许关闭 | boolean | true | | maskClosable | 点击蒙层是否允许关闭 | boolean | true |

View File

@ -322,7 +322,6 @@ exports[`test renders ./components/pagination/demo/mini.md correctly 1`] = `
<a /> <a />
</li> </li>
</ul> </ul>
<br />
<ul <ul
class="ant-pagination mini" class="ant-pagination mini"
unselectable="unselectable"> unselectable="unselectable">
@ -410,7 +409,6 @@ exports[`test renders ./components/pagination/demo/mini.md correctly 1`] = `
</div> </div>
</div> </div>
</ul> </ul>
<br />
<ul <ul
class="ant-pagination mini" class="ant-pagination mini"
unselectable="unselectable"> unselectable="unselectable">
@ -472,45 +470,57 @@ exports[`test renders ./components/pagination/demo/more.md correctly 1`] = `
class="ant-pagination " class="ant-pagination "
unselectable="unselectable"> unselectable="unselectable">
<li <li
class="ant-pagination-disabled ant-pagination-prev" class=" ant-pagination-prev"
title="上一页"> title="上一页">
<a /> <a />
</li> </li>
<li <li
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active" class="ant-pagination-item ant-pagination-item-1"
title="1"> title="1">
<a> <a>
1 1
</a> </a>
</li> </li>
<li <li
class="ant-pagination-item ant-pagination-item-2" class="ant-pagination-jump-prev"
title="2"> title="向前 5 页">
<a> <a />
2
</a>
</li> </li>
<li <li
class="ant-pagination-item ant-pagination-item-3" class="ant-pagination-item ant-pagination-item-4 ant-pagination-item-after-jump-prev"
title="3">
<a>
3
</a>
</li>
<li
class="ant-pagination-item ant-pagination-item-4"
title="4"> title="4">
<a> <a>
4 4
</a> </a>
</li> </li>
<li <li
class="ant-pagination-item ant-pagination-item-5 ant-pagination-item-before-jump-next" class="ant-pagination-item ant-pagination-item-5"
title="5"> title="5">
<a> <a>
5 5
</a> </a>
</li> </li>
<li
class="ant-pagination-item ant-pagination-item-6 ant-pagination-item-active"
title="6">
<a>
6
</a>
</li>
<li
class="ant-pagination-item ant-pagination-item-7"
title="7">
<a>
7
</a>
</li>
<li
class="ant-pagination-item ant-pagination-item-8 ant-pagination-item-before-jump-next"
title="8">
<a>
8
</a>
</li>
<li <li
class="ant-pagination-jump-next" class="ant-pagination-jump-next"
title="向后 5 页"> title="向后 5 页">

View File

@ -23,10 +23,14 @@ function showTotal(total) {
ReactDOM.render( ReactDOM.render(
<div> <div>
<Pagination size="small" total={50} /> <Pagination size="small" total={50} />
<br />
<Pagination size="small" total={50} showSizeChanger showQuickJumper /> <Pagination size="small" total={50} showSizeChanger showQuickJumper />
<br />
<Pagination size="small" total={50} showTotal={showTotal} /> <Pagination size="small" total={50} showTotal={showTotal} />
</div> </div>
, mountNode); , mountNode);
```` ````
<style>
#components-pagination-demo-mini .ant-pagination:not(:last-child) {
margin-bottom: 24px;
}
</style>

View File

@ -17,6 +17,6 @@ More pages.
import { Pagination } from 'antd'; import { Pagination } from 'antd';
ReactDOM.render( ReactDOM.render(
<Pagination defaultCurrent={1} total={500} /> <Pagination defaultCurrent={6} total={500} />
, mountNode); , mountNode);
```` ````

View File

@ -2,6 +2,7 @@
category: Components category: Components
type: Navigation type: Navigation
title: Pagination title: Pagination
cols: 1
--- ---
A long list can be divided into several pages by `Pagination`, and only one page will be loaded at a time. A long list can be divided into several pages by `Pagination`, and only one page will be loaded at a time.

View File

@ -3,6 +3,7 @@ category: Components
subtitle: 分页 subtitle: 分页
type: Navigation type: Navigation
title: Pagination title: Pagination
cols: 1
--- ---
采用分页的形式分隔长列表,每次只加载一个页面。 采用分页的形式分隔长列表,每次只加载一个页面。

View File

@ -49,7 +49,7 @@ export default class Radio extends React.Component<RadioProps, any> {
onMouseLeave={this.props.onMouseLeave} onMouseLeave={this.props.onMouseLeave}
> >
<RcRadio {...this.props} className={classString} style={null} children={null} /> <RcRadio {...this.props} className={classString} style={null} children={null} />
{children ? <span>{children}</span> : null} {children !== undefined ? <span>{children}</span> : null}
</label> </label>
); );
} }

View File

@ -115,7 +115,7 @@ span.@{radio-prefix-cls} + * {
.@{radio-prefix-cls}-button-wrapper { .@{radio-prefix-cls}-button-wrapper {
margin: 0; margin: 0;
height: @input-height-base; height: @input-height-base;
line-height: 26px; line-height: @input-height-base - 2;
color: @btn-default-color; color: @btn-default-color;
display: inline-block; display: inline-block;
transition: all 0.3s ease; transition: all 0.3s ease;
@ -138,12 +138,12 @@ span.@{radio-prefix-cls} + * {
.@{radio-group-prefix-cls}-large & { .@{radio-group-prefix-cls}-large & {
height: @input-height-lg; height: @input-height-lg;
line-height: 30px; line-height: @input-height-lg - 2px;
} }
.@{radio-group-prefix-cls}-small & { .@{radio-group-prefix-cls}-small & {
height: @input-height-sm; height: @input-height-sm;
line-height: 20px; line-height: @input-height-sm - 2;
padding: 0 12px; padding: 0 12px;
&:first-child { &:first-child {
border-radius: @border-radius-sm 0 0 @border-radius-sm; border-radius: @border-radius-sm 0 0 @border-radius-sm;

View File

@ -323,7 +323,7 @@ exports[`test renders ./components/select/demo/optgroup.md correctly 1`] = `
exports[`test renders ./components/select/demo/search.md correctly 1`] = ` exports[`test renders ./components/select/demo/search.md correctly 1`] = `
<div <div
class="ant-select-show-search ant-select ant-select-enabled" class="ant-select ant-select-enabled"
style="width:200px;"> style="width:200px;">
<div <div
aria-autocomplete="list" aria-autocomplete="list"

View File

@ -1,6 +1,6 @@
--- ---
order: 5 order: 5
title: title:
zh-CN: 分组 zh-CN: 分组
en-US: Option Group en-US: Option Group
--- ---
@ -23,9 +23,9 @@ function handleChange(value) {
} }
ReactDOM.render( ReactDOM.render(
<Select defaultValue="lucy" <Select
defaultValue="lucy"
style={{ width: 200 }} style={{ width: 200 }}
showSearch={false}
onChange={handleChange} onChange={handleChange}
> >
<OptGroup label="Manager"> <OptGroup label="Manager">

View File

@ -92,7 +92,6 @@ export default class Select extends React.Component<SelectProps, any> {
className = '', className = '',
size, size,
combobox, combobox,
showSearch,
} = this.props; } = this.props;
let { notFoundContent = 'Not Found', optionLabelProp } = this.props; let { notFoundContent = 'Not Found', optionLabelProp } = this.props;
@ -100,7 +99,6 @@ export default class Select extends React.Component<SelectProps, any> {
const cls = classNames({ const cls = classNames({
[`${prefixCls}-lg`]: size === 'large', [`${prefixCls}-lg`]: size === 'large',
[`${prefixCls}-sm`]: size === 'small', [`${prefixCls}-sm`]: size === 'small',
[`${prefixCls}-show-search`]: showSearch,
}, className); }, className);
const { antLocale } = this.context; const { antLocale } = this.context;

View File

@ -5,12 +5,12 @@ exports[`test renders ./components/slider/demo/basic.md correctly 1`] = `
<div <div
class="ant-slider-rail" /> class="ant-slider-rail" />
<div <div
class="ant-slider-track ant-slider-track-1" class="ant-slider-track"
style="visibility:visible;left:0%;width:30%;" /> style="visibility:visible;left:0%;width:30%;" />
<div <div
class="ant-slider-step" /> class="ant-slider-step" />
<div <div
class="ant-slider-handle ant-slider-handle-2 ant-slider-handle-upper" class="ant-slider-handle"
style="left:30%;" /> style="left:30%;" />
<div <div
class="ant-slider-mark" /> class="ant-slider-mark" />
@ -25,10 +25,10 @@ exports[`test renders ./components/slider/demo/basic.md correctly 1`] = `
<div <div
class="ant-slider-step" /> class="ant-slider-step" />
<div <div
class="ant-slider-handle ant-slider-handle-1 ant-slider-handle-lower" class="ant-slider-handle ant-slider-handle-1"
style="left:20%;" /> style="left:20%;" />
<div <div
class="ant-slider-handle ant-slider-handle-2 ant-slider-handle-upper" class="ant-slider-handle ant-slider-handle-2"
style="left:50%;" /> style="left:50%;" />
<div <div
class="ant-slider-mark" /> class="ant-slider-mark" />
@ -50,12 +50,12 @@ exports[`test renders ./components/slider/demo/event.md correctly 1`] = `
<div <div
class="ant-slider-rail" /> class="ant-slider-rail" />
<div <div
class="ant-slider-track ant-slider-track-1" class="ant-slider-track"
style="visibility:visible;left:0%;width:30%;" /> style="visibility:visible;left:0%;width:30%;" />
<div <div
class="ant-slider-step" /> class="ant-slider-step" />
<div <div
class="ant-slider-handle ant-slider-handle-2 ant-slider-handle-upper" class="ant-slider-handle"
style="left:30%;" /> style="left:30%;" />
<div <div
class="ant-slider-mark" /> class="ant-slider-mark" />
@ -70,10 +70,10 @@ exports[`test renders ./components/slider/demo/event.md correctly 1`] = `
<div <div
class="ant-slider-step" /> class="ant-slider-step" />
<div <div
class="ant-slider-handle ant-slider-handle-1 ant-slider-handle-lower" class="ant-slider-handle ant-slider-handle-1"
style="left:20%;" /> style="left:20%;" />
<div <div
class="ant-slider-handle ant-slider-handle-2 ant-slider-handle-upper" class="ant-slider-handle ant-slider-handle-2"
style="left:50%;" /> style="left:50%;" />
<div <div
class="ant-slider-mark" /> class="ant-slider-mark" />
@ -91,12 +91,12 @@ exports[`test renders ./components/slider/demo/icon-slider.md correctly 1`] = `
<div <div
class="ant-slider-rail" /> class="ant-slider-rail" />
<div <div
class="ant-slider-track ant-slider-track-1" class="ant-slider-track"
style="visibility:visible;left:0%;width:0%;" /> style="visibility:visible;left:0%;width:0%;" />
<div <div
class="ant-slider-step" /> class="ant-slider-step" />
<div <div
class="ant-slider-handle ant-slider-handle-2 ant-slider-handle-upper" class="ant-slider-handle"
style="left:0%;" /> style="left:0%;" />
<div <div
class="ant-slider-mark" /> class="ant-slider-mark" />
@ -117,12 +117,12 @@ exports[`test renders ./components/slider/demo/input-number.md correctly 1`] = `
<div <div
class="ant-slider-rail" /> class="ant-slider-rail" />
<div <div
class="ant-slider-track ant-slider-track-1" class="ant-slider-track"
style="visibility:visible;left:0%;width:0%;" /> style="visibility:visible;left:0%;width:0%;" />
<div <div
class="ant-slider-step" /> class="ant-slider-step" />
<div <div
class="ant-slider-handle ant-slider-handle-2 ant-slider-handle-upper" class="ant-slider-handle"
style="left:0%;" /> style="left:0%;" />
<div <div
class="ant-slider-mark" /> class="ant-slider-mark" />
@ -171,12 +171,12 @@ exports[`test renders ./components/slider/demo/input-number.md correctly 1`] = `
<div <div
class="ant-slider-rail" /> class="ant-slider-rail" />
<div <div
class="ant-slider-track ant-slider-track-1" class="ant-slider-track"
style="visibility:visible;left:0%;width:0%;" /> style="visibility:visible;left:0%;width:0%;" />
<div <div
class="ant-slider-step" /> class="ant-slider-step" />
<div <div
class="ant-slider-handle ant-slider-handle-2 ant-slider-handle-upper" class="ant-slider-handle"
style="left:0%;" /> style="left:0%;" />
<div <div
class="ant-slider-mark" /> class="ant-slider-mark" />
@ -229,7 +229,7 @@ exports[`test renders ./components/slider/demo/mark.md correctly 1`] = `
<div <div
class="ant-slider-rail" /> class="ant-slider-rail" />
<div <div
class="ant-slider-track ant-slider-track-1" class="ant-slider-track"
style="visibility:visible;left:0%;width:37%;" /> style="visibility:visible;left:0%;width:37%;" />
<div <div
class="ant-slider-step"> class="ant-slider-step">
@ -247,7 +247,7 @@ exports[`test renders ./components/slider/demo/mark.md correctly 1`] = `
style="left:100%;" /> style="left:100%;" />
</div> </div>
<div <div
class="ant-slider-handle ant-slider-handle-2 ant-slider-handle-upper" class="ant-slider-handle"
style="left:37%;" /> style="left:37%;" />
<div <div
class="ant-slider-mark"> class="ant-slider-mark">
@ -298,10 +298,10 @@ exports[`test renders ./components/slider/demo/mark.md correctly 1`] = `
style="left:100%;" /> style="left:100%;" />
</div> </div>
<div <div
class="ant-slider-handle ant-slider-handle-1 ant-slider-handle-lower" class="ant-slider-handle ant-slider-handle-1"
style="left:26%;" /> style="left:26%;" />
<div <div
class="ant-slider-handle ant-slider-handle-2 ant-slider-handle-upper" class="ant-slider-handle ant-slider-handle-2"
style="left:37%;" /> style="left:37%;" />
<div <div
class="ant-slider-mark"> class="ant-slider-mark">
@ -337,7 +337,7 @@ exports[`test renders ./components/slider/demo/mark.md correctly 1`] = `
<div <div
class="ant-slider-rail" /> class="ant-slider-rail" />
<div <div
class="ant-slider-track ant-slider-track-1" class="ant-slider-track"
style="visibility:hidden;left:0%;width:37%;" /> style="visibility:hidden;left:0%;width:37%;" />
<div <div
class="ant-slider-step"> class="ant-slider-step">
@ -355,7 +355,7 @@ exports[`test renders ./components/slider/demo/mark.md correctly 1`] = `
style="left:100%;" /> style="left:100%;" />
</div> </div>
<div <div
class="ant-slider-handle ant-slider-handle-2 ant-slider-handle-upper" class="ant-slider-handle"
style="left:37%;" /> style="left:37%;" />
<div <div
class="ant-slider-mark"> class="ant-slider-mark">
@ -391,7 +391,7 @@ exports[`test renders ./components/slider/demo/mark.md correctly 1`] = `
<div <div
class="ant-slider-rail" /> class="ant-slider-rail" />
<div <div
class="ant-slider-track ant-slider-track-1" class="ant-slider-track"
style="visibility:visible;left:0%;width:37%;" /> style="visibility:visible;left:0%;width:37%;" />
<div <div
class="ant-slider-step"> class="ant-slider-step">
@ -409,7 +409,7 @@ exports[`test renders ./components/slider/demo/mark.md correctly 1`] = `
style="left:100%;" /> style="left:100%;" />
</div> </div>
<div <div
class="ant-slider-handle ant-slider-handle-2 ant-slider-handle-upper" class="ant-slider-handle"
style="left:37%;" /> style="left:37%;" />
<div <div
class="ant-slider-mark"> class="ant-slider-mark">
@ -445,7 +445,7 @@ exports[`test renders ./components/slider/demo/mark.md correctly 1`] = `
<div <div
class="ant-slider-rail" /> class="ant-slider-rail" />
<div <div
class="ant-slider-track ant-slider-track-1" class="ant-slider-track"
style="visibility:visible;left:0%;width:37%;" /> style="visibility:visible;left:0%;width:37%;" />
<div <div
class="ant-slider-step"> class="ant-slider-step">
@ -463,7 +463,7 @@ exports[`test renders ./components/slider/demo/mark.md correctly 1`] = `
style="left:100%;" /> style="left:100%;" />
</div> </div>
<div <div
class="ant-slider-handle ant-slider-handle-2 ant-slider-handle-upper" class="ant-slider-handle"
style="left:37%;" /> style="left:37%;" />
<div <div
class="ant-slider-mark"> class="ant-slider-mark">
@ -501,12 +501,12 @@ exports[`test renders ./components/slider/demo/tip-formatter.md correctly 1`] =
<div <div
class="ant-slider-rail" /> class="ant-slider-rail" />
<div <div
class="ant-slider-track ant-slider-track-1" class="ant-slider-track"
style="visibility:visible;left:0%;width:0%;" /> style="visibility:visible;left:0%;width:0%;" />
<div <div
class="ant-slider-step" /> class="ant-slider-step" />
<div <div
class="ant-slider-handle ant-slider-handle-2 ant-slider-handle-upper" class="ant-slider-handle"
style="left:0%;" /> style="left:0%;" />
<div <div
class="ant-slider-mark" /> class="ant-slider-mark" />
@ -516,12 +516,12 @@ exports[`test renders ./components/slider/demo/tip-formatter.md correctly 1`] =
<div <div
class="ant-slider-rail" /> class="ant-slider-rail" />
<div <div
class="ant-slider-track ant-slider-track-1" class="ant-slider-track"
style="visibility:visible;left:0%;width:0%;" /> style="visibility:visible;left:0%;width:0%;" />
<div <div
class="ant-slider-step" /> class="ant-slider-step" />
<div <div
class="ant-slider-handle ant-slider-handle-2 ant-slider-handle-upper" class="ant-slider-handle"
style="left:0%;" /> style="left:0%;" />
<div <div
class="ant-slider-mark" /> class="ant-slider-mark" />
@ -539,12 +539,12 @@ exports[`test renders ./components/slider/demo/vertical.md correctly 1`] = `
<div <div
class="ant-slider-rail" /> class="ant-slider-rail" />
<div <div
class="ant-slider-track ant-slider-track-1" class="ant-slider-track"
style="visibility:visible;bottom:0%;height:30%;" /> style="visibility:visible;bottom:0%;height:30%;" />
<div <div
class="ant-slider-step" /> class="ant-slider-step" />
<div <div
class="ant-slider-handle ant-slider-handle-2 ant-slider-handle-upper" class="ant-slider-handle"
style="bottom:30%;" /> style="bottom:30%;" />
<div <div
class="ant-slider-mark" /> class="ant-slider-mark" />
@ -562,10 +562,10 @@ exports[`test renders ./components/slider/demo/vertical.md correctly 1`] = `
<div <div
class="ant-slider-step" /> class="ant-slider-step" />
<div <div
class="ant-slider-handle ant-slider-handle-1 ant-slider-handle-lower" class="ant-slider-handle ant-slider-handle-1"
style="bottom:20%;" /> style="bottom:20%;" />
<div <div
class="ant-slider-handle ant-slider-handle-2 ant-slider-handle-upper" class="ant-slider-handle ant-slider-handle-2"
style="bottom:50%;" /> style="bottom:50%;" />
<div <div
class="ant-slider-mark" /> class="ant-slider-mark" />
@ -596,10 +596,10 @@ exports[`test renders ./components/slider/demo/vertical.md correctly 1`] = `
style="bottom:100%;" /> style="bottom:100%;" />
</div> </div>
<div <div
class="ant-slider-handle ant-slider-handle-1 ant-slider-handle-lower" class="ant-slider-handle ant-slider-handle-1"
style="bottom:26%;" /> style="bottom:26%;" />
<div <div
class="ant-slider-handle ant-slider-handle-2 ant-slider-handle-upper" class="ant-slider-handle ant-slider-handle-2"
style="bottom:37%;" /> style="bottom:37%;" />
<div <div
class="ant-slider-mark"> class="ant-slider-mark">

View File

@ -1,6 +1,8 @@
import React from 'react'; import React from 'react';
import { PropTypes } from 'react'; import RcSlider from 'rc-slider/lib/Slider';
import RcSlider from 'rc-slider'; import RcRange from 'rc-slider/lib/Range';
import RcHandle from 'rc-slider/lib/Handle';
import Tooltip from '../tooltip';
export interface SliderMarks { export interface SliderMarks {
[key: number]: React.ReactNode | { [key: number]: React.ReactNode | {
@ -12,6 +14,8 @@ export interface SliderMarks {
export type SliderValue = number | [number, number]; export type SliderValue = number | [number, number];
export interface SliderProps { export interface SliderProps {
prefixCls?: string;
tooltipPrefixCls?: string;
range?: boolean; range?: boolean;
min?: number; min?: number;
max?: number; max?: number;
@ -32,15 +36,46 @@ export default class Slider extends React.Component<SliderProps, any> {
static defaultProps = { static defaultProps = {
prefixCls: 'ant-slider', prefixCls: 'ant-slider',
tooltipPrefixCls: 'ant-tooltip', tooltipPrefixCls: 'ant-tooltip',
tipTransitionName: 'zoom-down', tipFormatter(value) {
return value.toString();
},
}; };
static propTypes = { constructor(props) {
prefixCls: PropTypes.string, super(props);
tipTransitionName: PropTypes.string, this.state = { visibles: {} };
}; }
handleTooltipVisibleChange = (index, visible) => {
this.setState({
visibles: {
...this.state.visibles,
[index]: visible,
},
});
}
handleWithTooltip = ({ value, dragging, index, ...restProps }) => {
const { tooltipPrefixCls, tipFormatter } = this.props;
return (
<Tooltip
prefixCls={tooltipPrefixCls}
title={tipFormatter ? tipFormatter(value) : ''}
visible={tipFormatter && (this.state.visibles[index] || dragging)}
onVisibleChange={visible => this.handleTooltipVisibleChange(index, visible)}
placement="top"
transitionName="zoom-down"
key={index}
>
<RcHandle {...restProps} />
</Tooltip>
);
}
render() { render() {
return <RcSlider {...this.props} />; const { range, ...restProps } = this.props;
if (range) {
return <RcRange {...restProps} handle={this.handleWithTooltip} />;
}
return <RcSlider {...restProps} handle={this.handleWithTooltip} />;
} }
} }

View File

@ -75,11 +75,12 @@
&-blur { &-blur {
opacity: 0.7; opacity: 0.7;
-webkit-filter: blur(1px); -webkit-filter: blur(0.8px);
filter: blur(1px); filter: blur(0.8px);
/* IE6~IE9 */ /* autoprefixer: off */
filter: ~"progid\:DXImageTransform\.Microsoft\.Blur(PixelRadius\=1, MakeShadow\=false)"; // lesshint duplicateProperty: false filter: ~"progid\:DXImageTransform\.Microsoft\.Blur(PixelRadius\=1, MakeShadow\=false)";
/* autoprefixer: on */
// workround for a strange style bug in safari: // workround for a strange style bug in safari:
// https://github.com/ant-design/ant-design/issues/4622 // https://github.com/ant-design/ant-design/issues/4622
// have no clue why this works // have no clue why this works
@ -91,7 +92,9 @@
right: 0; right: 0;
top: 0; top: 0;
bottom: 0; bottom: 0;
background: transparent; background: #fff;
opacity: 0.2;
transition: all .3s;
} }
} }

View File

@ -10,6 +10,6 @@
.ant-motion-collapse { .ant-motion-collapse {
overflow: hidden; overflow: hidden;
&-active { &-active {
transition: height .2s @ease-out; transition: height .24s ease-in;
} }
} }

View File

@ -504,7 +504,7 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
}); });
} }
handlePageChange = (current) => { handlePageChange = (current, ...otherArguments) => {
const props = this.props; const props = this.props;
let pagination = assign({}, this.state.pagination); let pagination = assign({}, this.state.pagination);
if (current) { if (current) {
@ -512,7 +512,7 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
} else { } else {
pagination.current = pagination.current || 1; pagination.current = pagination.current || 1;
} }
pagination.onChange(pagination.current); pagination.onChange(pagination.current, ...otherArguments);
const newState = { const newState = {
pagination, pagination,
@ -724,6 +724,7 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
let total = pagination.total || this.getLocalData().length; let total = pagination.total || this.getLocalData().length;
return (total > 0) ? return (total > 0) ?
<Pagination <Pagination
key="pagination"
{...pagination} {...pagination}
className={`${this.props.prefixCls}-pagination`} className={`${this.props.prefixCls}-pagination`}
onChange={this.handlePageChange} onChange={this.handlePageChange}
@ -734,9 +735,12 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
/> : null; /> : null;
} }
// Get pagination, filters, sorter
prepareParamsArguments(state: any): [any, string[], Object] { prepareParamsArguments(state: any): [any, string[], Object] {
// 准备筛选、排序、分页的参数 const pagination = { ...state.pagination };
const pagination = state.pagination; // remove useless handle function in Table.onChange
delete pagination.onChange;
delete pagination.onShowSizeChange;
const filters = state.filters; const filters = state.filters;
const sorter: any = {}; const sorter: any = {};
if (state.sortColumn && state.sortOrder) { if (state.sortColumn && state.sortOrder) {
@ -858,8 +862,9 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
expandIconColumnIndex = restProps.expandIconColumnIndex as number; expandIconColumnIndex = restProps.expandIconColumnIndex as number;
} }
let table = ( const table = (
<RcTable <RcTable
key="table"
{...restProps} {...restProps}
prefixCls={prefixCls} prefixCls={prefixCls}
data={data} data={data}
@ -871,23 +876,27 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
emptyText={() => locale.emptyText} emptyText={() => locale.emptyText}
/> />
); );
// if there is no pagination or no data, // if there is no pagination or no data,
// the height of spin should decrease by half of pagination // the height of spin should decrease by half of pagination
const paginationPatchClass = (this.hasPagination() && data && data.length !== 0) const paginationPatchClass = (this.hasPagination() && data && data.length !== 0)
? `${prefixCls}-with-pagination` ? `${prefixCls}-with-pagination` : `${prefixCls}-without-pagination`;
: `${prefixCls}-without-pagination`;
let loading = this.props.loading; let loading = this.props.loading;
if (typeof (loading) === 'boolean') { if (typeof loading === 'boolean') {
loading = { loading = {
spinning: loading, spinning: loading,
}; };
} }
const spinClassName = this.props.loading ? `${paginationPatchClass} ${prefixCls}-spin-holder` : '';
return ( return (
<div className={`${className} clearfix`} style={style}> <div className={classNames(`${prefixCls}-wrapper`, className)} style={style}>
<Spin className={spinClassName} {...loading}> <Spin
{table} {...loading}
{this.renderPagination()} className={loading ? `${paginationPatchClass} ${prefixCls}-spin-holder` : ''}
>
{table}
{this.renderPagination()}
</Spin> </Spin>
</div> </div>
); );

View File

@ -56,9 +56,10 @@ describe('Table.pagination', () => {
it('fires change event', () => { it('fires change event', () => {
const handleChange = jest.fn(); const handleChange = jest.fn();
const handlePaginationChange = jest.fn();
const noop = () => {}; const noop = () => {};
const wrapper = mount(createTable({ const wrapper = mount(createTable({
pagination: { ...pagination, onChange: noop, onShowSizeChange: noop }, pagination: { ...pagination, onChange: handlePaginationChange, onShowSizeChange: noop },
onChange: handleChange, onChange: handleChange,
})); }));
@ -67,13 +68,13 @@ describe('Table.pagination', () => {
expect(handleChange).toBeCalledWith( expect(handleChange).toBeCalledWith(
{ {
current: 2, current: 2,
onChange: noop,
onShowSizeChange: noop,
pageSize: 2, pageSize: 2,
}, },
{}, {},
{} {}
); );
expect(handlePaginationChange).toBeCalledWith(2, 2);
}); });
// https://github.com/ant-design/ant-design/issues/4532 // https://github.com/ant-design/ant-design/issues/4532

View File

@ -14,7 +14,7 @@ exports[`Table.filter renders custom content correctly 1`] = `
exports[`Table.filter renders filter correctly 1`] = ` exports[`Table.filter renders filter correctly 1`] = `
<div <div
class=" clearfix"> class="ant-table-wrapper">
<div <div
class="ant-spin-nested-loading"> class="ant-spin-nested-loading">
<div <div
@ -24,75 +24,70 @@ exports[`Table.filter renders filter correctly 1`] = `
<div <div
class="ant-table-content"> class="ant-table-content">
<div <div
class=""> class="ant-table-body">
<span> <table
<div class="">
class="ant-table-body"> <colgroup>
<table <col />
class=""> </colgroup>
<colgroup> <thead
<col /> class="ant-table-thead">
</colgroup> <tr>
<thead <th
class="ant-table-thead"> class="">
<tr> <span>
<th Name
class=""> <i
<span> class="anticon anticon-filter ant-dropdown-trigger"
Name title="筛选" />
<i </span>
class="anticon anticon-filter ant-dropdown-trigger" </th>
title="筛选" /> </tr>
</span> </thead>
</th> <tbody
</tr> class="ant-table-tbody">
</thead> <tr
<tbody class="ant-table-row ant-table-row-level-0">
class="ant-table-tbody"> <td
<tr class="">
class="ant-table-row ant-table-row-level-0"> <span
<td class="ant-table-row-indent indent-level-0"
class=""> style="padding-left:0px;" />
<span Jack
class="ant-table-row-indent indent-level-0" </td>
style="padding-left:0px;" /> </tr>
Jack <tr
</td> class="ant-table-row ant-table-row-level-0">
</tr> <td
<tr class="">
class="ant-table-row ant-table-row-level-0"> <span
<td class="ant-table-row-indent indent-level-0"
class=""> style="padding-left:0px;" />
<span Lucy
class="ant-table-row-indent indent-level-0" </td>
style="padding-left:0px;" /> </tr>
Lucy <tr
</td> class="ant-table-row ant-table-row-level-0">
</tr> <td
<tr class="">
class="ant-table-row ant-table-row-level-0"> <span
<td class="ant-table-row-indent indent-level-0"
class=""> style="padding-left:0px;" />
<span Tom
class="ant-table-row-indent indent-level-0" </td>
style="padding-left:0px;" /> </tr>
Tom <tr
</td> class="ant-table-row ant-table-row-level-0">
</tr> <td
<tr class="">
class="ant-table-row ant-table-row-level-0"> <span
<td class="ant-table-row-indent indent-level-0"
class=""> style="padding-left:0px;" />
<span Jerry
class="ant-table-row-indent indent-level-0" </td>
style="padding-left:0px;" /> </tr>
Jerry </tbody>
</td> </table>
</tr>
</tbody>
</table>
</div>
</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,6 +1,6 @@
exports[`Table.pagination renders pagination correctly 1`] = ` exports[`Table.pagination renders pagination correctly 1`] = `
<div <div
class=" clearfix"> class="ant-table-wrapper">
<div <div
class="ant-spin-nested-loading"> class="ant-spin-nested-loading">
<div <div
@ -10,52 +10,47 @@ exports[`Table.pagination renders pagination correctly 1`] = `
<div <div
class="ant-table-content"> class="ant-table-content">
<div <div
class=""> class="ant-table-body">
<span> <table
<div class="">
class="ant-table-body"> <colgroup>
<table <col />
class=""> </colgroup>
<colgroup> <thead
<col /> class="ant-table-thead">
</colgroup> <tr>
<thead <th
class="ant-table-thead"> class="">
<tr> <span>
<th Name
class=""> </span>
<span> </th>
Name </tr>
</span> </thead>
</th> <tbody
</tr> class="ant-table-tbody">
</thead> <tr
<tbody class="ant-table-row ant-table-row-level-0">
class="ant-table-tbody"> <td
<tr class="">
class="ant-table-row ant-table-row-level-0"> <span
<td class="ant-table-row-indent indent-level-0"
class=""> style="padding-left:0px;" />
<span Jack
class="ant-table-row-indent indent-level-0" </td>
style="padding-left:0px;" /> </tr>
Jack <tr
</td> class="ant-table-row ant-table-row-level-0">
</tr> <td
<tr class="">
class="ant-table-row ant-table-row-level-0"> <span
<td class="ant-table-row-indent indent-level-0"
class=""> style="padding-left:0px;" />
<span Lucy
class="ant-table-row-indent indent-level-0" </td>
style="padding-left:0px;" /> </tr>
Lucy </tbody>
</td> </table>
</tr>
</tbody>
</table>
</div>
</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,6 +1,6 @@
exports[`Table renders JSX correctly 1`] = ` exports[`Table renders JSX correctly 1`] = `
<div <div
class=" clearfix"> class="ant-table-wrapper">
<div <div
class="ant-spin-nested-loading"> class="ant-spin-nested-loading">
<div <div
@ -10,92 +10,87 @@ exports[`Table renders JSX correctly 1`] = `
<div <div
class="ant-table-content"> class="ant-table-content">
<div <div
class=""> class="ant-table-body">
<span> <table
<div class="">
class="ant-table-body"> <colgroup>
<table <col />
class=""> <col />
<colgroup> <col />
<col /> </colgroup>
<col /> <thead
<col /> class="ant-table-thead">
</colgroup> <tr>
<thead <th
class="ant-table-thead"> class=""
<tr> colspan="2">
<th <span>
class="" Name
colspan="2"> </span>
<span> </th>
Name <th
</span> class=""
</th> rowspan="2">
<th <span>
class="" Age
rowspan="2"> </span>
<span> </th>
Age </tr>
</span> <tr>
</th> <th
</tr> class="">
<tr> <span>
<th First Name
class=""> </span>
<span> </th>
First Name <th
</span> class="">
</th> <span>
<th Last Name
class=""> </span>
<span> </th>
Last Name </tr>
</span> </thead>
</th> <tbody
</tr> class="ant-table-tbody">
</thead> <tr
<tbody class="ant-table-row ant-table-row-level-0">
class="ant-table-tbody"> <td
<tr class="">
class="ant-table-row ant-table-row-level-0"> <span
<td class="ant-table-row-indent indent-level-0"
class=""> style="padding-left:0px;" />
<span John
class="ant-table-row-indent indent-level-0" </td>
style="padding-left:0px;" /> <td
John class="">
</td> Brown
<td </td>
class=""> <td
Brown class="">
</td> 32
<td </td>
class=""> </tr>
32 <tr
</td> class="ant-table-row ant-table-row-level-0">
</tr> <td
<tr class="">
class="ant-table-row ant-table-row-level-0"> <span
<td class="ant-table-row-indent indent-level-0"
class=""> style="padding-left:0px;" />
<span Jim
class="ant-table-row-indent indent-level-0" </td>
style="padding-left:0px;" /> <td
Jim class="">
</td> Green
<td </td>
class=""> <td
Green class="">
</td> 42
<td </td>
class=""> </tr>
42 </tbody>
</td> </table>
</tr>
</tbody>
</table>
</div>
</span>
</div> </div>
</div> </div>
</div> </div>

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
--- ---
order: 24 order: 25
title: title:
en-US: Dynamic Settings en-US: Dynamic Settings
zh-CN: 动态控制表格属性 zh-CN: 动态控制表格属性

View File

@ -9,18 +9,22 @@ title:
适合同时展示有大量数据和数据列。 适合同时展示有大量数据和数据列。
> 若列头与内容不对齐,请指定列宽度 `width` > 若列头与内容不对齐,请指定列宽度 `width`
> 建议指定 `scroll.x` 为固定宽度。注意,非固定列宽度之和不要超过 `scroll.x` > 建议指定 `scroll.x` 为固定宽度。注意,非固定列宽度之和不要超过 `scroll.x`
> 如果希望 `scroll.x` 为自适应内容宽度,可以指定 `scroll={{ x: true }}`,然后使用 `white-space: nowrap` 样式强制单元格不自动换行。
## en-US ## en-US
Suitable for large amounts of data with long columns. A Solution for displaying large amounts of data with long columns.
> Specify the width of each column if header and cell do not align properly. > Specify the width of columns if header and cell do not align properly.
> A fixed width for `scroll.x` is recommended. The sum of unfixed columns should not greater than `scroll.x`. > A fixed width for `scroll.x` is recommended. The sum of unfixed columns should not greater than `scroll.x`.
> If you hope the `scroll.x` will adapt content's witdh, you can set `scroll={{ x: true }}` ant then use css `white-space: nowrap` to make sure table cell will not be wrapped.
````jsx ````jsx
import { Table } from 'antd'; import { Table } from 'antd';

View File

@ -9,17 +9,21 @@ title:
对于列数很多的数据,可以固定前后的列,横向滚动查看其它数据,需要和 `scroll.x` 配合使用。 对于列数很多的数据,可以固定前后的列,横向滚动查看其它数据,需要和 `scroll.x` 配合使用。
> 若列头与内容不对齐,请指定列宽度 `width` > 若列头与内容不对齐,请指定列宽度 `width`
> 建议指定 scroll.x 为固定宽度。 > 建议指定 `scroll.x` 为固定宽度。注意,非固定列宽度之和不要超过 `scroll.x`
> 如果希望 `scroll.x` 为自适应内容宽度,可以指定 `scroll={{ x: true }}`,然后使用 `white-space: nowrap` 样式强制单元格不自动换行。
## en-US ## en-US
Fix some columns and scroll in other columns. You must set `scoll.x` meanwhile. To fix some columns and scroll inside other columns, and you must set `scoll.x` meanwhile.
> Specify the width of each column if header and cell do not align properly. > Specify the width of columns if header and cell do not align properly.
> A fixed width for `scroll.x` is recommended. > A fixed width for `scroll.x` is recommended. The sum of unfixed columns should not greater than `scroll.x`.
> If you hope the `scroll.x` will adapt content's witdh, you can set `scroll={{ x: true }}` ant then use css `white-space: nowrap` to make sure table cell will not be wrapped.
````jsx ````jsx
import { Table } from 'antd'; import { Table } from 'antd';

View File

@ -0,0 +1,138 @@
---
order: 24
title:
en-US: Nested tables
zh-CN: 嵌套子表格
---
## zh-CN
展示每行数据更详细的信息。
## en-US
Showing more detailed info of every row.
````jsx
import { Table, Badge, Menu, Dropdown, Icon } from 'antd';
const menu = (
<Menu>
<Menu.Item>
Action 1
</Menu.Item>
<Menu.Item>
Action 2
</Menu.Item>
</Menu>
);
function NestedTable() {
const expandedRowRender = () => {
const columns = [
{ title: 'Date', dataIndex: 'date', key: 'date' },
{ title: 'Name', dataIndex: 'name', key: 'name' },
{ title: 'Status', key: 'state', render: () => <span><Badge status="success" />Finished</span> },
{ title: 'Upgrade Status', dataIndex: 'upgradeNum', key: 'upgradeNum' },
{
title: 'Action',
dataIndex: 'operation',
key: 'operation',
render: () => (
<span className={'table-operation'}>
<a href="#">Pause</a>
<a href="#">Stop</a>
<Dropdown overlay={menu}>
<a href="#">
More <Icon type="down" />
</a>
</Dropdown>
</span>
),
},
];
const data = [];
for (let i = 0; i < 3; ++i) {
data.push({
key: i,
date: '2014-12-24 23:12:00',
name: 'This is production name',
upgradeNum: 'Upgraded: 56',
});
}
return (
<Table
columns={columns}
dataSource={data}
pagination={false}
/>
);
};
const columns = [
{ title: 'Name', dataIndex: 'name', key: 'name' },
{ title: 'Platform', dataIndex: 'platform', key: 'platform' },
{ title: 'Version', dataIndex: 'version', key: 'version' },
{ title: 'Upgraded', dataIndex: 'upgradeNum', key: 'upgradeNum' },
{ title: 'Creator', dataIndex: 'creator', key: 'creator' },
{ title: 'Date', dataIndex: 'createdAt', key: 'createdAt' },
{ title: 'Action', key: 'operation', render: () => <a href="#">Publish</a> },
];
const data = [];
for (let i = 0; i < 3; ++i) {
data.push({
key: i,
name: 'Screem',
platform: 'iOS',
version: '10.3.4.5654',
upgradeNum: 500,
creator: 'Jack',
createdAt: '2014-12-24 23:12:00',
});
}
return (
<Table
className="components-table-demo-nested"
columns={columns}
expandedRowRender={expandedRowRender}
dataSource={data}
/>
);
}
ReactDOM.render(<NestedTable />, mountNode);
````
````css
.components-table-demo-nested .ant-table-expanded-row > td:last-child {
padding: 0 48px 0 8px;
}
.components-table-demo-nested .ant-table-expanded-row > td:last-child .ant-table-thead th {
border-bottom: 1px solid #e9e9e9;
}
.components-table-demo-nested .ant-table-expanded-row > td:last-child .ant-table-thead th:first-child {
padding-left: 0;
}
.components-table-demo-nested .ant-table-expanded-row > td:last-child .ant-table-row td:first-child {
padding-left: 0;
}
.components-table-demo-nested .ant-table-expanded-row .ant-table-row:last-child td {
border: none;
}
.components-table-demo-nested .ant-table-expanded-row .ant-table-thead > tr > th {
background: none;
}
.components-table-demo-nested .table-operation a:not(:last-child) {
margin-right: 24px;
}
````

View File

@ -1,55 +0,0 @@
---
order: 6
title:
en-US: Pagination
zh-CN: 分页
---
## zh-CN
表格中的分页器可以通过一个配置对象来配置,当 `pagination={false}` 时,会隐藏分页器。
## en-US
The pagination in table could be configured with an object, and you can use `pagination={false}` to turn off pagination.
````jsx
import { Table } from 'antd';
const columns = [{
title: 'Name',
dataIndex: 'name',
render: text => <a href="#">{text}</a>,
}, {
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}`,
});
}
const pagination = {
total: data.length,
showSizeChanger: true,
onShowSizeChange: (current, pageSize) => {
console.log('Current: ', current, '; PageSize: ', pageSize);
},
onChange: (current) => {
console.log('Current: ', current);
},
};
ReactDOM.render(
<Table columns={columns} dataSource={data} pagination={pagination} />
, mountNode);
````

View File

@ -156,7 +156,7 @@ class NameColumn extends Table.Column<IUser> {}
## Note ## Note
According to [React documentation](http://facebook.github.io/react/docs/multiple-components.html#dynamic-children), every child in array should be assigned a unique key. The value inside `dataSource` and `columns` should follow this in Table, and `dataSource[i].key` would be treated as key value defaultly for `dataSource`. According to [React documentation](https://facebook.github.io/react/docs/lists-and-keys.html#keys), every child in array should be assigned a unique key. The value inside `dataSource` and `columns` should follow this in Table, and `dataSource[i].key` would be treated as key value defaultly for `dataSource`.
If `dataSource[i].key` is not existed, then you should specify the primary key of dataSource value via `rowKey`. If not, warnings like above will show in browser console. If `dataSource[i].key` is not existed, then you should specify the primary key of dataSource value via `rowKey`. If not, warnings like above will show in browser console.

View File

@ -156,7 +156,7 @@ class NameColumn extends Table.Column<IUser> {}
## 注意 ## 注意
按照 React 的[规范](http://facebook.github.io/react/docs/multiple-components.html#dynamic-children),所有的组件数组必须绑定 key。在 Table 中,`dataSource` 和 `columns` 里的数据值都需要指定 `key` 值。对于 `dataSource` 默认将每列数据的 `key` 属性作为唯一的标识。 按照 [React 的规范](https://facebook.github.io/react/docs/lists-and-keys.html#keys),所有的组件数组必须绑定 key。在 Table 中,`dataSource` 和 `columns` 里的数据值都需要指定 `key` 值。对于 `dataSource` 默认将每列数据的 `key` 属性作为唯一的标识。
如果你的数据没有这个属性,务必使用 `rowKey` 来指定数据列的主键。若没有指定,控制台会出现以下的提示,表格组件也会出现各类奇怪的错误。 如果你的数据没有这个属性,务必使用 `rowKey` 来指定数据列的主键。若没有指定,控制台会出现以下的提示,表格组件也会出现各类奇怪的错误。

View File

@ -4,6 +4,10 @@
@table-prefix-cls: ~"@{ant-prefix}-table"; @table-prefix-cls: ~"@{ant-prefix}-table";
@table-head-background-color: @background-color-base; @table-head-background-color: @background-color-base;
.@{table-prefix-cls}-wrapper {
.clearfix;
}
.@{table-prefix-cls} { .@{table-prefix-cls} {
font-size: @font-size-base; font-size: @font-size-base;
color: @text-color; color: @text-color;
@ -333,10 +337,15 @@
display: none; display: none;
} }
} }
.@{table-prefix-cls}-placeholder {
border: 0;
}
} }
} }
&-placeholder { &-placeholder {
position: relative;
padding: 16px 8px; padding: 16px 8px;
background: @component-background; background: @component-background;
border-bottom: @border-width-base @border-style-base @border-color-split; border-bottom: @border-width-base @border-style-base @border-color-split;
@ -364,6 +373,8 @@
border: 0; border: 0;
box-shadow: none; box-shadow: none;
border-radius: @border-radius-base @border-radius-base 0 0; border-radius: @border-radius-base @border-radius-base 0 0;
max-height: 400px;
overflow-x: hidden;
&-item > label + span { &-item > label + span {
padding: 0; padding: 0;
@ -486,10 +497,8 @@
&-fixed-header &-scroll &-header { &-fixed-header &-scroll &-header {
overflow: scroll; overflow: scroll;
// Fix https://github.com/ant-design/ant-design/issues/4637 padding-bottom: 20px;
&::-webkit-scrollbar { margin-bottom: -20px;
display: none;
}
} }
/* fix firefox scrollbar bug */ /* fix firefox scrollbar bug */
@ -503,7 +512,6 @@
position: absolute; position: absolute;
top: 0; top: 0;
overflow: hidden; overflow: hidden;
z-index: 1;
transition: box-shadow .3s ease; transition: box-shadow .3s ease;
border-radius: 0; border-radius: 0;
table { table {

View File

@ -17,31 +17,33 @@ Hide default plus icon, and bind event for customized trigger.
import { Tabs, Button } from 'antd'; import { Tabs, Button } from 'antd';
const TabPane = Tabs.TabPane; const TabPane = Tabs.TabPane;
const Demo = React.createClass({ class Demo extends React.Component {
getInitialState() { constructor(props) {
super(props);
this.newTabIndex = 0; this.newTabIndex = 0;
const panes = [ const panes = [
{ title: 'Tab 1', content: 'Content of Tab Pane 1', key: '1' }, { title: 'Tab 1', content: 'Content of Tab Pane 1', key: '1' },
{ title: 'Tab 2', content: 'Content of Tab Pane 2', key: '2' }, { title: 'Tab 2', content: 'Content of Tab Pane 2', key: '2' },
]; ];
return { this.state = {
activeKey: panes[0].key, activeKey: panes[0].key,
panes, panes,
}; };
}, }
onChange(activeKey) {
onChange = (activeKey) => {
this.setState({ activeKey }); this.setState({ activeKey });
}, }
onEdit(targetKey, action) { onEdit = (targetKey, action) => {
this[action](targetKey); this[action](targetKey);
}, }
add() { add = () => {
const panes = this.state.panes; const panes = this.state.panes;
const activeKey = `newTab${this.newTabIndex++}`; const activeKey = `newTab${this.newTabIndex++}`;
panes.push({ title: 'New Tab', content: 'New Tab Pane', key: activeKey }); panes.push({ title: 'New Tab', content: 'New Tab Pane', key: activeKey });
this.setState({ panes, activeKey }); this.setState({ panes, activeKey });
}, }
remove(targetKey) { remove = (targetKey) => {
let activeKey = this.state.activeKey; let activeKey = this.state.activeKey;
let lastIndex; let lastIndex;
this.state.panes.forEach((pane, i) => { this.state.panes.forEach((pane, i) => {
@ -54,7 +56,7 @@ const Demo = React.createClass({
activeKey = panes[lastIndex].key; activeKey = panes[lastIndex].key;
} }
this.setState({ panes, activeKey }); this.setState({ panes, activeKey });
}, }
render() { render() {
return ( return (
<div> <div>
@ -72,8 +74,8 @@ const Demo = React.createClass({
</Tabs> </Tabs>
</div> </div>
); );
}, }
}); }
ReactDOM.render(<Demo />, mountNode); ReactDOM.render(<Demo />, mountNode);
```` ````

View File

@ -19,31 +19,34 @@ Only card type Tabs support adding & closable.
import { Tabs } from 'antd'; import { Tabs } from 'antd';
const TabPane = Tabs.TabPane; const TabPane = Tabs.TabPane;
const Demo = React.createClass({
getInitialState() { class Demo extends React.Component {
constructor(props) {
super(props);
this.newTabIndex = 0; this.newTabIndex = 0;
const panes = [ const panes = [
{ title: 'Tab 1', content: 'Content of Tab 1', key: '1', closable: false }, { title: 'Tab 1', content: 'Content of Tab 1', key: '1', closable: false },
{ title: 'Tab 2', content: 'Content of Tab 2', key: '2' }, { title: 'Tab 2', content: 'Content of Tab 2', key: '2' },
]; ];
return { this.state = {
activeKey: panes[0].key, activeKey: panes[0].key,
panes, panes,
}; };
}, }
onChange(activeKey) {
onChange = (activeKey) => {
this.setState({ activeKey }); this.setState({ activeKey });
}, }
onEdit(targetKey, action) { onEdit = (targetKey, action) => {
this[action](targetKey); this[action](targetKey);
}, }
add() { add = () => {
const panes = this.state.panes; const panes = this.state.panes;
const activeKey = `newTab${this.newTabIndex++}`; const activeKey = `newTab${this.newTabIndex++}`;
panes.push({ title: 'New Tab', content: 'Content of new Tab', key: activeKey }); panes.push({ title: 'New Tab', content: 'Content of new Tab', key: activeKey });
this.setState({ panes, activeKey }); this.setState({ panes, activeKey });
}, }
remove(targetKey) { remove = (targetKey) => {
let activeKey = this.state.activeKey; let activeKey = this.state.activeKey;
let lastIndex; let lastIndex;
this.state.panes.forEach((pane, i) => { this.state.panes.forEach((pane, i) => {
@ -56,7 +59,7 @@ const Demo = React.createClass({
activeKey = panes[lastIndex].key; activeKey = panes[lastIndex].key;
} }
this.setState({ panes, activeKey }); this.setState({ panes, activeKey });
}, }
render() { render() {
return ( return (
<Tabs <Tabs
@ -68,8 +71,8 @@ const Demo = React.createClass({
{this.state.panes.map(pane => <TabPane tab={pane.title} key={pane.key} closable={pane.closable}>{pane.content}</TabPane>)} {this.state.panes.map(pane => <TabPane tab={pane.title} key={pane.key} closable={pane.closable}>{pane.content}</TabPane>)}
</Tabs> </Tabs>
); );
}, }
}); }
ReactDOM.render(<Demo />, mountNode); ReactDOM.render(<Demo />, mountNode);
```` ````

View File

@ -18,15 +18,14 @@ import { Tabs, Select } from 'antd';
const TabPane = Tabs.TabPane; const TabPane = Tabs.TabPane;
const Option = Select.Option; const Option = Select.Option;
const Demo = React.createClass({
getInitialState() { class Demo extends React.Component {
return { state = {
tabPosition: 'top', tabPosition: 'top',
}; }
}, changeTabPosition = (tabPosition) => {
changeTabPosition(tabPosition) {
this.setState({ tabPosition }); this.setState({ tabPosition });
}, }
render() { render() {
return ( return (
<div> <div>
@ -48,8 +47,8 @@ const Demo = React.createClass({
</Tabs> </Tabs>
</div> </div>
); );
}, }
}); }
ReactDOM.render(<Demo />, mountNode); ReactDOM.render(<Demo />, mountNode);
```` ````

View File

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import Tooltip from '..'; import Tooltip from '..';
import Button from '../../button';
describe('Tooltip', () => { describe('Tooltip', () => {
it('check `onVisibleChange` arguments', () => { it('check `onVisibleChange` arguments', () => {
@ -49,4 +50,67 @@ describe('Tooltip', () => {
expect(onVisibleChange.mock.calls.length).toBe(lastCount); // no change with lastCount expect(onVisibleChange.mock.calls.length).toBe(lastCount); // no change with lastCount
expect(wrapper.ref('tooltip').prop('visible')).toBe(false); expect(wrapper.ref('tooltip').prop('visible')).toBe(false);
}); });
it('should hide when mouse leave native disabled button', () => {
const onVisibleChange = jest.fn();
const wrapper = mount(
<Tooltip
title="xxxxx"
mouseEnterDelay={0}
mouseLeaveDelay={0}
onVisibleChange={onVisibleChange}
>
<button disabled>Hello world!</button>
</Tooltip>
);
expect(wrapper.find('span')).toHaveLength(1);
const button = wrapper.find('span').at(0);
button.simulate('mouseenter');
expect(onVisibleChange).toBeCalledWith(true);
expect(wrapper.ref('tooltip').prop('visible')).toBe(true);
button.simulate('mouseleave');
expect(onVisibleChange).toBeCalledWith(false);
expect(wrapper.ref('tooltip').prop('visible')).toBe(false);
});
it('should hide when mouse leave antd disabled Button', () => {
const onVisibleChange = jest.fn();
const wrapper = mount(
<Tooltip
title="xxxxx"
mouseEnterDelay={0}
mouseLeaveDelay={0}
onVisibleChange={onVisibleChange}
>
<Button disabled>Hello world!</Button>
</Tooltip>
);
expect(wrapper.getDOMNode().tagName).toBe('SPAN');
const button = wrapper.find('span').at(0);
button.simulate('mouseenter');
expect(onVisibleChange).toBeCalledWith(true);
expect(wrapper.ref('tooltip').prop('visible')).toBe(true);
button.simulate('mouseleave');
expect(onVisibleChange).toBeCalledWith(false);
expect(wrapper.ref('tooltip').prop('visible')).toBe(false);
});
it('should render disabled Button style properly', () => {
const wrapper1 = mount(
<Tooltip title="xxxxx">
<Button disabled>Hello world!</Button>
</Tooltip>
);
const wrapper2 = mount(
<Tooltip title="xxxxx">
<Button disabled style={{ display: 'block' }}>Hello world!</Button>
</Tooltip>
);
expect(wrapper1.getDOMNode().style.display).toBe('inline-block');
expect(wrapper2.getDOMNode().style.display).toBe('block');
});
}); });

View File

@ -85,6 +85,32 @@ export default class Tooltip extends React.Component<TooltipProps, any> {
}); });
} }
// Fix Tooltip won't hide at disabled button
// mouse events don't trigger at disabled button in Chrome
// https://github.com/react-component/tooltip/issues/18
getDisabledCompatibleChildren(element) {
if ((element.type.__ANT_BUTTON || element.type === 'button') && element.props.disabled) {
// reserve display style for <Button style={{ display: 'block '}}></Button>
// Note:
// If people override ant-btn's style.display by css,
// it will be affected cause we reset it to 'inline-block'
const displayStyle = (element.props.style && element.props.style.display)
? element.props.style.display : 'inline-block';
const child = cloneElement(element, {
style: {
...element.props.style,
pointerEvents: 'none',
},
});
return (
<span style={{ display: displayStyle, cursor: 'not-allowed' }}>
{child}
</span>
);
}
return element;
}
isNoTitle() { isNoTitle() {
const { title, overlay } = this.props; const { title, overlay } = this.props;
return !title && !overlay; // overlay for old version compatibility return !title && !overlay; // overlay for old version compatibility
@ -132,7 +158,9 @@ export default class Tooltip extends React.Component<TooltipProps, any> {
visible = false; visible = false;
} }
const child = React.isValidElement(children) ? children : <span>{children}</span>; const child = this.getDisabledCompatibleChildren(
React.isValidElement(children) ? children : <span>{children}</span>
);
const childProps = child.props; const childProps = child.props;
const childCls = classNames(childProps.className, { const childCls = classNames(childProps.className, {
[openClassName || `${prefixCls}-open`]: true, [openClassName || `${prefixCls}-open`]: true,

View File

@ -18,17 +18,15 @@ You can customize the labels of the transfer buttons, the width and height of th
````jsx ````jsx
import { Transfer, Button } from 'antd'; import { Transfer, Button } from 'antd';
const App = React.createClass({ class App extends React.Component {
getInitialState() { state = {
return { mockData: [],
mockData: [], targetKeys: [],
targetKeys: [], }
};
},
componentDidMount() { componentDidMount() {
this.getMock(); this.getMock();
}, }
getMock() { getMock = () => {
const targetKeys = []; const targetKeys = [];
const mockData = []; const mockData = [];
for (let i = 0; i < 20; i++) { for (let i = 0; i < 20; i++) {
@ -44,11 +42,11 @@ const App = React.createClass({
mockData.push(data); mockData.push(data);
} }
this.setState({ mockData, targetKeys }); this.setState({ mockData, targetKeys });
}, }
handleChange(targetKeys) { handleChange = (targetKeys) => {
this.setState({ targetKeys }); this.setState({ targetKeys });
}, }
renderFooter() { renderFooter = () => {
return ( return (
<Button size="small" style={{ float: 'right', margin: 5 }} <Button size="small" style={{ float: 'right', margin: 5 }}
onClick={this.getMock} onClick={this.getMock}
@ -56,7 +54,7 @@ const App = React.createClass({
reload reload
</Button> </Button>
); );
}, }
render() { render() {
return ( return (
<Transfer <Transfer
@ -73,8 +71,8 @@ const App = React.createClass({
footer={this.renderFooter} footer={this.renderFooter}
/> />
); );
}, }
}); }
ReactDOM.render(<App />, mountNode); ReactDOM.render(<App />, mountNode);
```` ````

View File

@ -30,26 +30,27 @@ const targetKeys = mockData
.filter(item => +item.key % 3 > 1) .filter(item => +item.key % 3 > 1)
.map(item => item.key); .map(item => item.key);
const App = React.createClass({ class App extends React.Component {
getInitialState() { state = {
return { targetKeys,
targetKeys, selectedKeys: [],
selectedKeys: [], }
};
}, handleChange = (nextTargetKeys, direction, moveKeys) => {
handleChange(nextTargetKeys, direction, moveKeys) {
this.setState({ targetKeys: nextTargetKeys }); this.setState({ targetKeys: nextTargetKeys });
console.log('targetKeys: ', targetKeys); console.log('targetKeys: ', targetKeys);
console.log('direction: ', direction); console.log('direction: ', direction);
console.log('moveKeys: ', moveKeys); console.log('moveKeys: ', moveKeys);
}, }
handleSelectChange(sourceSelectedKeys, targetSelectedKeys) {
handleSelectChange = (sourceSelectedKeys, targetSelectedKeys) => {
this.setState({ selectedKeys: [...sourceSelectedKeys, ...targetSelectedKeys] }); this.setState({ selectedKeys: [...sourceSelectedKeys, ...targetSelectedKeys] });
console.log('sourceSelectedKeys: ', sourceSelectedKeys); console.log('sourceSelectedKeys: ', sourceSelectedKeys);
console.log('targetSelectedKeys: ', targetSelectedKeys); console.log('targetSelectedKeys: ', targetSelectedKeys);
}, }
render() { render() {
const state = this.state; const state = this.state;
return ( return (
@ -63,8 +64,8 @@ const App = React.createClass({
render={item => item.title} render={item => item.title}
/> />
); );
}, }
}); }
ReactDOM.render(<App />, mountNode); ReactDOM.render(<App />, mountNode);
```` ````

View File

@ -16,17 +16,15 @@ Custom each Transfer Item, and in this way you can render a complex datasource.
````jsx ````jsx
import { Transfer } from 'antd'; import { Transfer } from 'antd';
const App = React.createClass({ class App extends React.Component {
getInitialState() { state = {
return { mockData: [],
mockData: [], targetKeys: [],
targetKeys: [], }
};
},
componentDidMount() { componentDidMount() {
this.getMock(); this.getMock();
}, }
getMock() { getMock = () => {
const targetKeys = []; const targetKeys = [];
const mockData = []; const mockData = [];
for (let i = 0; i < 20; i++) { for (let i = 0; i < 20; i++) {
@ -42,12 +40,12 @@ const App = React.createClass({
mockData.push(data); mockData.push(data);
} }
this.setState({ mockData, targetKeys }); this.setState({ mockData, targetKeys });
}, }
handleChange(targetKeys, direction, moveKeys) { handleChange = (targetKeys, direction, moveKeys) => {
console.log(targetKeys, direction, moveKeys); console.log(targetKeys, direction, moveKeys);
this.setState({ targetKeys }); this.setState({ targetKeys });
}, }
renderItem(item) { renderItem = (item) => {
const customLabel = ( const customLabel = (
<span className="custom-item"> <span className="custom-item">
{item.title} - {item.description} {item.title} - {item.description}
@ -58,7 +56,7 @@ const App = React.createClass({
label: customLabel, // for displayed item label: customLabel, // for displayed item
value: item.title, // for title and filter matching value: item.title, // for title and filter matching
}; };
}, }
render() { render() {
return ( return (
<Transfer <Transfer
@ -72,8 +70,8 @@ const App = React.createClass({
render={this.renderItem} render={this.renderItem}
/> />
); );
}, }
}); }
ReactDOM.render(<App />, mountNode); ReactDOM.render(<App />, mountNode);
```` ````

View File

@ -17,17 +17,15 @@ title:
````jsx ````jsx
import { Transfer } from 'antd'; import { Transfer } from 'antd';
const App = React.createClass({ class App extends React.Component {
getInitialState() { state = {
return { mockData: [],
mockData: [], targetKeys: [],
targetKeys: [], }
};
},
componentDidMount() { componentDidMount() {
this.getMock(); this.getMock();
}, }
getMock() { getMock = () => {
const targetKeys = []; const targetKeys = [];
const mockData = []; const mockData = [];
for (let i = 0; i < 2000; i++) { for (let i = 0; i < 2000; i++) {
@ -43,11 +41,11 @@ const App = React.createClass({
mockData.push(data); mockData.push(data);
} }
this.setState({ mockData, targetKeys }); this.setState({ mockData, targetKeys });
}, }
handleChange(targetKeys, direction, moveKeys) { handleChange = (targetKeys, direction, moveKeys) => {
console.log(targetKeys, direction, moveKeys); console.log(targetKeys, direction, moveKeys);
this.setState({ targetKeys }); this.setState({ targetKeys });
}, }
render() { render() {
return ( return (
<Transfer <Transfer
@ -57,8 +55,8 @@ const App = React.createClass({
render={item => item.title} render={item => item.title}
/> />
); );
}, }
}); }
ReactDOM.render(<App />, mountNode); ReactDOM.render(<App />, mountNode);
```` ````

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