Как автоматически расширить выделение на весь связанный текст в Draft.js?

Я разрабатываю редактор форматированного текста с помощью Draft.js (и это здорово!). Следующий код, который позволяет пользователю редактировать ссылку, логически работает нормально, но меня не устраивает пользовательский интерфейс.

Если пользователь выбирает часть ссылки и запускает этот код, этот код разделяет эту ссылку на несколько ссылок, что НЕ нужно пользователю.

Например, если этап «купить эту книгу» связан с URL-A, а пользователь выбирает «купить эту» и меняет его на URL-B, эта часть будет связана с URL-B, но «книга» по-прежнему связан с URL-A.

В идеале, когда пользователь выбирает часть связанного текста, я хотел бы автоматически расширить выделение на всю ссылку, а затем выполнить этот код.

Я, однако, не могу понять, как это сделать (разверните выделение на всю ссылку).

editLink = () => {
    const { editorState } = this.state;
    const selection = editorState.getSelection();
    if (selection.isCollapsed()) {
      return;
    }

    let url = ''; // default
    const content = editorState.getCurrentContent();
    const startKey = selection.getStartKey();
    const startOffset = selection.getStartOffset();
    const blockWithLinkAtBeginning = content.getBlockForKey(startKey);
    const linkKey = blockWithLinkAtBeginning.getEntityAt(startOffset);
    if (linkKey) {
      const linkInstance = content.getEntity(linkKey);
      url = linkInstance.getData().url;
    }

    let link = window.prompt("Paste the link", url);
    if (!link) {
      console.log("removing link");
      const newEditorState = RichUtils.toggleLink(editorState, selection, null);
      this.setState({ editorState: newEditorState });
      return;
    }
    console.log("adding a link", link);
    const contentWithEntity = content.createEntity('LINK', 'MUTABLE', { url: link });
    const entityKey = contentWithEntity.getLastCreatedEntityKey();
    const newEditorState = EditorState.set(editorState, { currentContent: contentWithEntity });
    const yetNewEditorState = RichUtils.toggleLink(newEditorState, newEditorState.getSelection(), entityKey);

    this.setState({ editorState: yetNewEditorState} );
  }

Буду очень признателен за любую помощь или предложения.


person Satoshi Nakajima    schedule 25.06.2019    source источник
comment
stackoverflow.com/ вопросы/47123963/   -  person Jiang YD    schedule 22.07.2019


Ответы (1)


Есть два способа сделать это. Первое, вероятно, то, что вы пытаетесь применить - применить новую ссылку поверх текущей ссылки, тем самым переопределив ее. Это не лучший способ сделать это, но это можно сделать.

Второй проще. В объекте ContentState есть метод replaceEntityData(). Таким образом, вы можете реализовать это следующим образом:

editLink = () => {
    const { editorState } = this.state;
    const selection = editorState.getSelection();
    if (selection.isCollapsed()) {
      return;
    }

    let url = ''; // default
    const content = editorState.getCurrentContent();
    const startKey = selection.getStartKey();
    const startOffset = selection.getStartOffset();
    const block = content.getBlockForKey(startKey);
    const linkKey = block.getEntityAt(startOffset);

    let link = window.prompt("Paste the link", url);
    if (!link) { //REMOVING LINK
        var contentWithRemovedLink = content;
        block.findEntityRanges(charData => { //You need to use block.findEntityRanges() API to get the whole range of link

            const entityKey = charData.getEntity();
            if (!entityKey) return false;
            return entityKey === linkKey //Need to return TRUE only for your specific link. 
        }, (start, end) => {
                const entitySelection = new SelectionState({
                    anchorKey: block.getKey(),  //You already have the block key
                    focusKey: block.getKey(),
                    anchorOffset: start,   //Just use the start/end provided by the API
                    focusOffset: end })
                contentWithRemovedLink = Modifier.applyEntity(content, entitySelection, null)

            return;
        })

        const newEditorState = EditorState.set(
            editorState, { currentContent: contentWithRemovedLink });
        return;
    }
    console.log("adding a link", link);

    //CHANGING LINK
    const contentWithUpdatedLink = content.replaceEntityData(linkKey, { url: link });
    const newEditorState = EditorState.set(editorState, { currentContent: contentWithUpdatedLink });
    //Now do as you please.
  }

ЧТОБЫ УДАЛИТЬ ССЫЛКУ:

В ContentBlock API есть метод findEntityRanges(). Эта функция принимает два параметра:

  1. (char: CharacterMetadata) => boolean: функция фильтра для объекта characterMetadata (каждая непрерывная комбинация ENTITY + INLINE_STYLE имеет уникальный объект CharacterMetatdata. Вы можете добраться до объекта через characterMetadata.getEntity() оттуда.). Если эта функция выполняется как TRUE, выполняется (2).
  2. (start: number, end: number) => void. Это дает вам доступ к начальному и конечному смещениям для каждого конкретного диапазона символов, для которого выполняется TRUE. Теперь вы можете делать все, что хотите, с началом и концом.

После этого вы можете применить объект NULL с новым SelectionState, который охватывает всю ссылку. Это удаляет объект ссылки.

ЧТОБЫ ИЗМЕНИТЬ ССЫЛКУ:

У вас уже есть linkKey. Просто вызовите content.replaceEntityData(linkKey, {url: "MY NEW URL"}), чтобы сгенерировать новый ContentState с новым URL. API определен здесь: https://draftjs.org/docs/api-reference-content-state#replaceentitydata

person jf1234    schedule 25.07.2019