diff --git a/components/form/demo/advanced-search.md b/components/form/demo/advanced-search.md index 45bf9d947e..b6aeb0e172 100644 --- a/components/form/demo/advanced-search.md +++ b/components/form/demo/advanced-search.md @@ -22,25 +22,27 @@ Because the width of label is not fixed, you may need to adjust it by customizin import { Form, Row, Col, Input, Button, Icon } from 'antd'; const FormItem = Form.Item; -const AdvancedSearchForm = Form.create()(React.createClass({ - getInitialState() { - return { - expand: false, - }; - }, - handleSearch(e) { +class AdvancedSearchForm extends React.Component { + state = { + expand: false, + }; + + handleSearch = (e) => { e.preventDefault(); this.props.form.validateFields((err, values) => { console.log('Received values of form: ', values); }); - }, - handleReset() { + } + + handleReset = () => { this.props.form.resetFields(); - }, - toggle() { + } + + toggle = () => { const { expand } = this.state; this.setState({ expand: !expand }); - }, + } + render() { const { getFieldDecorator } = this.props.form; const formItemLayout = { @@ -86,10 +88,17 @@ const AdvancedSearchForm = Form.create()(React.createClass({ ); - }, -})); + } +} -ReactDOM.render(, mountNode); +const WrappedAdvancedSearchForm = Form.create()(AdvancedSearchForm); +ReactDOM.render( +
+ +
Search Result List
+
, + mountNode +); ```` ````css @@ -105,4 +114,13 @@ ReactDOM.render(, mountNode); #components-form-demo-advanced-search .ant-form-horizontal { max-width: none; } +#components-form-demo-advanced-search .search-result-list { + margin-top: 16px; + border: 1px dashed #e9e9e9; + border-radius: 6px; + background-color: #fafafa; + min-height: 200px; + text-align: center; + padding-top: 80px; +} diff --git a/components/form/demo/dynamic-form-item.md b/components/form/demo/dynamic-form-item.md index 637b31452f..1fbd72a39d 100644 --- a/components/form/demo/dynamic-form-item.md +++ b/components/form/demo/dynamic-form-item.md @@ -14,25 +14,33 @@ title: Add or remove form items dynamically. ````jsx -import { Form, Input, Button } from 'antd'; +import { Form, Input, Icon, Button } from 'antd'; +const FormItem = Form.Item; let uuid = 0; -let Demo = React.createClass({ +class DynamicFieldSet extends React.Component { componentWillMount() { this.props.form.setFieldsValue({ keys: [0], }); - }, - remove(k) { + } + + remove = (k) => { const { form } = this.props; // can use data-binding to get const keys = form.getFieldValue('keys'); + // We need at least one passenger + if (keys.length === 1) { + return; + } + // can use data-binding to set form.setFieldsValue({ keys: keys.filter(key => key !== k), }); - }, - add() { + } + + add = () => { uuid++; const { form } = this.props; // can use data-binding to get @@ -43,51 +51,72 @@ let Demo = React.createClass({ form.setFieldsValue({ keys: nextKeys, }); - }, - handleSubmit(e) { - e.preventDefault(); - this.props.form.validateFields((err, values) => { - if (!err) { - console.log('Received values of form: ', values); - } - }); - }, + } + render() { const { getFieldDecorator, getFieldValue } = this.props.form; - const formItemLayout = { - labelCol: { span: 6 }, - wrapperCol: { span: 18 }, + labelCol: { span: 4 }, + wrapperCol: { span: 20 }, + }; + const formItemLayoutWithOutLabel = { + wrapperCol: { span: 20, offset: 4 }, }; - const formItems = getFieldValue('keys').map((k) => { + const keys = getFieldValue('keys'); + const formItems = keys.map((k, index) => { return ( - - {getFieldDecorator(`name${k}`, { + + {getFieldDecorator(`names-${k}`, { + validateTrigger: ['onChange', 'onBlur'], rules: [{ required: true, whitespace: true, - message: "Your good friend's name", + message: "Please input passenger's name or delete this field.", }], })( )} - - + this.remove(k)} + /> + ); }); return ( -
+ {formItems} - - - - + + +
); - }, -}); + } +} + +const WrappedDynamicFieldSet = Form.create()(DynamicFieldSet); +ReactDOM.render(, mountNode); +```` -Demo = Form.create()(Demo); -ReactDOM.render(, mountNode); +````css +#components-form-demo-dynamic-form-item .delete-button { + cursor: pointer; + position: relative; + top: 4px; + font-size: 24px; + color: #999; +} +#components-form-demo-dynamic-form-item .delete-button[disabled] { + cursor: not-allowed; +} ```` diff --git a/components/form/demo/without-form-create.md b/components/form/demo/without-form-create.md index 13ea524f02..fa74d6df17 100644 --- a/components/form/demo/without-form-create.md +++ b/components/form/demo/without-form-create.md @@ -17,47 +17,49 @@ title: import { Form, InputNumber } from 'antd'; const FormItem = Form.Item; -const RawForm = React.createClass({ - getInitialState() { +function validatePrimeNumber(number) { + if (number === 11) { return { - number: { - value: 11, - }, + validateStatus: 'success', + errorMsg: null, }; - }, - handleNumberChange(value) { + } + return { + validateStatus: 'error', + errorMsg: 'The prime between 8 and 12 is 11!', + }; +} + +class RawForm extends React.Component { + state = { + number: { + value: 11, + }, + }; + + handleNumberChange = (value) => { this.setState({ number: { - ...this.validatePrimeNumber(value), + ...validatePrimeNumber(value), value, }, }); - }, - validatePrimeNumber(number) { - if (number === 11) { - return { - validateStatus: 'success', - errorMsg: null, - }; - } - return { - validateStatus: 'error', - errorMsg: 'The prime number between 8 to 12 is 11!', - }; - }, + } + render() { const formItemLayout = { - labelCol: { span: 8 }, + labelCol: { span: 7 }, wrapperCol: { span: 12 }, }; const number = this.state.number; + const tips = 'A prime is a natural number greater than 1 that has no positive divisors other than 1 and itself.'; return (
); - }, -}); + } +} ReactDOM.render(, mountNode); ```` diff --git a/components/tag/CheckableTag.tsx b/components/tag/CheckableTag.tsx new file mode 100644 index 0000000000..ecf4ab88dd --- /dev/null +++ b/components/tag/CheckableTag.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import classNames from 'classnames'; +import splitObject from '../_util/splitObject'; + +export interface CheckableTagProps { + prefixCls?: string; + className?: string; + checked: boolean; + onChange?: (checked) => void; +} + +export default class CheckableTag extends React.Component { + handleClick = () => { + const { checked, onChange } = this.props; + if (onChange) { + onChange(!checked); + } + } + render() { + const [{ prefixCls = 'ant-tag', className = '', checked }, restProps ] = splitObject( + this.props, ['prefixCls', 'className', 'checked'] + ); + const cls = classNames({ + [`${prefixCls}`]: true, + [`${prefixCls}-checkable`]: true, + [`${prefixCls}-checkable-checked`]: checked, + [className]: className, + }); + + delete restProps.onChange; + return
; + } +} diff --git a/components/tag/demo/basic.md b/components/tag/demo/basic.md index 00cd4676c5..a4d91c7d5a 100644 --- a/components/tag/demo/basic.md +++ b/components/tag/demo/basic.md @@ -1,28 +1,32 @@ --- order: 0 -title: +title: zh-CN: 基本 en-US: Basic --- ## zh-CN -简单的标签展示,添加 closable 表示可关闭。 +基本标签的用法,可以通过添加 `closable` 变为可关闭标签。可关闭标签具有 `onClose` `afterClose` 两个事件。 ## en-US -Simple demo of tag, 'closable' property represents which can be closed. +Usage of basic Tag, and it could be closable by set `closable` property. Closable Tag supports `onClose` `afterClose` events. ````jsx import { Tag } from 'antd'; -function onClose(e) { +function log(e) { console.log(e); } -ReactDOM.render(
- Tag 1 - Tag 2 - Tag 3 -
, mountNode); +ReactDOM.render( +
+ Tag 1 + Tag 2 + Tag 3 + Link +
, + mountNode +); ```` diff --git a/components/tag/demo/checkable.md b/components/tag/demo/checkable.md new file mode 100644 index 0000000000..fd04012b3a --- /dev/null +++ b/components/tag/demo/checkable.md @@ -0,0 +1,39 @@ +--- +order: 3 +title: + zh-CN: 可选择 + en-US: Checkable +--- + +## zh-CN + +可通过 Tag.CheckableTag 实现类似 Checkbox 的效果,该组件为完全受控组件,不支持非受控用法。 + +## en-US + +Tag.CheckableTag works like Checkbox, and it is an absolute controlled component and has no uncontrolled mode. + +````jsx +import { Tag } from 'antd'; +const CheckableTag = Tag.CheckableTag; + +class UncontrolledCheckableTag extends React.Component { + state = { checked: false }; + handleChange = (checked) => { + this.setState({ checked }); + } + + render() { + return ; + } +} + +ReactDOM.render( +
+ Unchecked + Checked + Uncontrolled +
, + mountNode +); +```` diff --git a/components/tag/demo/colorful.md b/components/tag/demo/colorful.md index 3453a7ab67..f9155eb1aa 100644 --- a/components/tag/demo/colorful.md +++ b/components/tag/demo/colorful.md @@ -1,20 +1,26 @@ --- -debug: true -order: -1 -title: Colorful Tags for Debugging +order: 1 +title: + zh-CN: 多彩标签 + en-US: Colorful --- -`Tag[color]` is deprecated, but we need this demo for debugging until next major version. +## zh-CN + +基本标签可以通过 `color` 设置背景色,以提供视觉暗示区分不同目的的标签。 + +## en-US + +We can set the background color of basic Tag by `color`, and it's helpful to tell different Tags. ````jsx import { Tag } from 'antd'; ReactDOM.render(
- Blue - Green - Yellow - Red + #f50 + #87d068 + #2db7f5
, mountNode ); diff --git a/components/tag/demo/control.md b/components/tag/demo/control.md index 6eec297369..2bdd5497f7 100644 --- a/components/tag/demo/control.md +++ b/components/tag/demo/control.md @@ -1,47 +1,45 @@ --- -order: 1 -title: +order: 2 +title: zh-CN: 动态添加和删除 - en-US: Dynamically add and remove + en-US: Add & Remove Dynamically --- ## zh-CN -用数组生成一组标签,可以动态添加和删除。 - -使用 `afterClose` 删除时有动画效果。 +用数组生成一组标签,可以动态添加和删除,通过监听删除动画结束的事件 `afterClose` 实现。 ## en-US -Generating a set of tag by array, you can dynamically add and remove. - -Using 'afterClose' property, There are animated when a tag was removed. +Generating a set of Tags by array, you can add and remove dynamically. +It's based on `afterClose` event, which will be triggered while the close animation end. ````jsx import { Tag, Button } from 'antd'; let index = 3; -const App = React.createClass({ - getInitialState() { - return { - tags: [ - { key: 1, name: 'Unremovable' }, - { key: 2, name: 'Tag 2' }, - { key: 3, name: 'Tag 3' }, - ], - }; - }, - handleClose(key) { +class EditableTagGroup extends React.Component { + state = { + tags: [ + { key: 1, name: 'Unremovable' }, + { key: 2, name: 'Tag 2' }, + { key: 3, name: 'Tag 3' }, + ], + }; + + handleClose = (key) => { const tags = [...this.state.tags].filter(tag => (tag.key !== key) && tag); console.log(tags); this.setState({ tags }); - }, - addTag() { - const tags = [...this.state.tags]; + } + + addTag = () => { index += 1; - tags.push({ key: index, name: `New tag ${index}` }); + const tags = [...this.state.tags, { key: index, name: `New tag ${index}` }]; + console.log(tags); this.setState({ tags }); - }, + } + render() { const { tags } = this.state; return ( @@ -54,8 +52,8 @@ const App = React.createClass({
); - }, -}); + } +} -ReactDOM.render(, mountNode); +ReactDOM.render(, mountNode); ```` diff --git a/components/tag/demo/hot-tags.md b/components/tag/demo/hot-tags.md new file mode 100644 index 0000000000..1e1d26fb23 --- /dev/null +++ b/components/tag/demo/hot-tags.md @@ -0,0 +1,56 @@ +--- +order: 4 +title: + zh-CN: 热门标签 + en-US: Hot Tags +--- + +## zh-CN + +选择你感兴趣的话题。 + +## en-US + +Select your favourite topics. + +````jsx +import { Tag } from 'antd'; +const CheckableTag = Tag.CheckableTag; + +const tagsFromServer = ['Movie', 'Books', 'Music']; + +class HotTags extends React.Component { + state = { + selectedTags: [], + }; + + handleChange(tag, checked) { + const { selectedTags } = this.state; + const nextSelectedTags = checked ? + [...selectedTags, tag] : + selectedTags.filter(t => t !== tag); + console.log('You are interested in: ', nextSelectedTags); + this.setState({ selectedTags: nextSelectedTags }); + } + + render() { + const { selectedTags } = this.state; + return ( +
+ Hots: + {tagsFromServer.map(tag => ( + -1} + onChange={checked => this.handleChange(tag, checked)} + > + {tag} + + ))} +
+ ); + } +} + +ReactDOM.render(, mountNode); +```` diff --git a/components/tag/index.en-US.md b/components/tag/index.en-US.md index 835fae246d..decd0e910a 100644 --- a/components/tag/index.en-US.md +++ b/components/tag/index.en-US.md @@ -14,8 +14,18 @@ Tag for categorizing or markuping. ## API +### Tag + | Property | Description | Type | Default | |--------------|-----------------------|----------|--------------| +| color | The background color of Tag | string | - | | closable | Tag can be closed. | boolean | false | -| onClose | Callback when tag was closed | function(event)| - | -| afterClose | Callback when closed animation is complete | function(event)| - | +| onClose | Callback when tag was closed | (e) => void| - | +| afterClose | Callback when closed animation is complete | () => void | - | + +### Tag.CheckableTag + +| Property | Description | Type | Default | +|--------------|-----------------------|----------|--------------| +| checked | To set the checked status for Tag | boolean | false | +| onChange | A callback which will be called while Tag is clicked | (checked) => void | - | diff --git a/components/tag/index.tsx b/components/tag/index.tsx index a507539399..5371b717be 100644 --- a/components/tag/index.tsx +++ b/components/tag/index.tsx @@ -7,8 +7,10 @@ import assign from 'object-assign'; import Icon from '../icon'; import warning from '../_util/warning'; import splitObject from '../_util/splitObject'; +import CheckableTag from './CheckableTag'; export interface TagProps { + color?: string; /** 标签是否可以关闭 */ closable?: boolean; /** 关闭时的回调 */ @@ -19,14 +21,19 @@ export interface TagProps { } export default class Tag extends React.Component { + static CheckableTag = CheckableTag; static defaultProps = { prefixCls: 'ant-tag', closable: false, }; - constructor(props) { + constructor(props: TagProps) { super(props); - warning(!('color' in props), '`Tag[color]` is deprecated, please override color by CSS instead.'); + warning( + !/blue|red|green|yellow/.test(props.color || ''), + '`Tag[color=red|green|blue|yellow]` is deprecated, ' + + 'please set color by `#abc` or `rgb(a, b, c)` instead.' + ); this.state = { closing: false, diff --git a/components/tag/index.zh-CN.md b/components/tag/index.zh-CN.md index 4e23029349..c311a7029d 100644 --- a/components/tag/index.zh-CN.md +++ b/components/tag/index.zh-CN.md @@ -14,8 +14,18 @@ title: Tag ## API +### Tag + | 参数 | 说明 | 类型 | 默认值 | |----------------|-------------------------------|------|--------| +| color | 标签背景色 | string | - | | closable | 标签是否可以关闭 | boolean | false | -| onClose | 关闭时的回调 | function(event) | - | -| afterClose | 关闭动画完成后的回调 | function(event) | - | +| onClose | 关闭时的回调 | (e) => void | - | +| afterClose | 关闭动画完成后的回调 | () => void | - | + +### Tag.CheckableTag + +| 参数 | 说明 | 类型 | 默认值 | +|----------------|-------------------------------|------|--------| +| checked | 设置标签的选中状态 | boolean | false | +| onChange | 点击标签时触发的回调 | (checked) => void | - | diff --git a/components/tag/style/index.less b/components/tag/style/index.less index a6eafe91c3..0014a4a21d 100644 --- a/components/tag/style/index.less +++ b/components/tag/style/index.less @@ -16,7 +16,7 @@ vertical-align: middle; opacity: 1; overflow: hidden; - margin: 2px 4px 2px 0; + margin: 4px 8px 4px 0; cursor: pointer; &:hover { @@ -53,12 +53,6 @@ &-has-color { border-color: transparent; - } - - &-blue, - &-green, - &-yellow, - &-red { &, a, a:hover, @@ -84,6 +78,24 @@ background: @error-color; } + &-checkable { + background-color: transparent; + border-color: transparent; + + &:hover, + &:active, + &-checked { + color: #fff; + } + &:hover { + background-color: tint(@primary-color, 20%); + } + &:active, + &-checked { + background-color: shade(@primary-color, 5%); + } + } + &-close { width: 0 !important; padding: 0;