Как автоматически расширить выделение на весь связанный текст в Draft.js? - PullRequest
0 голосов
/ 26 июня 2019

Я разрабатываю текстовый редактор с 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} );
  }

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

1 Ответ

0 голосов
/ 25 июля 2019

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

Второй проще. В объекте 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

...