From 01fabac77a692cb1ca0da42db15471e60bdd3839 Mon Sep 17 00:00:00 2001 From: RaoHai Date: Mon, 4 Jul 2016 10:31:27 +0800 Subject: [PATCH] Mention Component --- components/index.tsx | 3 + components/mention/demo/async.md | 56 +++++++++++++++ components/mention/demo/avatar.md | 66 ++++++++++++++++++ components/mention/demo/basic.md | 30 ++++++++ components/mention/demo/customTag.md | 64 +++++++++++++++++ components/mention/demo/multilines.md | 31 +++++++++ components/mention/index.en-US.md | 42 ++++++++++++ components/mention/index.tsx | 97 ++++++++++++++++++++++++++ components/mention/index.zh-CN.md | 43 ++++++++++++ components/mention/style/index.less | 99 +++++++++++++++++++++++++++ components/mention/style/index.tsx | 1 + package.json | 1 + 12 files changed, 533 insertions(+) create mode 100644 components/mention/demo/async.md create mode 100644 components/mention/demo/avatar.md create mode 100644 components/mention/demo/basic.md create mode 100644 components/mention/demo/customTag.md create mode 100644 components/mention/demo/multilines.md create mode 100644 components/mention/index.en-US.md create mode 100644 components/mention/index.tsx create mode 100644 components/mention/index.zh-CN.md create mode 100644 components/mention/style/index.less create mode 100644 components/mention/style/index.tsx diff --git a/components/index.tsx b/components/index.tsx index 6ad621be9f..5723cca7f7 100644 --- a/components/index.tsx +++ b/components/index.tsx @@ -48,3 +48,6 @@ export { Timeline } import Tooltip from './tooltip'; export { Tooltip } + +import Mention from './mention'; +export { Mention }; diff --git a/components/mention/demo/async.md b/components/mention/demo/async.md new file mode 100644 index 0000000000..533be939ee --- /dev/null +++ b/components/mention/demo/async.md @@ -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 (); + }, +}); + +ReactDOM.render( + , + mountNode +); +```` diff --git a/components/mention/demo/avatar.md b/components/mention/demo/avatar.md new file mode 100644 index 0000000000..63160d21fb --- /dev/null +++ b/components/mention/demo/avatar.md @@ -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 => + ); + this.setState({ + suggestions, + }); + }, + render() { + const { suggestions, loading } = this.state; + return (); + }, +}); + +ReactDOM.render( + , + mountNode +); +```` diff --git a/components/mention/demo/basic.md b/components/mention/demo/basic.md new file mode 100644 index 0000000000..ae48cec6ea --- /dev/null +++ b/components/mention/demo/basic.md @@ -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( + , + mountNode +); +```` diff --git a/components/mention/demo/customTag.md b/components/mention/demo/customTag.md new file mode 100644 index 0000000000..de9efd0a48 --- /dev/null +++ b/components/mention/demo/customTag.md @@ -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 => + ); + this.setState({ + suggestions, + }); + }, + render() { + const { suggestions, loading } = this.state; + return (); + }, +}); + +ReactDOM.render( + , + mountNode +); +```` diff --git a/components/mention/demo/multilines.md b/components/mention/demo/multilines.md new file mode 100644 index 0000000000..7d82a2262f --- /dev/null +++ b/components/mention/demo/multilines.md @@ -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( + , + mountNode +); +```` diff --git a/components/mention/index.en-US.md b/components/mention/index.en-US.md new file mode 100644 index 0000000000..ddc397806f --- /dev/null +++ b/components/mention/index.en-US.md @@ -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 + , +``` + +## API + +### Mention props + +| Property | Description | Type | Default | +|----------|---------------|----------|--------------| +| suggestions | suggestion content | Array or Array | [] | +| 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 | {} | diff --git a/components/mention/index.tsx b/components/mention/index.tsx new file mode 100644 index 0000000000..369f180b92 --- /dev/null +++ b/components/mention/index.tsx @@ -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; + onSearchChange?: Function; + onChange?: Function; + notFoundContent?: any; + loading?: Boolean; + multiLines?: Boolean; +} + +export interface MentionState { + suggestions?: Array; + focus?: Boolean; +} + +export default class Mention extends React.Component { + 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 = ; + } + + return this.setState({focus: true})} + onBlur={() => this.setState({focus: false})} + suggestions={suggestions} + notFoundContent={notFoundContent} + />; + } +} diff --git a/components/mention/index.zh-CN.md b/components/mention/index.zh-CN.md new file mode 100644 index 0000000000..b42d6ac860 --- /dev/null +++ b/components/mention/index.zh-CN.md @@ -0,0 +1,43 @@ +--- +category: Components +chinese: 提及 +cols: 1 +type: Views +english: Mention +--- + +提及组件。(圈人组件) + +## 何时使用 + +用于在输入中提及某人或某事。 + +```html + , +``` + +## API + +### Mention props + +| 参数 | 说明 | 类型 | 默认值 | +|----------|---------------|----------|--------------| +| suggestions | 建议内容 | Array or Array | [] | +| 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 | {} | diff --git a/components/mention/style/index.less b/components/mention/style/index.less new file mode 100644 index 0000000000..7f40f38ac3 --- /dev/null +++ b/components/mention/style/index.less @@ -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; + } + } + } +} \ No newline at end of file diff --git a/components/mention/style/index.tsx b/components/mention/style/index.tsx new file mode 100644 index 0000000000..d74e52ee9f --- /dev/null +++ b/components/mention/style/index.tsx @@ -0,0 +1 @@ +import './index.less'; diff --git a/package.json b/package.json index 90e69dc9b7..aeb75af105 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "rc-collapse": "~1.6.3", "rc-dialog": "~6.1.1", "rc-dropdown": "~1.4.8", + "rc-editor-mention": "0.0.27", "rc-form": "~0.17.1", "rc-input-number": "~2.5.12", "rc-menu": "~4.12.4",