Merge pull request #2259 from RaoHai/MentionComponent

Mention Component
This commit is contained in:
yiminghe 2016-07-20 11:17:19 +08:00 committed by GitHub
commit 6158b39335
12 changed files with 540 additions and 0 deletions

View File

@ -48,3 +48,6 @@ export { Timeline }
import Tooltip from './tooltip';
export { Tooltip }
import Mention from './mention';
export { Mention };

View File

@ -0,0 +1,56 @@
---
order: 1
title: 异步加载
---
## zh-CN
匹配内容列表为异步返回时。
## en-US
asnyc
````jsx
import { Mention } from 'antd';
const users = ['afc163', 'benjycui', 'yiminghe', 'jljsj33', 'dqaria', 'RaoHai'];
const AsyncMention = React.createClass({
getInitialState() {
return {
suggestions: [],
loading: false,
};
},
fetchSuggestions(value, callback) {
setTimeout(() => {
callback(users.filter(item => item.indexOf(value) !== -1));
}, 500);
},
onSearchChange(value) {
this.fetchSuggestions(value, (suggestions) => {
this.setState({
suggestions,
loading: false,
});
});
this.setState({
loading: true,
});
},
render() {
const { suggestions, loading } = this.state;
return (<Mention
loading={loading}
suggestions={suggestions}
onSearchChange={this.onSearchChange}
/>);
},
});
ReactDOM.render(
<AsyncMention />,
mountNode
);
````

View File

@ -0,0 +1,66 @@
---
order: 3
title: 头像
---
## zh-CN
自定义建议(含头像)
注意自定义建议时onSearchChange 必须不能为空。
## en-US
Customize suggestions
````jsx
import { Mention } from 'antd';
const Nav = Mention.Nav;
const webFrameworks = [
{ name: 'React', type: 'JavaScript', icon: 'https://zos.alipayobjects.com/rmsportal/LFIeMPzdLcLnEUe.svg' },
{ name: 'Angular', type: 'JavaScript', icon: 'https://zos.alipayobjects.com/rmsportal/PJTbxSvzYWjDZnJ.png' },
{ name: 'Laravel', type: 'PHP', icon: 'http://laravel-china.org/assets/img/laravel-logo.png' },
{ name: 'Flask', type: 'Python', icon: 'https://zos.alipayobjects.com/rmsportal/xaypBUijfnpAlXE.png' },
];
const CustomNavMention = React.createClass({
getInitialState() {
return {
suggestions: [],
loading: false,
};
},
onSearchChange(value) {
const searchValue = value.toLowerCase();
const filtered = webFrameworks.filter(item =>
item.name.toLowerCase().indexOf(searchValue) !== -1
);
const suggestions = filtered.map(suggestion =>
<Nav value={suggestion.name} data={suggestion} disabled={suggestion.disabled}>
<span>
<img alt={suggestion.name} style={{ height: 16, width: 16, marginRight: 5 }} src={suggestion.icon} />
{suggestion.name} - {suggestion.type}
</span>
</Nav>);
this.setState({
suggestions,
});
},
render() {
const { suggestions, loading } = this.state;
return (<Mention
loading={loading}
suggestions={suggestions}
onSearchChange={this.onSearchChange}
/>);
},
});
ReactDOM.render(
<CustomNavMention />,
mountNode
);
````

View File

@ -0,0 +1,32 @@
---
order: 0
title: 基本使用
---
## zh-CN
基本使用
## en-US
Basic usage.
````jsx
import { Mention } from 'antd';
const { toString, toEditorState } = Mention;
function onChange(editorState) {
console.log(toString(editorState));
}
ReactDOM.render(
<Mention
onChange={onChange}
defaultValue={toEditorState('@afc163')}
suggestions={['afc163', 'benjycui', 'yiminghe', 'jljsj33', 'dqaria', 'RaoHai']}
/>,
mountNode
);
````

View File

