基于Draftjs实现的Electron富文本聊天输入框(一) —— 群@功能

@功能

会话是群组时,输入框提供@功能,用户是当前群的群主时,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滚动

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容