美文网首页
基于Draftjs实现的Electron富文本聊天输入框(一)

基于Draftjs实现的Electron富文本聊天输入框(一)

作者: charles0427 | 来源:发表于2018-02-11 15:24 被阅读261次

    @功能

    会话是群组时,输入框提供@功能,用户是当前群的群主时,suggestionsList包含@全体成员;

    @组件使用Draft plugins提供的mention plugin

    主要code:

    import Editor from 'draft-js-plugins-editor';
    import createMentionPlugin, {defaultSuggestionsFilter} from 'draft-js-mention-plugin';
    
    import createLinkifyPlugin from 'draft-js-linkify-plugin';
    import 'draft-js-mention-plugin/lib/plugin.css';
    import 'draft-js-linkify-plugin/lib/plugin.css';
    
    import MentionEntry from './Mention';
    
    // 自定义mention弹出的位置
    const positionSuggestions = ({decoratorRect, state, props}) => {
      const editorWrapper = document.getElementsByClassName('chat-draft-editor')[0];
      const wrapperRect = editorWrapper.getBoundingClientRect();
      let left = decoratorRect.left - wrapperRect.left;
      const newDecoratorRight = decoratorRect.left + 170;
      if (newDecoratorRight >= wrapperRect.right) {
        left = left - (newDecoratorRight - wrapperRect.right) - 10;
      }
      const bottom = wrapperRect.height + 25 - (decoratorRect.bottom - wrapperRect.top);
      console.log('editor', decoratorRect);
    
      let transform;
      let transition;
    
      if (state.isActive & props.suggestions.size > 0) {
        transform = 'scale(1)';
        transition = 'all 0.25s cubic-bezier(.3,1.2,.2,1)';
      } else if (state.isActive) {
        transform = 'scale(0)';
        transition = 'all 0.35s cubic-bezier(.3,1,.2,1)';
      }
    
      return {
        position: 'absolute',
        left: `${left}px`,
        bottom: `${bottom}px`,
        minWidth: '170px',
        maxHeight: '230px',
        overflowY: 'scroll',
        border: '1px solid #E6E6E6',
        borderRadius: '6px',
        transform,
        transformOrigin: '1em 0%',
        transition,
      };
    };
    const defaultSuggestions = [];
    
    export default class ChatDraftEditor extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          editorState: EditorState.createEmpty(),
          suggestions: [],
        };
        this.mentionPlugin = createMentionPlugin({
          defaultSuggestions,
          entityMutability: 'IMMUTABLE',
          positionSuggestions,
          mentionPrefix: '@',
        });
      }
    }
    
    onSearchChange = ({value}) => {
        const userList = this.props.userList;
        const filterSuggestions = value === ''? userList : defaultSuggestionsFilter(value, userList);
        this.setState({
          suggestions: filterSuggestions,
        });
      };
    
    render() {
        const {MentionSuggestions} = this.mentionPlugin;
        const plugins = [this.mentionPlugin];
        return (
          <div
            className="chat-draft-editor"
            onClick={()=> {
              this.focus(0);
            }}
          >
            <Editor
              ref={(e) => {
                this.editor = e;
              }}
              plugins={plugins}
              decorators={draftDecorator}
              editorState={this.state.editorState}
              onChange={this.onChange.bind(this)}
              onBlur={this.handleBlur.bind(this)}
              blockRendererFn={this.blockRendererFn.bind(this)}
              handleKeyCommand={this.handleKeyCommand.bind(this)}
              keyBindingFn={this.keyBindingFn}
            />
            <MentionSuggestions
              onSearchChange={this.onSearchChange.bind(this)}
              suggestions={this.state.suggestions}
              entryComponent={MentionEntry}
              // onOpen={this.handleMentionOpen.bind(this)}
              // onClose={this.handleMentionClose.bind(this)}
            />
          </div>
        );
      }
    
    1. <MetionSuggestions />是@列表组件,其属性suggestions为数组对象,格式可以自定义..如果格式自定义了的话,最好同时修改entryComponent

    2. entryComponent用来传入自定义的mention列表中item的组件样式,意味着可以根据你的mention对象格式定制组件样式:

      import React from 'react';
      import Avatar from '../common/Avatar';
      
      const MentionEntry = (props) => {
        const {
          mention,
          ...parentProps
        } = props;
        const isAll = mention.get('uid') == 'upcgroupatall' ? true: false;
        const avatarType = mention.get('gender') == '2'? 'women': 'men';
      
        const suggestionEntry = isAll? (
          <div className="mention-all">{mention.get('name')}</div>
        ): (
          <div className="mention-entry">
            <Avatar
              icon={mention.get('avatar')}
              shape="square"
              size="min"
              type={avatarType}
            />
            <div className="mention-text">{mention.get('name')}</div>
          </div>
        );
        return (
          <div {...parentProps}>
            <div className="mention-suggestion" >
              {suggestionEntry}
            </div>
          </div>
        );
      };
      
      module.exports = MentionEntry;
      
      
    3. onSearchChange绑定@时的关键字filter方法:

      onSearchChange = ({value}) => {
          const userList = this.props.userList;
          const filterSuggestions = value === ''? userList : defaultSuggestionsFilter(value, userList);
          this.setState({
            suggestions: filterSuggestions,
          });
        };
      

      这里原本按照官网demo写,会导致一个bug,即第一次触发@,value为''时,filter方法并未返回整个list。因此添加判断如上。

    4. mention插件目前仍有些未解决的影响体验的问题:会影响输入框up/down arrow事件的正常处理;mention选择时,滚轮不会随着up/down arrow滚动

    相关文章

      网友评论

          本文标题:基于Draftjs实现的Electron富文本聊天输入框(一)

          本文链接:https://www.haomeiwen.com/subject/qjnktftx.html