How to programatically add mentions using draft-js-mention-plugin?

北慕城南 提交于 2020-08-05 09:44:23

问题


The problem:

I'm trying to create an edit interface for contents created with draft-js + draft-js-mention-plugin. However, editorState wasn't persisted, only plain text. Mentions were saved as an array of objects. Now I need to recreate the editorState with that data.


Example:

I have a plain text like this:

const content = '@marcello we need to add spell check'

And a mentions array with objects like this:

const mentions = [{
  length: 8,
  offset: 0,
  user: 'user:59441f5c37b1e209c300547d',
}]

To create the editorState with the plain text I'm using these lines:

const contentState = ContentState.createFromText(content)
EditorState.createWithContent(contentState)

That works well. The plain text is set as initial state, but without mentions.

Now I need a way to add mentions based on mentions objects.

I'm trying to read the library code to figure it out, but without success so far.


回答1:


How i "hacked" my solution for it:

// Imports
import { EditorState,convertToRaw, ContentState, convertFromRaw, genKey, ContentBlock  } from 'draft-js';
// Init some kind of block with a mention
let exampleState = {
  blocks: [
        {
          key: genKey(), //Use the genKey function from draft
          text: 'Some text with mention',
          type: 'unstyled',
          inlineStyleRanges: [],
          data: {},
          depth: 0,
          entityRanges: [
            { offset: 15, length: 7, key: 0 }
          ]
        }
  ],
  entityMap: [
    "0": {
      "type": "mention",
      "mutability": "SEGMENTED",
      "data": {
        "mention": {
          "name": "<name>",
          "link": "<link>",
          "avatar": "<avatar-url>"
        }
      }
    }
  ]
};
this.state.editorState = EditorState.createWithContent(convertFromRaw(exampleState));

Here you can can create some function to input your text and output a entityRange return a offset/length of the mention and replace the "entityRanges" array with your highlighted stuff!

In this example the word "mention" would get highlighted with whatever styling your using with the mention plugin

Sidenote:

You could use the ContentBlock class from draft or create your own implementation to make this more pretty




回答2:


This is the solution I managed to put up for adding a mention (#) (with entityMap, to a new block at the end of state). It can be retreived as a mention and so on... Of course that can be simplified, but it works as expected for me.

 // import {....} from 'draft-js';
 import Immutable, {List, Repeat} from 'immutable' ;

  const addMentionLast = (editorState, mentionData) => {
   
    if(!mentionData.id) return;

    // debugger;
    const contentState = editorState.getCurrentContent();
    const oldBlockMap = contentState.getBlockMap();
    const lastKey = lastNonEmptyKey(contentState);
    const charData = CharacterMetadata.create();
    
    //new state with mention
    const selection = editorState.getSelection();
    const entityKey = Entity.create('#mention', 'SEGMENTED', {"mention":{...mentionData }} );
    //add text 
    const textWithEntity = Modifier.insertText(contentState, selection , `#${mentionData.name}` , null,  entityKey); 
    const _editorState = EditorState.push(editorState,  textWithEntity ,  'insert-characters');
    
    //create new block
    const _newBlock = new ContentBlock({
      key:  genKey(),
      type: 'unstyled',
      depth: 0,
      text: mentionData.name,
      characterList: List(Repeat(charData, mentionData.name.length)),
    });

    //set the entity
    const __newBlock =  applyEntityToContentBlock(_newBlock,0, mentionData.name.length, entityKey)

    //set new block in order..
    const blocksMap =
      Immutable.OrderedMap().withMutations(map => {
        if (lastKey) {
          //after the last non empty:
          for (let [k, v] of oldBlockMap.entries()) {
            map.set(k, v);
            if (lastKey === k) {
              map.set(k, v);
              map.set(__newBlock.key, __newBlock);
            }
          }
        }
        else {
          // first line:
          map.set(__newBlock.key, __newBlock);
        }
      });
   
    return EditorState.push(
      _editorState,
          ContentState
            .createFromBlockArray(Array.from(blocksMap.values()))
            .set('selectionBefore', contentState.getSelectionBefore())
            .set('selectionAfter', contentState.getSelectionAfter())
    )

  }

  function lastNonEmptyKey (content){
    const lastNonEmpty = content.getBlockMap().reverse().skipUntil((block, _) => block.getLength()).first();
 if (lastNonEmpty) return lastNonEmpty.getKey();
}

Thanks to all for sharing!



来源:https://stackoverflow.com/questions/47316926/how-to-programatically-add-mentions-using-draft-js-mention-plugin

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!