@ -0,0 +1,63 @@
---
order: 2
title: 自定义建议
---
## zh-CN
自定义建议
注意自定义建议时onSearchChange 必须不能为空。
## en-US
Customize suggestions
````jsx
import { Mention } from 'antd';
const Nav = Mention.Nav;
const webFrameworks = [
{ name: 'React', type: 'JavaScript' },
{ name: 'Angular', type: 'JavaScript' },
{ name: 'Laravel', type: 'PHP', disabled: true },
{ name: 'Flask', type: 'Python' },
{ name: 'Django', type: 'Python' },
];
const CustomNavMention = React.createClass({
getInitialState() {
return {
suggestions: [],
loading: false,
};
},
onSearchChange(value) {
const searchValue = value.toLowerCase();
const filtered = webFrameworks.filter(item =>
item.name.toLowerCase().indexOf(searchValue) !== -1
);
const suggestions = filtered.map(suggestion =>
<Nav value={suggestion.name} >
<span>{suggestion.name} - {suggestion.type} </span>
</Nav>);
this.setState({
suggestions,
});
},
render() {
const { suggestions, loading } = this.state;
return (<Mention
loading={loading}
suggestions={suggestions}
onSearchChange={this.onSearchChange}
/>);
},
});
ReactDOM.render(
<CustomNavMention />,
mountNode
);
````

View File

@ -0,0 +1,32 @@
---
order: 4
title: 多行
---
## zh-CN
多行模式,多行模式必须指定高度。
## en-US
Multi lines mode.
````jsx
import { Mention } from 'antd';
const { toString } = Mention;
function onChange(editorState) {
console.log(toString(editorState));
}
ReactDOM.render(
<Mention
style={{ width: '100%', height: 200 }}
onChange={onChange}
suggestions={['afc163', 'benjycui', 'yiminghe', 'jljsj33', 'dqaria', 'RaoHai']}
multiLines
/>,
mountNode
);
````

View File

@ -0,0 +1,43 @@
---
category: Components
chinese: 提及
cols: 1
type: Views
english: Mention
---
Mention component。
## When To Use
When need to mention someone or something.
```html
<Mention
onChange={onChange}
suggestions={['afc163', 'benjycui', 'yiminghe', 'jljsj33', 'dqaria', 'RaoHai']}
/>,
```
## API
### Mention props
| Property | Description | Type | Default |
|----------|---------------|----------|--------------|
| suggestions | suggestion content | Array<string> or Array<Mention.Nav> | [] |
| suggestionStyle | style of suggestion container | Objet | {} |
| onSearchChange | Callback function called when search content changes | function(value:String) | [] |
| onChange | Callback function called when content of input changes | function(editorState: EditorState) | null |
| notFoundContent| suggestion when suggestions empty | string | '无匹配结果,轻敲空格完成输入' |
| loading | loading mode | boolean | false |
| multiLines | multilines mode | boolean | false |
| defaultValue | default value | EditorState, you can use `Mention.toEditorState` to convert text to `EditorState` | null |
| value | core state of mention | EditorState | null |
### Nav props
| Property | Description | Type | Default |
|----------|---------------|----------|--------------|
| value | value of suggestionthe value will insert into input filed while selected | string | "" |
| children | suggestion content | Objet | {} |

View File

@ -0,0 +1,101 @@
import * as React from 'react';
import RcMention, { Nav, toString, toEditorState } from 'rc-editor-mention';
import classnames from 'classnames';
export interface MentionProps {
prefixCls: string;
suggestionStyle?: Object;
suggestions?: Array<any>;
onSearchChange?: Function;
onChange?: Function;
notFoundContent?: any;
loading?: Boolean;
style?: Object;
defaultValue?: string;
className?: string;
multiLines?: Boolean;
}
export interface MentionState {
suggestions?: Array<any>;
focus?: Boolean;
}
export default class Mention extends React.Component<MentionProps, MentionState> {
static Nav = Nav;
static toString = toString;
static toEditorState = toEditorState;
static defaultProps = {
prefixCls: 'ant-mention',
suggestions: [],
notFoundContent: '无匹配结果,轻敲空格完成输入',
loading: false,
multiLines: false,
};
constructor(props) {
super(props);
this.state = {
suggestions: props.suggestions,
focus: false,
};
}
componentWillReceiveProps(nextProps) {
this.setState({
suggestions: nextProps.suggestions,
});
}
onSearchChange(value) {
if (this.props.onSearchChange) {
return this.props.onSearchChange(value);
}
return this.defaultSearchChange(value);
}
onChange(editorState) {
if (this.props.onChange) {
this.props.onChange(editorState);
}
}
defaultSearchChange(value: String): void {
const searchValue = value.toLowerCase();
const filteredSuggestions = this.props.suggestions.filter(
suggestion => suggestion.toLowerCase().indexOf(searchValue) !== -1
);
this.setState({
suggestions: filteredSuggestions,
});
}
render() {
const { className, prefixCls, style, multiLines, defaultValue } = this.props;
let { notFoundContent } = this.props;
const { suggestions, focus } = this.state;
const cls = classnames({
[className]: !!className,
['active']: focus,
});
if (this.props.loading) {
notFoundContent = <i className="anticon anticon-loading"></i>;
}
return <RcMention
{...this.props}
className={cls}
prefixCls={prefixCls}
style={style}
defaultValue={defaultValue}
multiLines={multiLines}
onSearchChange={this.onSearchChange.bind(this)}
onChange={this.onChange.bind(this)}
onFocus={() => this.setState({focus: true})}
onBlur={() => this.setState({focus: false})}
suggestions={suggestions}
notFoundContent={notFoundContent}
/>;
}
}

