Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Draft.js - add link without text selected

Tags:

draftjs

How to add a link? I know how to add link to selection

              const contentState = editorState.getCurrentContent();
              const contentStateWithEntity = contentState.createEntity(
                'LINK',
                'MUTABLE',
                {url: urlValue}
              );
              const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
              const newEditorState = EditorState.set(editorState, { currentContent: contentStateWithEntity });
              this.setState({
                editorState: RichUtils.toggleLink(
                  newEditorState,
                  newEditorState.getSelection(),
                  entityKey
                )}

We get selection newEditorState.getSelection() and add link.

But how to add link without selection? Just add new a tag with text if text is not selected? It doesn't add anything if I doesn't select any text.

like image 888
Nick Avatar asked Nov 02 '17 21:11

Nick


2 Answers

My really concise solution. With cool annotation

insertLink = (type, data, text) => {
    const editorState = this.state.editorState;
    const contentState = editorState.getCurrentContent();
    const selection = editorState.getSelection();
    const textWithSpace = text.concat(' ');
    // create new content with text
    const newContent = Modifier.insertText(
      contentState,
      selection,
      textWithSpace,
    );
    // create new link entity
    const newContentWithEntity = newContent.createEntity(
      type,
      'MUTABLE',
      data,
      false,
    );
    const entityKey = newContentWithEntity.getLastCreatedEntityKey();
    // create new selection with the inserted text
    const anchorOffset = selection.getAnchorOffset();
    const newSelection = new SelectionState({
      anchorKey: selection.getAnchorKey(),
      anchorOffset,
      focusKey: selection.getAnchorKey(),
      focusOffset: anchorOffset + text.length,
    });
    // and aply link entity to the inserted text
    const newContentWithLink = Modifier.applyEntity(
      newContentWithEntity,
      newSelection,
      entityKey,
    );
    // create new state with link text
    const withLinkText = EditorState.push(
      editorState,
      newContentWithLink,
      'insert-characters',
    );
    // now lets add cursor right after the inserted link
    const withProperCursor = EditorState.forceSelection(
      withLinkText,
      newContent.getSelectionAfter(),
    );
    // update the editor with all changes
    this.setState({editorState: withProperCursor });
  };
like image 93
Pavel Poberezhnyi Avatar answered Nov 06 '22 02:11

Pavel Poberezhnyi


You can do any operations with the editor state without the component updating. So you can add a text that you need, after that programmatically set selection on this text and finally create a link and update your component.

Look at this working example - https://jsfiddle.net/levsha/2op5cyxm/ Here I add a link after a button click.

handleAddLink = () => {
  const { editorState } = this.state;
  const selectionState = editorState.getSelection();
  const contentState = editorState.getCurrentContent();
  const currentBlock = contentState.getBlockForKey(selectionState.getStartKey());
  const currentBlockKey = currentBlock.getKey();
  const blockMap = contentState.getBlockMap();
  const blocksBefore = blockMap.toSeq().takeUntil((v) => (v === currentBlock));
  const blocksAfter = blockMap.toSeq().skipUntil((v) => (v === currentBlock)).rest();
  const newBlockKey = genKey();

  // add new ContentBlock to editor state with appropriate text

  const newBlock = new ContentBlock({
    key: newBlockKey,
    type: 'unstyled',
    text: linkText,
    characterList: new List(Repeat(CharacterMetadata.create(), linkText.length)),
  });

  const newBlockMap = blocksBefore.concat(
    [[currentBlockKey, currentBlock], [newBlockKey, newBlock]],
    blocksAfter
  ).toOrderedMap();

  const selection = editorState.getSelection();

  const newContent = contentState.merge({
    blockMap: newBlockMap,
    selectionBefore: selection,
    selectionAfter: selection.merge({
      anchorKey: newBlockKey,
      anchorOffset: 0,
      focusKey: newBlockKey,
      focusOffset: 0,
      isBackward: false,
    }),
  });

  let newEditorState = EditorState.push(editorState, newContent, 'split-block');

  // programmatically apply selection on this text 

  const newSelection = new SelectionState({
    anchorKey: newBlockKey,
    anchorOffset: 0,
    focusKey: newBlockKey,
    focusOffset: linkText.length
  });

  newEditorState = EditorState.forceSelection(newEditorState, newSelection);

  // create link entity

  const newContentState = newEditorState.getCurrentContent();

  const contentStateWithEntity = newContentState.createEntity(
    'LINK',
    'IMMUTABLE',
    { url: linkUrl }
  );

  const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
  newEditorState = EditorState.set(newEditorState, { currentContent: contentStateWithEntity });

  newEditorState = RichUtils.toggleLink(newEditorState, newEditorState.getSelection(), entityKey);

  // reset selection

  newSelection = new SelectionState({
    anchorKey: newBlockKey,
    anchorOffset: linkText.length,
    focusKey: newBlockKey,
    focusOffset: linkText.length
  });

  newEditorState = EditorState.forceSelection(newEditorState, newSelection);

  // update our component

  this._handleChange(newEditorState);
}
like image 6
Mikhail Shabrikov Avatar answered Nov 06 '22 01:11

Mikhail Shabrikov