mirror of
https://github.com/ant-design/ant-design.git
synced 2025-06-12 12:23:08 +08:00
Mention Component
This commit is contained in:
parent
f0995bd1e3
commit
01fabac77a
@ -48,3 +48,6 @@ export { Timeline }
|
|||||||
|
|
||||||
import Tooltip from './tooltip';
|
import Tooltip from './tooltip';
|
||||||
export { Tooltip }
|
export { Tooltip }
|
||||||
|
|
||||||
|
import Mention from './mention';
|
||||||
|
export { Mention };
|
||||||
|
56
components/mention/demo/async.md
Normal file
56
components/mention/demo/async.md
Normal 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
|
||||||
|
);
|
||||||
|
````
|
66
components/mention/demo/avatar.md
Normal file
66
components/mention/demo/avatar.md
Normal 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
|
||||||
|
);
|
||||||
|
````
|
30
components/mention/demo/basic.md
Normal file
30
components/mention/demo/basic.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
order: 0
|
||||||
|
title: 基本使用
|
||||||
|
---
|
||||||
|
|
||||||
|
## zh-CN
|
||||||
|
|
||||||
|
基本使用
|
||||||
|
|
||||||
|
## en-US
|
||||||
|
|
||||||
|
Basic usage.
|
||||||
|
|
||||||
|
````jsx
|
||||||
|
|
||||||
|
import { Mention } from 'antd';
|
||||||
|
|
||||||
|
function onChange(value) {
|
||||||
|
console.log(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<Mention
|
||||||
|
onChange={onChange}
|
||||||
|
defaultValue="@afc163"
|
||||||
|
suggestions={['afc163', 'benjycui', 'yiminghe', 'jljsj33', 'dqaria', 'RaoHai']}
|
||||||
|
/>,
|
||||||
|
mountNode
|
||||||
|
);
|
||||||
|
````
|
64
components/mention/demo/customTag.md
Normal file
64
components/mention/demo/customTag.md
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
---
|
||||||
|
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
|
||||||
|
);
|
||||||
|
````
|
31
components/mention/demo/multilines.md
Normal file
31
components/mention/demo/multilines.md
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
order: 4
|
||||||
|
title: 多行
|
||||||
|
---
|
||||||
|
|
||||||
|
## zh-CN
|
||||||
|
|
||||||
|
多行模式,多行模式必须指定高度。
|
||||||
|
|
||||||
|
## en-US
|
||||||
|
|
||||||
|
Multi lines mode.
|
||||||
|
|
||||||
|
````jsx
|
||||||
|
|
||||||
|
import { Mention } from 'antd';
|
||||||
|
|
||||||
|
function onChange(value) {
|
||||||
|
console.log(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<Mention
|
||||||
|
style={{ width: '100%', height: 200 }}
|
||||||
|
onChange={onChange}
|
||||||
|
suggestions={['afc163', 'benjycui', 'yiminghe', 'jljsj33', 'dqaria', 'RaoHai']}
|
||||||
|
multiLines
|
||||||
|
/>,
|
||||||
|
mountNode
|
||||||
|
);
|
||||||
|
````
|
42
components/mention/index.en-US.md
Normal file
42
components/mention/index.en-US.md
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
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(value:String) | null |
|
||||||
|
| notFoundContent| suggestion when suggestions empty | string | '无匹配结果,轻敲空格完成输入' |
|
||||||
|
| loading | loading mode | boolean | false |
|
||||||
|
| multiLines | multilines mode | boolean | false |
|
||||||
|
| defaultValue | default value | string | null |
|
||||||
|
|
||||||
|
### Nav props
|
||||||
|
|
||||||
|
| Property | Description | Type | Default |
|
||||||
|
|----------|---------------|----------|--------------|
|
||||||
|
| value | value of suggestion,the value will insert into input filed while selected | string | "" |
|
||||||
|
| children | suggestion content | Objet | {} |
|
97
components/mention/index.tsx
Normal file
97
components/mention/index.tsx
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import RcMention, { Nav } 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;
|
||||||
|
multiLines?: Boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MentionState {
|
||||||
|
suggestions?: Array<any>;
|
||||||
|
focus?: Boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Mention extends React.Component<MentionProps, MentionState> {
|
||||||
|
static Nav = Nav;
|
||||||
|
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) {
|
||||||
|
console.log('onSearchChange', this.props.onSearchChange);
|
||||||
|
if (this.props.onSearchChange) {
|
||||||
|
return this.props.onSearchChange(value);
|
||||||
|
}
|
||||||
|
return this.defaultSearchChange(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(editorState, textValue) {
|
||||||
|
if (this.props.onChange) {
|
||||||
|
this.props.onChange(textValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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}
|
||||||
|
/>;
|
||||||
|
}
|
||||||
|
}
|
43
components/mention/index.zh-CN.md
Normal file
43
components/mention/index.zh-CN.md
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
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(value:String) | null |
|
||||||
|
| notFoundContent| 未找到时的内容 | string | '无匹配结果,轻敲空格完成输入' |
|
||||||
|
| loading | 加载中 | boolean | false |
|
||||||
|
| multiLines | 多行模式 | boolean | false |
|
||||||
|
| defaultValue | 默认值 | string | null |
|
||||||
|
|
||||||
|
|
||||||
|
### Nav props
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认值 |
|
||||||
|
|----------|---------------|----------|--------------|
|
||||||
|
| value | 建议值,选择建议时,用此值插入到输入框中 | string | "" |
|
||||||
|
| children | 建议内容 | Objet | {} |
|
99
components/mention/style/index.less
Normal file
99
components/mention/style/index.less
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
@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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
components/mention/style/index.tsx
Normal file
1
components/mention/style/index.tsx
Normal file
@ -0,0 +1 @@
|
|||||||
|
import './index.less';
|
@ -47,6 +47,7 @@
|
|||||||
"rc-collapse": "~1.6.3",
|
"rc-collapse": "~1.6.3",
|
||||||
"rc-dialog": "~6.1.1",
|
"rc-dialog": "~6.1.1",
|
||||||
"rc-dropdown": "~1.4.8",
|
"rc-dropdown": "~1.4.8",
|
||||||
|
"rc-editor-mention": "0.0.27",
|
||||||
"rc-form": "~0.17.1",
|
"rc-form": "~0.17.1",
|
||||||
"rc-input-number": "~2.5.12",
|
"rc-input-number": "~2.5.12",
|
||||||
"rc-menu": "~4.12.4",
|
"rc-menu": "~4.12.4",
|
||||||
|
Loading…
Reference in New Issue
Block a user