Merge branch 'master' of github.com:ant-design/ant-design

This commit is contained in:
afc163 2016-09-01 14:39:15 +08:00
commit 5f8b176c69
34 changed files with 710 additions and 354 deletions

View File

@ -1,10 +1,18 @@
---
order: 4
title: 参考对象
title:
zh-CN: 滚动容器
en-US: Container to scroll.
---
## zh-CN
`target` 设置 `Affix` 需要监听其滚动事件的元素,默认为 `window`
## en-US
Set a `target` for 'Affix', which is listen to scroll event of target element (default is `window`).
````jsx
import { Affix, Button } from 'antd';
@ -17,7 +25,7 @@ const Demo = () => {
<br />
<br />
<Affix target={() => document.getElementById('affix-target')} offsetTop={20}>
<Button type="primary">固定在容器顶部</Button>
<Button type="primary">Fixed at the top of container</Button>
</Affix>
</div>
</div>

View File

@ -0,0 +1,50 @@
---
order: 9
title:
zh-CN: 搜索
en-US: Search
---
## zh-CN
可以直接搜索选项并选择。
## en-US
Search and select options directly.
````jsx
import { Cascader } from 'antd';
const options = [{
value: 'zhejiang',
label: '浙江',
children: [{
value: 'hangzhou',
label: '杭州',
children: [{
value: 'xihu',
label: '西湖',
}],
}],
}, {
value: 'jiangsu',
label: '江苏',
children: [{
value: 'nanjing',
label: '南京',
children: [{
value: 'zhonghuamen',
label: '中华门',
}],
}],
}];
function onChange(value) {
console.log(value);
}
ReactDOM.render(
<Cascader options={options} onChange={onChange} placeholder="请选择地区" showSearch />
, mountNode);
````

View File

@ -36,3 +36,15 @@ Cascade selection box.
| allowClear | whether allow clear | boolean | true |
| expandTrigger | expand current item when click or hover, one of 'click' 'hover' | string | 'click' |
| changeOnSelect | change value on each selection if set to true, see above demo for details | boolean | false |
| showSearch | Whether show search input in single mode. | Boolean or Object | false |
| notFoundContent | Specify content to show when no result matches. | String | 'Not Found' |
Fields in `showSearch`:
| Property | Description | Type | Default |
|----------|-------------|------|---------|
| filter | The function will receive two arguments, inputValue and option, if the function returns true, the option will be included in the filtered set; Otherwise, it will be excluded. | `function(inputValue, path): boolean` | |
| render | Used to render filtered options. | `function(inputValue, path): React.ReactNode` | |
| sort | Used to sort filtered options. | `function(a, b, inputValue)` | |
| matchInputWidth | Whether the width of result list equals to input's | boolean | |

View File

@ -15,6 +15,14 @@ export interface CascaderOptionType {
}
export type CascaderExpandTrigger = 'click' | 'hover'
export interface ShowSearchType {
filter?: (inputValue: string, path: CascaderOptionType[]) => boolean;
render?: (inputValue: string, path: CascaderOptionType[]) => React.ReactNode;
sort?: (a: CascaderOptionType[], b: CascaderOptionType[], inputValue: string) => number;
matchInputWidth?: boolean;
}
export interface CascaderProps {
/** 可选项数据源 */
options: Array<CascaderOptionType>;
@ -42,6 +50,8 @@ export interface CascaderProps {
disabled?: boolean;
/** 是否支持清除*/
allowClear?: boolean;
showSearch?: boolean | ShowSearchType;
notFoundContent?: React.ReactNode;
/** 次级菜单的展开方式,可选 'click' 和 'hover' */
expandTrigger?: CascaderExpandTrigger;
/** 当此项为 true 时,点选每级菜单选项值都会发生变化 */
@ -50,6 +60,33 @@ export interface CascaderProps {
onPopupVisibleChange?: (popupVisible: boolean) => void;
}
function highlightKeyword(str: string, keyword: string) {
return str.split(keyword)
.map((node: string, index: number) => index === 0 ? node : [
<span className="ant-cascader-menu-item-keyword" key="seperator">{keyword}</span>,
node,
]);
}
function defaultFilterOption(inputValue, path) {
return path.some(option => option.label.indexOf(inputValue) > -1);
}
function defaultRenderFilteredOption(inputValue, path) {
return path.map(({ label }, index) => {
const node = label.indexOf(inputValue) > -1 ? highlightKeyword(label, inputValue) : label;
return index === 0 ? node : [' / ', node];
});
}
function defaultSortFilteredOption(a, b, inputValue) {
function callback(elem) {
return elem.label.indexOf(inputValue) > -1;
}
return a.findIndex(callback) - b.findIndex(callback);
}
export default class Cascader extends React.Component<CascaderProps, any> {
static defaultProps = {
prefixCls: 'ant-cascader',
@ -61,20 +98,27 @@ export default class Cascader extends React.Component<CascaderProps, any> {
displayRender: label => label.join(' / '),
disabled: false,
allowClear: true,
showSearch: false,
notFoundContent: 'Not Found',
onPopupVisibleChange() {},
};
cachedOptions: CascaderOptionType[];
refs: {
[key: string]: any;
input: {
refs: { input: HTMLElement }
};
};
constructor(props) {
super(props);
let value;
if ('value' in props) {
value = props.value;
} else if ('defaultValue' in props) {
value = props.defaultValue;
}
this.state = {
value: value || [],
value: props.value || props.defautValue || [],
inputValue: '',
inputFocused: false,
popupVisible: false,
flattenOptions: props.showSearch && this.flattenTree(props.options, props.changeOnSelect),
};
}
@ -82,17 +126,45 @@ export default class Cascader extends React.Component<CascaderProps, any> {
if ('value' in nextProps) {
this.setState({ value: nextProps.value || [] });
}
if (nextProps.showSearch && this.props.options !== nextProps.options) {
this.setState({ flattenOptions: this.flattenTree(nextProps.options, nextProps.changeOnSelect) });
}
}
handleChange = (value, selectedOptions) => {
this.setValue(value, selectedOptions);
const unwrappedValue = Array.isArray(value[0]) ? value[0] : value;
this.setState({ inputValue: '' });
this.setValue(unwrappedValue, selectedOptions);
}
handlePopupVisibleChange = (popupVisible) => {
this.setState({ popupVisible });
this.setState({
popupVisible,
inputFocused: popupVisible,
});
this.props.onPopupVisibleChange(popupVisible);
}
handleInputBlur = () => {
this.setState({
inputFocused: false,
});
}
handleInputClick = (e) => {
const { inputFocused, popupVisible } = this.state;
// Prevent `Trigger` behaviour.
if (inputFocused || popupVisible) {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
}
}
handleInputChange = (e) => {
const inputValue = e.target.value;
this.setState({ inputValue });
}
setValue = (value, selectedOptions = []) => {
if (!('value' in this.props)) {
this.setState({ value });
@ -102,7 +174,9 @@ export default class Cascader extends React.Component<CascaderProps, any> {
getLabel() {
const { options, displayRender } = this.props;
const selectedOptions = arrayTreeFilter(options, (o, level) => o.value === this.state.value[level]);
const value = this.state.value;
const unwrappedValue = Array.isArray(value[0]) ? value[0] : value;
const selectedOptions = arrayTreeFilter(options, (o, level) => o.value === unwrappedValue[level]);
const label = selectedOptions.map(o => o.label);
return displayRender(label, selectedOptions);
}
@ -110,28 +184,71 @@ export default class Cascader extends React.Component<CascaderProps, any> {
clearSelection = (e) => {
e.preventDefault();
e.stopPropagation();
this.setValue([]);
this.setState({ popupVisible: false });
if (!this.state.inputValue) {
this.setValue([]);
this.setState({ popupVisible: false });
} else {
this.setState({ inputValue: '' });
}
}
flattenTree(options, changeOnSelect, ancestor = []) {
let flattenOptions = [];
options.forEach((option) => {
const path = ancestor.concat(option);
if (changeOnSelect || !option.children) {
flattenOptions.push(path);
}
if (option.children) {
flattenOptions = flattenOptions.concat(this.flattenTree(option.children, changeOnSelect, path));
}
});
return flattenOptions;
}
generateFilteredOptions() {
const { showSearch, notFoundContent } = this.props;
const {
filter = defaultFilterOption,
render = defaultRenderFilteredOption,
sort = defaultSortFilteredOption,
} = showSearch as ShowSearchType;
const { flattenOptions, inputValue } = this.state;
const filtered = flattenOptions.filter((path) => filter(this.state.inputValue, path))
.sort((a, b) => sort(a, b, inputValue));
if (filtered.length > 0) {
return filtered.map((path) => {
return {
label: render(inputValue, path),
value: path.map(o => o.value),
};
});
}
return [{ label: notFoundContent, value: 'ANT_CASCADER_NOT_FOUND', disabled: true }];
}
render() {
const props = this.props;
const state = this.state;
const [{ prefixCls, children, placeholder, size, disabled,
className, style, allowClear }, otherProps] = splitObject(props,
['prefixCls', 'children', 'placeholder', 'size', 'disabled', 'className', 'style', 'allowClear']);
className, style, allowClear, showSearch }, otherProps] = splitObject(props,
['prefixCls', 'children', 'placeholder', 'size', 'disabled', 'className',
'style', 'allowClear', 'showSearch']);
const value = state.value;
const sizeCls = classNames({
'ant-input-lg': size === 'large',
'ant-input-sm': size === 'small',
});
const clearIcon = (allowClear && !disabled && this.state.value.length > 0) ?
const clearIcon = (allowClear && !disabled && value.length > 0) || state.inputValue ?
<Icon type="cross-circle"
className={`${prefixCls}-picker-clear`}
onClick={this.clearSelection}
/> : null;
const arrowCls = classNames({
[`${prefixCls}-picker-arrow`]: true,
[`${prefixCls}-picker-arrow-expand`]: this.state.popupVisible,
[`${prefixCls}-picker-arrow-expand`]: state.popupVisible,
});
const pickerCls = classNames({
[className]: !!className,
@ -154,14 +271,44 @@ export default class Cascader extends React.Component<CascaderProps, any> {
'getPopupContainer',
'loadData',
'popupClassName',
'filterOption',
'renderFilteredOption',
'sortFilteredOption',
'notFoundContent',
]);
let options = props.options;
if (state.inputValue) {
options = this.generateFilteredOptions();
}
// Dropdown menu should keep previous status until it is fully closed.
if (!state.popupVisible) {
options = this.cachedOptions;
} else {
this.cachedOptions = options;
}
const dropdownMenuColumnStyle = {
width: undefined,
height: undefined,
};
const isNotFound = (options || []).length === 1 && options[0].value === 'ANT_CASCADER_NOT_FOUND';
if (isNotFound) {
dropdownMenuColumnStyle.height = 'auto'; // Height of one row.
}
// The default value of `matchInputWidth` is `true`
const resultListMatchInputWidth = showSearch.matchInputWidth === false ? false : true;
if (resultListMatchInputWidth && state.inputValue && this.refs.input) {
dropdownMenuColumnStyle.width = this.refs.input.refs.input.offsetWidth;
}
return (
<RcCascader {...props}
value={this.state.value}
popupVisible={this.state.popupVisible}
options={options}
value={value}
popupVisible={state.popupVisible}
onPopupVisibleChange={this.handlePopupVisibleChange}
onChange={this.handleChange}
dropdownMenuColumnStyle={dropdownMenuColumnStyle}
>
{children ||
<span
@ -169,11 +316,15 @@ export default class Cascader extends React.Component<CascaderProps, any> {
className={pickerCls}
>
<Input {...inputProps}
placeholder={this.state.value && this.state.value.length > 0 ? null : placeholder}
ref="input"
placeholder={value && value.length > 0 ? null : placeholder}
className={`${prefixCls}-input ${sizeCls}`}
value=""
value={state.inputValue}
disabled={disabled}
readOnly
readOnly={!showSearch}
onClick={showSearch ? this.handleInputClick : null}
onBlur={showSearch ? this.handleInputBlur : null}
onChange={showSearch ? this.handleInputChange : null}
/>
<span className={`${prefixCls}-picker-label`}>{this.getLabel()}</span>
{clearIcon}

View File

@ -1,8 +1,8 @@
---
category: Components
type: Form Controls
chinese: 级联选择
english: Cascader
title: Cascader
subtitle: 级联选择
---
级联选择框。
@ -22,18 +22,29 @@ english: Cascader
| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|--------|
| options | 可选项数据源 | object | - |
| defaultValue | 默认的选中项 | array |[] |
| value | 指定选中项 | array | - |
| options | 可选项数据源 | Object | - |
| defaultValue | 默认的选中项 | Array |[] |
| value | 指定选中项 | Array | - |
| onChange | 选择完成后的回调 | `function(value, selectedOptions)` | - |
| displayRender | 选择后展示的渲染函数 | `function(label, selectedOptions)` | `label => label.join(' / ')` |
| style | 自定义样式 | string | - |
| className | 自定义类名 | string | - |
| popupClassName | 自定义浮层类名 | string | - |
| popupPlacement | 浮层预设位置:`bottomLeft` `bottomRight` `topLeft` `topRight` | string | `bottomLeft` |
| placeholder | 输入框占位文本 | string | '请选择' |
| size | 输入框大小,可选 `large` `default` `small` | string | `default` |
| disabled | 禁用 | boolean | false |
| allowClear | 是否支持清除 | boolean | true |
| expandTrigger | 次级菜单的展开方式,可选 'click' 和 'hover' | string | 'click' |
| changeOnSelect | 当此项为 true 时,点选每级菜单选项值都会发生变化,具体见上面的演示 | boolean | false |
| style | 自定义样式 | String | - |
| className | 自定义类名 | String | - |
| popupClassName | 自定义浮层类名 | String | - |
| popupPlacement | 浮层预设位置:`bottomLeft` `bottomRight` `topLeft` `topRight` | Enum | `bottomLeft` |
| placeholder | 输入框占位文本 | String | '请选择' |
| size | 输入框大小,可选 `large` `default` `small` | String | `default` |
| disabled | 禁用 | Boolean | false |
| allowClear | 是否支持清除 | Boolean | true |
| expandTrigger | 次级菜单的展开方式,可选 'click' 和 'hover' | String | 'click' |
| changeOnSelect | 当此项为 true 时,点选每级菜单选项值都会发生变化,具体见上面的演示 | Boolean | false |
| showSearch | 在选择框中显示搜索框 | Boolean | false |
| notFoundContent | 当下拉列表为空时显示的内容 | String | 'Not Found' |
`showSearch` 为对象时,其中的字段:
| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|--------|
| filter | 接收 `inputValue` `path` 两个参数,当 `path` 符合筛选条件时,应返回 true反之则返回 false。 | `function(inputValue, path): boolean` | |
| render | 用于渲染 filter 后的选项 | `function(inputValue, path): React.ReactNode` | |
| sort | 用于排序 filter 后的选项 | `function(a, b, inputValue)` | |
| matchInputWidth | 搜索结果列表是否与输入框同宽 | boolean | |

View File

@ -6,6 +6,7 @@
.@{cascader-prefix-cls} {
font-size: @font-size-base;
&-input.ant-input {
background-color: transparent;
display: block;
cursor: pointer;
width: 100%;
@ -22,6 +23,10 @@
&-disabled {
cursor: not-allowed;
.@{cascader-prefix-cls}-input {
cursor: not-allowed;
}
}
&-label {
@ -36,7 +41,6 @@
overflow: hidden;
width: 100%;
padding: 0 12px 0 8px;
z-index: 1;
}
&-clear {
@ -171,5 +175,9 @@
right: 15px;
}
}
& &-keyword {
color: @error-color;
}
}
}

View File

@ -116,6 +116,15 @@ export default class Form extends React.Component<FormProps, any> {
};
},
render() {
const getFieldProps = this.props.form.getFieldProps;
function deprecatedGetFieldProps(name, option) {
warning(
false,
'`getFieldProps` is deprecated and will be removed in future, please use `getFieldDecorator` instead'
);
return getFieldProps(name, option);
}
this.props.form.getFieldProps = deprecatedGetFieldProps;
return <Component {...this.props} />;
},
}));

View File

@ -14,7 +14,7 @@ export interface FormItemLabelColOption {
export interface FormItemProps {
prefixCls?: string;
id?: string;
label?: string;
label?: string | React.ReactNode;
labelCol?: FormItemLabelColOption;
wrapperCol?: FormItemLabelColOption;
help?: React.ReactNode;
@ -40,7 +40,7 @@ export default class FormItem extends React.Component<FormItemProps, any> {
static propTypes = {
prefixCls: React.PropTypes.string,
label: React.PropTypes.string,
label: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.node]),
labelCol: React.PropTypes.object,
help: React.PropTypes.oneOfType([React.PropTypes.node, React.PropTypes.bool]),
validateStatus: React.PropTypes.oneOf(['', 'success', 'warning', 'error', 'validating']),
@ -187,7 +187,7 @@ export default class FormItem extends React.Component<FormItemProps, any> {
// remove user input colon
let label = props.label;
if (typeof label === 'string' && label.trim() !== '') {
label = props.label.replace(/[|:]\s*$/, '');
label = (props.label as string).replace(/[|:]\s*$/, '');
}
return props.label ? (

View File

@ -1,6 +1,6 @@
---
order: 15
title:
title:
zh-CN: 动态增减表单项
en-US: Dynamic form item
---
@ -18,6 +18,11 @@ import { Form, Input, Button } from 'antd';
let uuid = 0;
let Demo = React.createClass({
componentWillMount() {
this.props.form.setFieldsValue({
keys: [0],
});
},
remove(k) {
const { form } = this.props;
// can use data-binding to get
@ -52,10 +57,7 @@ let Demo = React.createClass({
});
},
render() {
const { getFieldProps, getFieldValue } = this.props.form;
getFieldProps('keys', {
initialValue: [0],
});
const { getFieldDecorator, getFieldValue } = this.props.form;
const formItemLayout = {
labelCol: { span: 6 },
@ -65,14 +67,15 @@ let Demo = React.createClass({
const formItems = getFieldValue('keys').map((k) => {
return (
<Form.Item {...formItemLayout} label={`good friend${k}`} key={k}>
<Input {...getFieldProps(`name${k}`, {
{getFieldDecorator(`name${k}`, {
rules: [{
required: true,
whitespace: true,
message: "Your good friend's name",
}],
})} style={{ width: '80%', marginRight: 8 }}
/>
})(
<Input style={{ width: '80%', marginRight: 8 }} />
)}
<Button onClick={() => this.remove(k)}>remove</Button>
</Form.Item>
);

View File

@ -1,6 +1,6 @@
---
order: 3
title:
title:
zh-CN: 表单控件
en-US: Form controls
---
@ -15,7 +15,7 @@ title:
A list off all the controls that can be used with form.
**Note**: Input control: Only if set correct type for it, then it will be set correct style
**Note**: Input control: Only if set correct type for it, then it will be set correct style.
````jsx
import { Form, Input, Select, Checkbox, Radio } from 'antd';

View File

@ -37,7 +37,7 @@ let Demo = React.createClass({
},
render() {
const { getFieldProps } = this.props.form;
const { getFieldDecorator } = this.props.form;
const formItemLayout = {
labelCol: { span: 4 },
@ -52,13 +52,17 @@ let Demo = React.createClass({
{...formItemLayout}
label="User name"
>
<Input {...getFieldProps('username', {})} type="text" autoComplete="off" />
{getFieldDecorator('username')(
<Input type="text" autoComplete="off" />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Password"
>
<Input {...getFieldProps('password', {})} type="password" autoComplete="off" />
{getFieldDecorator('password')(
<Input type="password" autoComplete="off" />
)}
</FormItem>
</Form>
</Modal>

View File

@ -1,6 +1,6 @@
---
order: 2
title:
title:
zh-CN: 典型表单
en-US: Horizontal form
---
@ -25,7 +25,7 @@ let Demo = React.createClass({
},
render() {
const { getFieldProps } = this.props.form;
const { getFieldDecorator } = this.props.form;
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 14 },
@ -42,29 +42,37 @@ let Demo = React.createClass({
{...formItemLayout}
label="Password"
>
<Input type="password" {...getFieldProps('pass', { initialValue: '' })} placeholder="Please input the password" />
{getFieldDecorator('pass', { initialValue: '' })(
<Input type="password" placeholder="Please input the password" />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Gender"
>
<RadioGroup {...getFieldProps('gender', { initialValue: 'female' })}>
<Radio value="male">male</Radio>
<Radio value="female">female</Radio>
</RadioGroup>
{getFieldDecorator('gender', { initialValue: 'female' })(
<RadioGroup>
<Radio value="male">male</Radio>
<Radio value="female">female</Radio>
</RadioGroup>
)}
</FormItem>
<FormItem
{...formItemLayout}
label="remarks"
help="Please input something"
>
<Input type="textarea" placeholder="Please input something" {...getFieldProps('remark', { initialValue: '' })} />
{getFieldDecorator('remark', { initialValue: '' })(
<Input type="textarea" placeholder="Please input something" />
)}
</FormItem>
<FormItem
{...formItemLayout}
label={<span>Sold myself <Tooltip title="I come for Qiu Xiang"><Icon type="question-circle-o" /></Tooltip></span>}
>
<Checkbox {...getFieldProps('agreement', { initialValue: false, valuePropName: 'checked' })}>agree</Checkbox>
{getFieldDecorator('agreement', { initialValue: false, valuePropName: 'checked' })(
<Checkbox>agree</Checkbox>
)}
</FormItem>
<FormItem wrapperCol={{ span: 16, offset: 6 }} style={{ marginTop: 24 }}>
<Button type="primary" htmlType="submit">OK</Button>

View File

@ -1,6 +1,6 @@
---
order: 1
title:
title:
zh-CN: 平行排列
en-US: Inline form
---
@ -11,7 +11,7 @@ title:
## en-US
Inline form is often used for login.
Inline form is often used for login.
````jsx
import { Form, Input, Button, Checkbox } from 'antd';
@ -24,25 +24,27 @@ let Demo = React.createClass({
},
render() {
const { getFieldProps } = this.props.form;
const { getFieldDecorator } = this.props.form;
return (
<Form inline onSubmit={this.handleSubmit}>
<FormItem
label="Account"
>
<Input placeholder="Please input the account"
{...getFieldProps('userName')}
/>
{getFieldDecorator('userName')(
<Input placeholder="Please input the account" />
)}
</FormItem>
<FormItem
label="Password"
>
<Input type="password" placeholder="Please input the password"
{...getFieldProps('password')}
/>
{getFieldDecorator('password')(
<Input type="password" placeholder="Please input the password" />
)}
</FormItem>
<FormItem>
<Checkbox {...getFieldProps('agreement')}>Remember me</Checkbox>
{getFieldDecorator('agreement')(
<Checkbox>Remember me</Checkbox>
)}
</FormItem>
<Button type="primary" htmlType="submit">Submit</Button>
</Form>

View File

@ -1,6 +1,6 @@
---
order: 4
title:
title:
zh-CN: 输入框组合
en-US: Input group
---

View File

@ -1,6 +1,6 @@
---
order: 5
title:
title:
zh-CN: 表单组合
en-US: mix
---
@ -48,7 +48,7 @@ let Demo = React.createClass({
},
render() {
const { getFieldProps } = this.props.form;
const { getFieldDecorator } = this.props.form;
return (
<Form horizontal onSubmit={this.handleSubmit} >
<FormItem
@ -56,9 +56,9 @@ let Demo = React.createClass({
labelCol={{ span: 8 }}
wrapperCol={{ span: 10 }}
>
<InputNumber min={1} max={10} style={{ width: 100 }}
{...getFieldProps('inputNumber', { initialValue: 3 })}
/>
{getFieldDecorator('inputNumber', { initialValue: 3 })(
<InputNumber min={1} max={10} style={{ width: 100 }} />
)}
<span className="ant-form-text"> machines</span>
</FormItem>
@ -79,7 +79,9 @@ let Demo = React.createClass({
wrapperCol={{ span: 10 }}
required
>
<Switch {...getFieldProps('switch', { valuePropName: 'checked' })} />
{getFieldDecorator('switch', { valuePropName: 'checked' })(
<Switch />
)}
</FormItem>
<FormItem
@ -88,7 +90,9 @@ let Demo = React.createClass({
wrapperCol={{ span: 10 }}
required
>
<Slider marks={['A', 'B', 'C', 'D', 'E', 'F', 'G']} {...getFieldProps('slider')} />
{getFieldDecorator('slider')(
<Slider marks={{ 0: 'A', 20: 'B', 40: 'C', 60: 'D', 80: 'E', 100: 'F' }} />
)}
</FormItem>
<FormItem
@ -97,14 +101,14 @@ let Demo = React.createClass({
wrapperCol={{ span: 16 }}
required
>
<Select style={{ width: 200 }}
{...getFieldProps('select')}
>
<Option value="jack">jack</Option>
<Option value="lucy">lucy</Option>
<Option value="disabled" disabled>disabled</Option>
<Option value="yiminghe">yiminghe</Option>
</Select>
{getFieldDecorator('select')(
<Select style={{ width: 200 }}>
<Option value="jack">jack</Option>
<Option value="lucy">lucy</Option>
<Option value="disabled" disabled>disabled</Option>
<Option value="yiminghe">yiminghe</Option>
</Select>
)}
</FormItem>
<FormItem
@ -114,7 +118,9 @@ let Demo = React.createClass({
required
hasFeedback
>
<Cascader style={{ width: 200 }} options={areaData} {...getFieldProps('area')} />
{getFieldDecorator('area')(
<Cascader style={{ width: 200 }} options={areaData} />
)}
</FormItem>
<FormItem
@ -124,7 +130,9 @@ let Demo = React.createClass({
>
<Col span="6">
<FormItem>
<DatePicker {...getFieldProps('startDate')} />
{getFieldDecorator('startDate')(
<DatePicker />
)}
</FormItem>
</Col>
<Col span="1">
@ -132,7 +140,9 @@ let Demo = React.createClass({
</Col>
<Col span="6">
<FormItem>
<DatePicker {...getFieldProps('endDate')} />
{getFieldDecorator('endDate')(
<DatePicker />
)}
</FormItem>
</Col>
</FormItem>
@ -144,18 +154,22 @@ let Demo = React.createClass({
wrapperCol={{ span: 16 }}
required
>
<TimePicker {...getFieldProps('time')} />
{getFieldDecorator('time')(
<TimePicker />
)}
</FormItem>
<FormItem
label="Options"
labelCol={{ span: 8 }}
>
<RadioGroup {...getFieldProps('rg')}>
<RadioButton value="a">item 1</RadioButton>
<RadioButton value="b">item 2</RadioButton>
<RadioButton value="c">item 3</RadioButton>
</RadioGroup>
{getFieldDecorator('rg')(
<RadioGroup>
<RadioButton value="a">item 1</RadioButton>
<RadioButton value="b">item 2</RadioButton>
<RadioButton value="c">item 3</RadioButton>
</RadioGroup>
)}
</FormItem>
<FormItem
@ -164,16 +178,16 @@ let Demo = React.createClass({
wrapperCol={{ span: 16 }}
help="longgggggggggggggggggggggggggggggggggg"
>
<Upload name="logo" action="/upload.do" listType="picture" onChange={this.handleUpload}
{...getFieldProps('upload', {
valuePropName: 'fileList',
normalize: this.normFile,
})}
>
<Button type="ghost">
<Icon type="upload" /> Click to upload
</Button>
</Upload>
{getFieldDecorator('upload', {
valuePropName: 'fileList',
normalize: this.normFile,
})(
<Upload name="logo" action="/upload.do" listType="picture" onChange={this.handleUpload}>
<Button type="ghost">
<Icon type="upload" /> Click to upload
</Button>
</Upload>
)}
</FormItem>
<FormItem wrapperCol={{ span: 16, offset: 8 }} style={{ marginTop: 24 }}>

View File

@ -72,46 +72,7 @@ let BasicDemo = React.createClass({
},
render() {
const { getFieldProps, getFieldError, isFieldValidating } = this.props.form;
const nameProps = getFieldProps('name', {
rules: [
{ required: true, min: 5, message: 'User name for at least 5 characters' },
{ validator: this.userExists },
],
});
const emailProps = getFieldProps('email', {
validate: [{
rules: [
{ required: true },
],
trigger: 'onBlur',
}, {
rules: [
{ type: 'email', message: 'Please input the correct email' },
],
trigger: ['onBlur', 'onChange'],
}],
});
const passwdProps = getFieldProps('passwd', {
rules: [
{ required: true, whitespace: true, message: 'Please enter your password' },
{ validator: this.checkPass },
],
});
const rePasswdProps = getFieldProps('rePasswd', {
rules: [{
required: true,
whitespace: true,
message: 'Please confirm your password',
}, {
validator: this.checkPass2,
}],
});
const textareaProps = getFieldProps('textarea', {
rules: [
{ required: true, message: 'Really not supposed to write something?' },
],
});
const { getFieldDecorator, getFieldError, isFieldValidating } = this.props.form;
const formItemLayout = {
labelCol: { span: 7 },
wrapperCol: { span: 12 },
@ -124,7 +85,14 @@ let BasicDemo = React.createClass({
hasFeedback
help={isFieldValidating('name') ? 'validating...' : (getFieldError('name') || []).join(', ')}
>
<Input {...nameProps} placeholder="Real-tiem validation, try to input JasonWood" />
{getFieldDecorator('name', {
rules: [
{ required: true, min: 5, message: 'User name for at least 5 characters' },
{ validator: this.userExists },
],
})(
<Input placeholder="Real-tiem validation, try to input JasonWood" />
)}
</FormItem>
<FormItem
@ -132,7 +100,21 @@ let BasicDemo = React.createClass({
label="Email"
hasFeedback
>
<Input {...emailProps} type="email" placeholder="This control uses onBlur and onChange" />
{getFieldDecorator('email', {
validate: [{
rules: [
{ required: true },
],
trigger: 'onBlur',
}, {
rules: [
{ type: 'email', message: 'Please input the correct email' },
],
trigger: ['onBlur', 'onChange'],
}],
})(
<Input type="email" placeholder="This control uses onBlur and onChange" />
)}
</FormItem>
<FormItem
@ -140,9 +122,16 @@ let BasicDemo = React.createClass({
label="Password"
hasFeedback
>
<Input {...passwdProps} type="password" autoComplete="off"
onContextMenu={noop} onPaste={noop} onCopy={noop} onCut={noop}
/>
{getFieldDecorator('passwd', {
rules: [
{ required: true, whitespace: true, message: 'Please enter your password' },
{ validator: this.checkPass },
],
})(
<Input type="password" autoComplete="off"
onContextMenu={noop} onPaste={noop} onCopy={noop} onCut={noop}
/>
)}
</FormItem>
<FormItem
@ -150,16 +139,32 @@ let BasicDemo = React.createClass({
label="Confirm password"
hasFeedback
>
<Input {...rePasswdProps} type="password" autoComplete="off" placeholder="Two passwords that you enter must be consistent"
onContextMenu={noop} onPaste={noop} onCopy={noop} onCut={noop}
/>
{getFieldDecorator('rePasswd', {
rules: [{
required: true,
whitespace: true,
message: 'Please confirm your password',
}, {
validator: this.checkPass2,
}],
})(
<Input type="password" autoComplete="off" placeholder="Two passwords that you enter must be consistent"
onContextMenu={noop} onPaste={noop} onCopy={noop} onCut={noop}
/>
)}
</FormItem>
<FormItem
{...formItemLayout}
label="remark"
>
<Input {...textareaProps} type="textarea" placeholder="Please write something" id="textarea" name="textarea" />
{getFieldDecorator('textarea', {
rules: [
{ required: true, message: 'Really not supposed to write something?' },
],
})(
<Input type="textarea" placeholder="Please write something" id="textarea" name="textarea" />
)}
</FormItem>
<FormItem wrapperCol={{ span: 12, offset: 7 }}>

View File

@ -122,40 +122,31 @@ let Demo = React.createClass({
},
render() {
const { getFieldProps } = this.props.form;
const passProps = getFieldProps('pass', {
rules: [
{ required: true, whitespace: true, message: 'Please enter your password' },
{ validator: this.checkPass },
],
onChange: (e) => {
console.log('Your password is stolen in this way', e.target.value);
},
});
const rePassProps = getFieldProps('rePass', {
rules: [{
required: true,
whitespace: true,
message: 'Please confirm your password',
}, {
validator: this.checkPass2,
}],
});
const { getFieldDecorator } = this.props.form;
return (
<div>
<Form vertical style={{ maxWidth: 600 }} form={this.props.form}>
<Row type="flex" align="middle">
<Col span={12}>
<FormItem label="Password">
<Input {...passProps} type="password"
onContextMenu={noop} onPaste={noop} onCopy={noop} onCut={noop}
autoComplete="off" id="pass"
onBlur={(e) => {
const value = e.target.value;
this.setState({ dirty: this.state.dirty || !!value });
}}
/>
{getFieldDecorator('pass', {
rules: [
{ required: true, whitespace: true, message: 'Please enter your password' },
{ validator: this.checkPass },
],
})(
<Input type="password"
onContextMenu={noop} onPaste={noop} onCopy={noop} onCut={noop}
autoComplete="off" id="pass"
onChange={(e) => {
console.log('Your password is stolen in this way', e.target.value);
}}
onBlur={(e) => {
const value = e.target.value;
this.setState({ dirty: this.state.dirty || !!value });
}}
/>
)}
</FormItem>
</Col>
<Col span={12}>
@ -165,10 +156,20 @@ let Demo = React.createClass({
<Row type="flex" align="middle">
<Col span={12}>
<FormItem label="Confirm">
<Input {...rePassProps} type="password"
onContextMenu={noop} onPaste={noop} onCopy={noop} onCut={noop}
autoComplete="off" id="rePass"
/>
{getFieldDecorator('rePass', {
rules: [{
required: true,
whitespace: true,
message: 'Please confirm your password',
}, {
validator: this.checkPass2,
}],
})(
<Input type="password"
onContextMenu={noop} onPaste={noop} onCopy={noop} onCut={noop}
autoComplete="off" id="rePass"
/>
)}
</FormItem>
</Col>
<Col span={12}>

View File

@ -56,7 +56,7 @@ let Demo = React.createClass({
checkPrime(rule, value, callback) {
if (value !== 11) {
callback(new Error('The prime number between 8 to 12 is obiviously 11!'));
callback(new Error('The prime number between 8 to 12 is 11!'));
} else {
callback();
}
@ -71,45 +71,7 @@ let Demo = React.createClass({
label: 'Hang Zhou',
}],
}];
const { getFieldProps } = this.props.form;
const selectProps = getFieldProps('select', {
rules: [
{ required: true, message: 'Please select your country' },
],
});
const multiSelectProps = getFieldProps('multiSelect', {
rules: [
{ required: true, message: 'Please select your favourite colors', type: 'array' },
],
});
const radioProps = getFieldProps('radio', {
rules: [
{ required: true, message: 'Please select your gender' },
],
});
const birthdayProps = getFieldProps('birthday', {
rules: [
{
required: true,
type: 'date',
message: 'When is your birthday?',
}, {
validator: this.checkBirthday,
},
],
});
const timeProps = getFieldProps('time', {
getValueFromEvent: (value, timeString) => timeString,
rules: [
{ required: true, message: 'Please select the time' },
],
});
const primeNumberProps = getFieldProps('primeNumber', {
rules: [{ validator: this.checkPrime }],
});
const addressProps = getFieldProps('address', {
rules: [{ required: true, type: 'array' }],
});
const { getFieldDecorator } = this.props.form;
const formItemLayout = {
labelCol: { span: 7 },
wrapperCol: { span: 12 },
@ -120,80 +82,131 @@ let Demo = React.createClass({
{...formItemLayout}
label="Country"
>
<Select {...selectProps} placeholder="Please select a country" style={{ width: '100%' }}>
<Option value="china">China</Option>
<Option value="use">U.S.A</Option>
<Option value="japan">Japan</Option>
<Option value="korean">Korea</Option>
<Option value="Thailand">Thai</Option>
</Select>
{getFieldDecorator('select', {
rules: [
{ required: true, message: 'Please select your country' },
],
})(
<Select placeholder="Please select a country" style={{ width: '100%' }}>
<Option value="china">China</Option>
<Option value="use">U.S.A</Option>
<Option value="japan">Japan</Option>
<Option value="korean">Korea</Option>
<Option value="Thailand">Thai</Option>
</Select>
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Favourite colors"
>
<Select {...multiSelectProps} multiple placeholder="Please select favourite colors" style={{ width: '100%' }}>
<Option value="red">Red</Option>
<Option value="orange">Orange</Option>
<Option value="yellow">Yellow</Option>
<Option value="green">Green</Option>
<Option value="blue">Blue</Option>
</Select>
{getFieldDecorator('multiSelect', {
rules: [
{ required: true, message: 'Please select your favourite colors', type: 'array' },
],
})(
<Select multiple placeholder="Please select favourite colors" style={{ width: '100%' }}>
<Option value="red">Red</Option>
<Option value="orange">Orange</Option>
<Option value="yellow">Yellow</Option>
<Option value="green">Green</Option>
<Option value="blue">Blue</Option>
</Select>
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Gender"
>
<RadioGroup {...radioProps}>
<Radio value="male">male</Radio>
<Radio value="female">female</Radio>
</RadioGroup>
<span><Icon type="info-circle-o" /> Temporarily does not support ohter gender</span>
{getFieldDecorator('radio', {
rules: [
{ required: true, message: 'Please select your gender' },
],
})(
<RadioGroup>
<Radio value="male">male</Radio>
<Radio value="female">female</Radio>
</RadioGroup>
)}
<span><Icon type="info-circle-o" /> Temporarily no ohter gender</span>
</FormItem>
<FormItem
{...formItemLayout}
label="Hobby"
>
<Checkbox {...getFieldProps('eat', {
{getFieldDecorator('eat', {
valuePropName: 'checked',
})}>eat</Checkbox>
<Checkbox {...getFieldProps('sleep', {
})(
<Checkbox>eat</Checkbox>
)}
{getFieldDecorator('sleep', {
valuePropName: 'checked',
})}>sleeping</Checkbox>
<Checkbox {...getFieldProps('beat', {
})(
<Checkbox>sleeping</Checkbox>
)}
{getFieldDecorator('beat', {
valuePropName: 'checked',
})}>dozen doug</Checkbox>
})(
<Checkbox>dozen doug</Checkbox>
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Birthday"
>
<DatePicker {...birthdayProps} />
{getFieldDecorator('birthday', {
rules: [
{
required: true,
type: 'date',
message: 'When is your birthday?',
}, {
validator: this.checkBirthday,
},
],
})(
<DatePicker />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Select the time"
>
<TimePicker {...timeProps} />
{getFieldDecorator('time', {
getValueFromEvent: (value, timeString) => timeString,
rules: [
{ required: true, message: 'Please select the time' },
],
})(
<TimePicker />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="A prime number between 8 to 12"
label="Prime num between 8, 12"
>
<InputNumber {...primeNumberProps} min={8} max={12} />
{getFieldDecorator('primeNumber', {
rules: [{ validator: this.checkPrime }],
})(
<InputNumber min={8} max={12} />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Please select address"
>
<Cascader {...addressProps} options={address} />
{getFieldDecorator('address', {
rules: [{ required: true, type: 'array' }],
})(
<Cascader options={address} />
)}
</FormItem>
<FormItem

View File

@ -1,6 +1,6 @@
---
order: 6
title:
title:
zh-CN: 校验提示
en-US: Validation message
---

View File

@ -73,19 +73,15 @@ If the form has been decorated by `Form.create` then it has `this.props.form` pr
| getFieldError | Get the error of a field. | Function(name) |
| isFieldValidating | Check if the specified field is being validated. | Function(name) |
| resetFields | Reset the specified fields' value and status. If you don't specify a parameter, all the fields will be reset. | Function([names: string[]]) |
| getFieldProps | Two-way binding for form, please read below for details. | |
| getFieldDecorator | Two-way binding for form, please read below for details. | |
### this.props.form.getFieldProps(id, options)
### this.props.form.getFieldDecorator(id, options)
#### Special attention
If you use `react@<15.3.0`, then, you can't use `getFieldProps` in functional components: https://github.com/facebook/react/pull/6534
If you use `react@<15.3.0`, then, you can't use `getFieldDecorator` in stateless component: https://github.com/facebook/react/pull/6534
The return value of `getFieldProps` contains `id`、`value`(or any other props `valuePropName` that you specified),`ref`,`onChange`(or any other `trigger` `validateTrigger` that you specified), **shouldn't set same property again** in order to avoid conflict. If you concerntate on the details on return value, you can print them to console by `console.log`.
> Don't use `defaultValue` in form, please use `initialValue` instead of it.
#### getFieldProps options
#### getFieldDecorator's parameters
| Property | Description | Type | Default Value |
|-----------|-----------------------------------------|-----|--------|
@ -96,7 +92,6 @@ The return value of `getFieldProps` contains `id`、`value`(or any other props `
| options.getValueFromEvent | To convert parameters of onChange to the value of control, for example, set value of DatePicker: `(date, dateString) => dateString` | function(..args) | [reference](https://github.com/react-component/form#optiongetvaluefromevent) |
| options.validateTrigger | When to validate the value of children node. | string | 'onChange' |
| options.rules | Includes validation rules. Please refer to [async-validator](https://github.com/yiminghe/async-validator) for details. | array | n/a |
| options.onXXX | Because `getFieldProps` will replace events like `onChange`, `trigger`, `validateTrigger`, if you still want to bind these events, you may set them in `options` | function | n/a |
| options.exclusive | Whether it is exclusive with other controls, particularly for Radio. | boolean | false |
### Form.Item

View File

@ -75,30 +75,25 @@ CustomizedForm = Form.create({})(CustomizedForm);
| getFieldError | 获取某个输入控件的 Error | Function(name) |
| isFieldValidating | 判断一个输入控件是否在校验状态 | Function(name) |
| resetFields | 重置一组输入控件的值与状态,如不传入参数,则重置所有组件 | Function([names: string[]]) |
| getFieldProps | 用于和表单进行双向绑定,详见下方描述 | |
| getFieldDecorator | 用于和表单进行双向绑定,详见下方描述 | |
### this.props.form.getFieldProps(id, options)
### this.props.form.getFieldDecorator(id, options)
#### 特别注意
如果使用的是 `react@<15.3.0`,则 `getFieldProps` 调用不能位于纯函数组件中: https://github.com/facebook/react/pull/6534
如果使用的是 `react@<15.3.0`,则 `getFieldDecorator` 调用不能位于纯函数组件中: https://github.com/facebook/react/pull/6534
`getFieldProps` 返回的属性包括 `id`、`value`(或你设置的其它 `valuePropName`)、`ref`、`onChange`(或者你设置的其它 `trigger` `validateTrigger`**所以不应再设置同样的属性**,以免冲突。如果对其返回值的细节有兴趣,可以 `console.log` 出来查看。
> 在表单中 `defaultValue` 也不应该被设置,请使用下面的 `initialValue`
#### getFieldProps options
#### getFieldDecorator 参数
| 参数 | 说明 | 类型 | 默认值 |
|-----------|-----------------------------------------|-----|--------|
| options.id | 必填输入控件唯一标志 | string | |
| id | 必填输入控件唯一标志 | string | |
| options.valuePropName | 子节点的值的属性,如 Switch 的是 'checked' | string | 'value' |
| options.initialValue | 子节点的初始值,类型、可选值均由子节点决定 | | |
| options.trigger | 收集子节点的值的时机 | string | 'onChange' |
| options.getValueFromEvent | 可以把 onChange 的参数转化为控件的值,例如 DatePicker 可设为:`(date, dateString) => dateString` | function(..args) | [reference](https://github.com/react-component/form#optiongetvaluefromevent) |
| options.validateTrigger | 校验子节点值的时机 | string | 'onChange' |
| options.rules | 校验规则,参见 [async-validator](https://github.com/yiminghe/async-validator) | array | |
| options.onXXX | 由于 `getFieldProps` 会占用 `onChange` 等事件(即你所设置的 `trigger` `validateTrigger`),所以如果仍需绑定事件,请在 `options` 内设置 | function | 无 |
| options.exclusive | 是否和其他控件互斥,特别用于 Radio 单选控件 | boolean | false |
### Form.Item

View File

@ -48,13 +48,14 @@ export interface InputProps {
onPressEnter?: React.FormEventHandler;
onKeyDown?: React.FormEventHandler;
onChange?: React.FormEventHandler;
onClick?: React.FormEventHandler;
onBlur?: React.FormEventHandler;
autosize?: boolean | AutoSizeType;
}
export default class Input extends Component<InputProps, any> {
static Group: any;
static defaultProps = {
defaultValue: '',
disabled: false,
prefixCls: 'ant-input',
type: 'text',

View File

@ -9,10 +9,14 @@ title:
点击菜单,收起其他展开的所有菜单,保持菜单聚焦简洁。
> 该用法要求 antd@2.0+
## en-US
Click the menu and you will see that all the other menus gets collapsed to keep the entire menu compact.
> This demo is for antd@2.0+.
````jsx
import { Menu, Icon } from 'antd';
const SubMenu = Menu.SubMenu;
@ -26,25 +30,30 @@ const Sider = React.createClass({
},
handleClick(e) {
console.log('click ', e);
this.setState({
current: e.key,
openKeys: e.keyPath.slice(1),
});
this.setState({ current: e.key });
},
onToggle(info) {
this.setState({
openKeys: info.open ? info.keyPath : info.keyPath.slice(1),
});
onOpenChange(openKeys) {
const latestOpenKey = openKeys.find((key) => !(this.state.openKeys.indexOf(key) > -1));
this.setState({ openKeys: this.getKeyPath(latestOpenKey) });
},
getKeyPath(key) {
const map = {
sub1: ['sub1'],
sub2: ['sub2'],
sub3: ['sub2', 'sub3'],
sub4: ['sub4'],
};
return map[key] || [];
},
render() {
return (
<Menu onClick={this.handleClick}
style={{ width: 240 }}
openKeys={this.state.openKeys}
onOpen={this.onToggle}
onClose={this.onToggle}
selectedKeys={[this.state.current]}
<Menu
mode="inline"
openKeys={this.state.openKeys}
selectedKeys={[this.state.current]}
style={{ width: 240 }}
onOpenChange={this.onOpenChange}
onClick={this.handleClick}
>
<SubMenu key="sub1" title={<span><Icon type="mail" /><span>导航一</span></span>}>
<Menu.Item key="1">选项1</Menu.Item>

View File

@ -5,15 +5,6 @@ import animation from '../_util/openAnimation';
function noop() {
}
interface OpenCloseParam {
openKeys: Array<string>;
key: string;
item: any;
trigger: string;
open: boolean;
keyPath: Array<string>;
}
interface SelectParam {
key: string;
keyPath: Array<string>;
@ -43,8 +34,7 @@ export interface MenuProps {
openKeys?: Array<string>;
/** 初始展开的菜单项 key 数组*/
defaultOpenKeys?: Array<string>;
onOpen?: (param: OpenCloseParam) => void;
onClose?: (param: OpenCloseParam) => void;
onOpenChange?: (openKeys: string[]) => void;
/**
*
*
@ -71,8 +61,7 @@ export default class Menu extends React.Component<MenuProps, any> {
static defaultProps = {
prefixCls: 'ant-menu',
onClick: noop,
onOpen: noop,
onClose: noop,
onOpenChange: noop,
className: '',
theme: 'light', // or dark
};
@ -96,15 +85,9 @@ export default class Menu extends React.Component<MenuProps, any> {
this.setOpenKeys([]);
this.props.onClick(e);
}
handleOpenKeys = (e) => {
const { openKeys } = e;
handleOpenChange = (openKeys: string[]) => {
this.setOpenKeys(openKeys);
this.props.onOpen(e);
}
handleCloseKeys = (e) => {
const { openKeys } = e;
this.setOpenKeys(openKeys);
this.props.onClose(e);
this.props.onOpenChange(openKeys);
}
setOpenKeys(openKeys) {
if (!('openKeys' in this.props)) {
@ -144,8 +127,7 @@ export default class Menu extends React.Component<MenuProps, any> {
props = {
openKeys: this.state.openKeys,
onClick: this.handleClick,
onOpen: this.handleOpenKeys,
onClose: this.handleCloseKeys,
onOpenChange: this.handleOpenChange,
openTransitionName: openAnimation,
className,
};

View File

@ -35,8 +35,7 @@ subtitle: 导航菜单
| defaultSelectedKeys | 初始选中的菜单项 key 数组 | Array | |
| openKeys | 当前展开的 SubMenu 菜单项 key 数组 | Array | |
| defaultOpenKeys | 初始展开的 SubMenu 菜单项 key 数组 | | |
| onOpen | SubMenu 展开回调 | Function({ key, item, keyPath }) | |
| onClose | SubMenu 收起回调 | Function({ key, item, keyPath }) | |
| onOpenChange | SubMenu 展开/关闭的回调 | Function(openKeys: string[]) | noop |
| onSelect | 被选中时调 | Function({ item, key, selectedKeys }) | 无 |
| onDeselect | 取消选中时调用,仅在 multiple 生效 | Function({ item, key, selectedKeys }) | - |
| onClick | 点击 menuitem 调用此函数,参数为 {item, key, keyPath} | function | - |

View File

@ -33,8 +33,8 @@
"typings": "lib/index.d.ts",
"license": "MIT",
"dependencies": {
"babel-runtime": "6.x",
"array-tree-filter": "~1.0.0",
"babel-runtime": "6.x",
"classnames": "~2.2.0",
"css-animation": "^1.2.5",
"gregorian-calendar": "~4.1.0",
@ -49,16 +49,16 @@
"rc-dialog": "~6.2.1",
"rc-dropdown": "~1.4.8",
"rc-editor-mention": "^0.2.2",
"rc-form": "~0.17.1",
"rc-form": "~1.0.0",
"rc-input-number": "~2.6.3",
"rc-menu": "~4.13.0",
"rc-menu": "^5.0.0",
"rc-notification": "~1.3.4",
"rc-pagination": "~1.5.3",
"rc-progress": "~1.0.4",
"rc-queue-anim": "~0.12.4",
"rc-radio": "~2.0.0",
"rc-rate": "~1.1.2",
"rc-select": "~6.4.6",
"rc-select": "^6.5.1",
"rc-slider": "~4.0.0",
"rc-steps": "~2.1.5",
"rc-switch": "~1.4.2",

View File

@ -11,5 +11,33 @@ module.exports = {
'app.header.menu.spec': 'Specification',
'app.header.menu.resource': 'Resource',
'app.header.lang': '中文',
'app.home.slogan': 'One Design Language for UI',
'app.home.start': 'Getting Started',
'app.home.best-practice': 'Best Practice',
'app.home.experience': 'Designed by experienced team, and showcase dozens of inspiring projects.',
'app.home.design-pattern': 'Design Pattern',
'app.home.pattern': 'Provide solutions for usual problems that may be encountered while developing enterprise-like complex UIs.',
'app.home.reusable-components': 'Dozens of Reusable Components',
'app.home.components-intro': 'Dozens of flexible and practical reusable components that increase your productivity.',
'app.home.learn-more': 'Learn more',
'app.home.sub-slogan': 'A Little Happiness in Hand',
'app.home.vision': 'This is a design language dedicated to improve the user and design experience.',
'app.footer.repo': 'Repository',
'app.footer.scaffold': 'Scaffold',
'app.footer.dev-tools': 'Developer Tools',
'app.footer.dva': 'Framework',
'app.footer.links': 'Links',
'app.footer.mobile': 'Mobile',
'app.footer.data-vis': 'Data Visualization',
'app.footer.data-vis-spec': 'Specification of Data Visualization',
'app.footer.motion': 'Motion',
'app.footer.material': 'Sitemap Template',
'app.footer.community': 'Community',
'app.footer.change-log': 'Change Log',
'app.footer.feedback': 'Feedback',
'app.footer.discuss': 'Chat Room',
'app.footer.bug-report': 'Bug Report',
'app.footer.version': 'Version: ',
'app.footer.author': 'Created by Ant UED',
},
};

View File

@ -1,5 +1,6 @@
import React from 'react';
import { Link } from 'react-router';
import { FormattedMessage } from 'react-intl';
import ScrollElement from 'rc-scroll-anim/lib/ScrollElement';
import GitHubButton from 'react-github-button';
import 'react-github-button/assets/style.css';
@ -22,11 +23,12 @@ export default class Banner extends React.Component {
<ScrollElement scrollName="banner" className="page">
<QueueAnim className="banner-text-wrapper" type={this.typeFunc} delay={300}>
<h2 key="h2">ANT <p>DESIGN</p></h2>
<p key="content">一个 UI 设计语言</p>
<p key="content"><FormattedMessage id="app.home.slogan" /></p>
<span className="line" key="line" />
<div key="button" className="start-button clearfix">
<Link to="/docs/spec/introduce">
<Icon type="smile-circle" /> 开始探索
<Icon type="smile-circle" />
<FormattedMessage id="app.home.start" />
</Link>
</div>
<GitHubButton key="github-button" type="stargazers"

View File

@ -1,5 +1,6 @@
import React from 'react';
import { Link } from 'react-router';
import { FormattedMessage } from 'react-intl';
import TweenOne from 'rc-tween-one';
import ScrollOverPack from 'rc-scroll-anim/lib/ScrollOverPack';
import { Icon, Button } from 'antd';
@ -28,12 +29,12 @@ export default function Page1() {
style={{ transform: 'translateX(-100px)', opacity: 0 }}
/>
<QueueAnim className="text-wrapper" delay={300} key="text" duration={550} leaveReverse>
<h2 key="h2">最佳实践</h2>
<p key="p" style={{ maxWidth: 310 }}>近一年的中后台设计实践积累了大量的优秀案例</p>
<h2 key="h2"><FormattedMessage id="app.home.best-practice" /></h2>
<p key="p" style={{ maxWidth: 310 }}><FormattedMessage id="app.home.experience" /></p>
<div key="button">
<Link to="/docs/practice/cases">
<Button type="primary" size="large">
了解更多
<FormattedMessage id="app.home.learn-more" />
<Icon type="right" />
</Button>
</Link>

View File

@ -1,4 +1,5 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { Link } from 'react-router';
import TweenOne from 'rc-tween-one';
import ScrollOverPack from 'rc-scroll-anim/lib/ScrollOverPack';
@ -14,12 +15,12 @@ export default function Page2() {
<QueueAnim className="text-wrapper left-text" delay={300} key="text"
duration={550} type="bottom" leaveReverse
>
<h2 key="h2">设计模式</h2>
<p key="p" style={{ maxWidth: 260 }}>总结中后台设计中反复出现的问题并提供相应的解决方案</p>
<h2 key="h2"><FormattedMessage id="app.home.design-pattern" /></h2>
<p key="p" style={{ maxWidth: 260 }}><FormattedMessage id="app.home.pattern" /></p>
<div key="button">
<Link to="/docs/pattern/navigation">
<Button type="primary" size="large">
了解更多
<FormattedMessage id="app.home.learn-more" />
<Icon type="right" />
</Button>
</Link>

View File

@ -1,5 +1,6 @@
import React from 'react';
import { Link } from 'react-router';
import { FormattedMessage } from 'react-intl';
import TweenOne from 'rc-tween-one';
import ScrollOverPack from 'rc-scroll-anim/lib/ScrollOverPack';
import { Icon, Button } from 'antd';
@ -17,12 +18,12 @@ export default function Page3() {
<QueueAnim className="text-wrapper" delay={300} key="text" duration={550}
leaveReverse style={{ top: '40%' }}
>
<h2 key="h2">丰富的基础组件</h2>
<p key="p" style={{ maxWidth: 280 }}>丰富灵活实用的基础组件为业务产品提供强有力的设计支持</p>
<h2 key="h2"><FormattedMessage id="app.home.reusable-components" /></h2>
<p key="p" style={{ maxWidth: 280 }}><FormattedMessage id="app.home.components-intro" /></p>
<div key="button">
<Link to="/docs/react/introduce">
<Button type="primary" size="large">
了解更多
<FormattedMessage id="app.home.learn-more" />
<Icon type="right" />
</Button>
</Link>

View File

@ -1,5 +1,6 @@
import React from 'react';
import TweenOne from 'rc-tween-one';
import { FormattedMessage } from 'react-intl';
import ScrollOverPack from 'rc-scroll-anim/lib/ScrollOverPack';
import QueueAnim from 'rc-queue-anim';
@ -11,8 +12,8 @@ export default function Page4() {
<QueueAnim className="text-wrapper-bottom" delay={300} key="text" duration={550}
leaveReverse type="bottom"
>
<h2 key="h2">微小·确定·幸福</h2>
<p key="p">这是一套致力于提升用户设计者使用体验的中后台设计语言</p>
<h2 key="h2"><FormattedMessage id="app.home.sub-slogan" /></h2>
<p key="p"><FormattedMessage id="app.home.vision" /></p>
</QueueAnim>
<TweenOne key="image" className="image4 bottom-wrapper" animation={{ y: 0, opacity: 1, duration: 550, delay: 550 }}
style={{ transform: 'translateY(50px)', opacity: 0 }}

View File

@ -1,4 +1,5 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { Select, Modal } from 'antd';
import { version as antdVersion } from 'antd/package.json';
import { docVersions } from '../../';
@ -68,54 +69,67 @@ export default class Footer extends React.Component {
<li>
<h2>GitHub</h2>
<div>
<a target="_blank " href="https://github.com/ant-design/ant-design">仓库</a>
<a target="_blank " href="https://github.com/ant-design/ant-design">
<FormattedMessage id="app.footer.repo" />
</a>
</div>
<div>
<a target="_blank" rel="noopener noreferrer" href="https://github.com/ant-design/antd-init">antd-init</a> - 脚手架
<a target="_blank" rel="noopener noreferrer" href="https://github.com/ant-design/antd-init">antd-init</a> -
<FormattedMessage id="app.footer.scaffold" />
</div>
<div>
<a target="_blank" rel="noopener noreferrer" href="http://ant-tool.github.io">ant-tool</a> - 开发工具
<a target="_blank" rel="noopener noreferrer" href="http://ant-tool.github.io">ant-tool</a> - <FormattedMessage id="app.footer.dev-tools" />
</div>
<div>
<a target="_blank" rel="noopener noreferrer" href="https://github.com/dvajs/dva">dva</a> - 应用框架
<a target="_blank" rel="noopener noreferrer" href="https://github.com/dvajs/dva">dva</a> - <FormattedMessage id="app.footer.dva" />
</div>
</li>
<li>
<h2>相关站点</h2>
<div><a href="http://mobile.ant.design">Ant Design Mobile</a> - 移动版</div>
<div><a href="https://g2.alipay.com/">G2</a> - 数据可视化</div>
<div><a href="https://antv.alipay.com/">AntV</a> - 数据可视化规范</div>
<div><a href="http://motion.ant.design">Ant Motion</a> - 设计动效</div>
<div><a href="http://ux.ant.design">Ant UX</a> - 页面逻辑素材</div>
<h2><FormattedMessage id="app.footer.links" /></h2>
<div><a href="http://mobile.ant.design">Ant Design Mobile</a> -
<FormattedMessage id="app.footer.mobile" />
</div>
<div><a href="https://g2.alipay.com/">G2</a> -
<FormattedMessage id="app.footer.data-vis" />
</div>
<div><a href="https://antv.alipay.com/">AntV</a> -
<FormattedMessage id="app.footer.data-vis-spec" />
</div>
<div><a href="http://motion.ant.design">Ant Motion</a> -
<FormattedMessage id="app.footer.motion" />
</div>
<div><a href="http://ux.ant.design">Ant UX</a> -
<FormattedMessage id="app.footer.material" />
</div>
</li>
<li>
<h2>社区</h2>
<h2><FormattedMessage id="app.footer.community" /></h2>
<div>
<a target="_blank" rel="noopener noreferrer" href="http://ant.design/changelog">
更新记录
<FormattedMessage id="app.footer.change-log" />
</a>
</div>
<div>
<a target="_blank" rel="noopener noreferrer" href="https://github.com/ant-design/ant-design/issues">
反馈和建议
<FormattedMessage id="app.footer.feedback" />
</a>
</div>
<div>
<a target="_blank" rel="noopener noreferrer" href="https://gitter.im/ant-design/ant-design">
讨论
<FormattedMessage id="app.footer.discuss" />
</a>
</div>
<div>
<a target="_blank" rel="noopener noreferrer" href="https://github.com/ant-design/ant-design/issues/new">
报告 Bug
<FormattedMessage id="app.footer.bug-report" />
</a>
</div>
</li>
<li>
<div>©2016 蚂蚁金服体验技术部出品</div>
<div>©2016 <FormattedMessage id="app.footer.author" /></div>
<div>Powered by <a href="https://github.com/benjycui/bisheng">BiSheng</a></div>
<div style={{ marginTop: 10 }}>
文档版本
<FormattedMessage id="app.footer.version" />
<Select
size="small"
dropdownMatchSelectWidth={false}

View File

@ -11,5 +11,33 @@ module.exports = {
'app.header.menu.spec': '语言',
'app.header.menu.resource': '资源',
'app.header.lang': 'EN',
'app.home.slogan': '一个 UI 设计语言',
'app.home.start': '开始探索',
'app.home.best-practice': '最佳实践',
'app.home.experience': '近一年的中后台设计实践,积累了大量的优秀案例。',
'app.home.design-pattern': '设计模式',
'app.home.pattern': '总结中后台设计中反复出现的问题,并提供相应的解决方案。',
'app.home.reusable-components': '丰富的基础组件',
'app.home.components-intro': '丰富、灵活、实用的基础组件,为业务产品提供强有力的设计支持。',
'app.home.learn-more': '了解更多',
'app.home.sub-slogan': '微小·确定·幸福',
'app.home.vision': '这是一套致力于提升『用户』和『设计者』使用体验的中后台设计语言。',
'app.footer.repo': '仓库',
'app.footer.scaffold': '脚手架',
'app.footer.dev-tools': '开发工具',
'app.footer.dva': '应用框架',
'app.footer.links': '相关站点',
'app.footer.mobile': '移动版',
'app.footer.data-vis': '数据可视化',
'app.footer.data-vis-spec': '数据可视化规范',
'app.footer.motion': '设计动效',
'app.footer.material': '页面逻辑素材',
'app.footer.community': '社区',
'app.footer.change-log': '更新记录',
'app.footer.feedback': '反馈和建议',
'app.footer.discuss': '讨论',
'app.footer.bug-report': '报告 Bug,',
'app.footer.version': '文档版本:',
'app.footer.author': '蚂蚁金服体验技术部出品',
},
};