View File

@ -0,0 +1,44 @@
---
category: Components
chinese: 提及
cols: 1
type: Views
english: Mention
---
提及组件。(圈人组件)
## 何时使用
用于在输入中提及某人或某事。
```html
<Mention
onChange={onChange}
suggestions={['afc163', 'benjycui', 'yiminghe', 'jljsj33', 'dqaria', 'RaoHai']}
/>,
```
## API
### Mention props
| 参数 | 说明 | 类型 | 默认值 |
|----------|---------------|----------|--------------|
| suggestions | 建议内容 | Array<string> or Array<Mention.Nav> | [] |
| suggestionStyle | 弹出下拉框样式 | Objet | {} |
| onSearchChange | 输入框中 @ 变化时回调 | function(value:String) | [] |
| onChange | 输入框内容变化时回调 | function(editorState: EditorState) | null |
| notFoundContent| 未找到时的内容 | string | '无匹配结果,轻敲空格完成输入' |
| loading | 加载中 | boolean | false |
| multiLines | 多行模式 | boolean | false |
| defaultValue | 默认值 | EditorState, 可以用 Mention.toEditorState(text) 把文字转换成 EditorState | null |
| value | 值 | EditorState | null |
### Nav props
| 参数 | 说明 | 类型 | 默认值 |
|----------|---------------|----------|--------------|
| value | 建议值,选择建议时,用此值插入到输入框中 | string | "" |
| children | 建议内容 | Objet | {} |

View File

@ -0,0 +1,98 @@
@import "../../style/themes/default";
@import "../../style/mixins/index";
@import '../../input/style/mixin.less';
.ant-mention-wrapper {
position: relative;
display: inline-block;
width: 100%;
&.active .ant-mention-editor {
.active;
}
.ant-mention-editor {
.input;
padding: 0;
}
.ant-mention-editor-wrapper {
overflow-y: auto;
height: auto;
}
.DraftEditor-editorContainer .public-DraftEditor-content {
height: auto;
padding: 4px 7px;
}
.ant-mention-dropdown {
margin-top: 1.5em;
max-height: 250px;
min-width: 120px;
background-color: white;
border: 1px solid @border-color-base;
box-shadow: @box-shadow-base;
border-radius: @border-radius-base;
box-sizing: border-box;
z-index: @zindex-dropdown;
left: -9999px;
top: -9999px;
position: absolute;
outline: none;
overflow-x: hidden;
overflow-y: auto;
font-size: @font-size-base;
&-notfound.ant-mention-dropdown-item {
color: #ccc;
.anticon-loading {
color: @primary-color;
}
}
&-item {
position: relative;
display: block;
padding: 7px 15px;
font-weight: normal;
color: #666;
white-space: nowrap;
cursor: pointer;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
transition: background 0.3s ease;
&:hover,
&.focus,
&-active {
background-color: tint(@primary-color, 90%);
}
&-disabled {
color: #ccc;
cursor: not-allowed;
&:hover {
color: #ccc;
background-color: #fff;
cursor: not-allowed;
}
}
&-selected {
&,
&:hover {
background-color: #f7f7f7;
font-weight: bold;
color: #666;
}
}
&-divider {
height: 1px;
margin: 1px 0;
overflow: hidden;
background-color: #e5e5e5;
line-height: 0;
}
}
}
}

View File

@ -0,0 +1 @@
import './index.less';

View File

@ -47,6 +47,7 @@
"rc-collapse": "~1.6.4",
"rc-dialog": "~6.2.1",
"rc-dropdown": "~1.4.8",
"rc-editor-mention": "^0.1.0",
"rc-form": "~0.17.1",
"rc-input-number": "~2.5.14",
"rc-menu": "~4.12.4",