Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DraftJs: Replace an entity using its entity key

I'm creating a rich text editor using draftjs and can't find any ressources to help me in my problem.

Please first have look to the codesandbox.

You can see a text that contains a link (testtest in red). If you click on it you will see some infos of the link in the table:

| 🔗 link src         | http://localhost:8080/testtest |
| 📝 link text        | testtest                       |
| 🔑 link Entity key  | ab5a7c6d...                    |

I get the current link key (🔑) thanks to my getCurrentLinkKey helper:

const getCurrentLinkKey = (
  editorState: EditorState,
  contentState?: ContentState
): string => {
  if (contentState === undefined) {
    contentState = editorState.getCurrentContent();
  }

  const startKey = editorState.getSelection().getStartKey();
  const startOffset = editorState.getSelection().getStartOffset();
  const blockWithLinkAtBeginning = contentState.getBlockForKey(startKey);

  return blockWithLinkAtBeginning.getEntityAt(startOffset);
};

Then with this key I can get the link Entity using getCurrentLinkEntity helper:

const getCurrentLinkEntity = (
  editorState: EditorState
): EntityInstance | null => {
  const contentState = editorState.getCurrentContent();
  const linkKey = getCurrentLinkKey(editorState, contentState);

  if (linkKey) {
    return contentState.getEntity(linkKey);
  }

  return null;
};

Using the link Entity I can finally get the src and text value:

getCurrentLinkEntity(editorState).getData().url   // 🔗
getCurrentLinkEntity(editorState).getData().text  // 📝

You can see at the bottom a button Insert link. If you select the entire link testtest and click on this button, the link will be replaced.

This feature is handled by the insertLink helper:

const insertLink = (
  link: string,
  text: string,
  editorState: EditorState,
  setEditorState: (editorState: EditorState) => void
): void => {
  const contentStateWithEntity = editorState
    .getCurrentContent()
    .createEntity("LINK", "MUTABLE", { url: link, text });

  const entityKey = contentStateWithEntity.getLastCreatedEntityKey();

  const contentState = Modifier.replaceText(
    editorState.getCurrentContent(),
    editorState.getSelection(),
    text,
    editorState.getCurrentInlineStyle(),
    entityKey
  );

  const newEditorState = EditorState.set(editorState, {
    currentContent: contentStateWithEntity
  });
  const newEditorStateWithLink = RichUtils.toggleLink(
    newEditorState,
    newEditorState.getSelection(),
    entityKey
  );

  setEditorState(
    EditorState.push(newEditorStateWithLink, contentState, "insert-characters")
  );
};

But this function will just replace your selected text by the google link. What I want is, if you are on a link and click the button, then the entire link should be updated. So I created the replaceLink helper:

const replaceLink = (
  link: string,
  text: string,
  editorState: EditorState,
  setEditorState: (editorState: EditorState) => void
): void => {
  const contentStateWithEntity = editorState
    .getCurrentContent()
    .createEntity("LINK", "MUTABLE", { url: link, text });

  const entityKey = contentStateWithEntity.getLastCreatedEntityKey();

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

  const contentState = newEditorState
    .getCurrentContent()
    .replaceEntityData(getCurrentLinkKey(editorState), { entityKey });

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

  setEditorState(
    EditorState.push(newEditorStateWithLink, contentState, "insert-characters")
  );
};

But sadly, if I click on the Replace link button (that trigger replaceLink helper), the link is not updated but src and text are empty:

| 🔗 link src         |             |
| 📝 link text        |             |
| 🔑 link Entity key  | a1e34047... |

So as someone an idea how I can replace the link Entity using it's entity key?

like image 352
johannchopin Avatar asked Nov 15 '22 06:11

johannchopin


1 Answers

Well I come with an answer that use the draftjs-utils package and replace a link but without using its entity key:

This replaceLink helper was inspired by the work from the react-draft-wysiwyg library:

export const replaceLink = (
  link: string,
  text: string,
  editorState: EditorState,
  setEditorState: (editorState: EditorState) => void
): void => {
  const currentContent = editorState.getCurrentContent();

  // Create new link entity
  const contentStateWithEntity = currentContent.createEntity(
    "LINK",
    "MUTABLE",
    { url: link, text }
  );

  // Get created link entity's key
  const entityKey = contentStateWithEntity.getLastCreatedEntityKey();

  let selection = editorState.getSelection();

  const entityRange = getEntityRange(
    editorState,
    getSelectionEntity(editorState)
  );

  const isBackward = selection.getIsBackward();

  if (isBackward) {
    selection = selection.merge({
      anchorOffset: entityRange.end,
      focusOffset: entityRange.start
    });
  } else {
    selection = selection.merge({
      anchorOffset: entityRange.start,
      focusOffset: entityRange.end
    });
  }

  const updatedEditorWithText = Modifier.replaceText(
    currentContent,
    selection,
    text,
    editorState.getCurrentInlineStyle(),
    entityKey
  );

  const newEditorState = EditorState.push(
    editorState,
    updatedEditorWithText,
    "insert-characters"
  );

  setEditorState(
    EditorState.push(newEditorState, updatedEditorWithText, "insert-characters")
  );
};

Check the codesandbox to see it live.

like image 180
johannchopin Avatar answered Dec 19 '22 14:12

johannchopin