Перекрытие встроенных аннотаций с помощью Quill

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

This [abc]is a set of [xyz]overlapping[/xyz] comments[/abc]

Я определяю свое пятно привязки комментария следующим образом:

let Inline = Quill.import('blots/inline');

class CommentAnchorBlot extends Inline  {
  static create(commentId) {
    let node = super.create();
    node.setAttribute("class", "comment-anchor comment-anchor-" + commentId);
    return node;
  }

  static formats(node) {
    var classNames = node.getAttribute('class').split(/\s+/);
    for (var i = 0, len = classNames.length; i < len; i++) {
      var className = classNames[i];
      if (className.indexOf("comment-anchor-") === 0) {
        return className.replace("comment-anchor-", "");
      }
    }
    return null;
  }
}
CommentAnchorBlot.blotName = 'comment';
CommentAnchorBlot.className = 'comment-anchor';
CommentAnchorBlot.tagName = 'span';
Quill.register(CommentAnchorBlot);

Но когда я проверяю его на работающем экземпляре Quill, он генерирует такой пергамент:

{
  "ops" : [
    { "insert" : "This " },
    { "insert" : "is a set of ", "attributes" : { "comment" : "abc" } },
    { "insert" : "overlapping ", "attributes" : { "comment" : "xyz" } },
    { "insert" : " comments", "attributes" : { "comment" : "abc" } }
  ]
}

Что проблематично, потому что слово «перекрывающееся» должно фактически иметь как «abc», так и «xyz» в качестве идентификаторов привязки комментариев.

Как бы вы рекомендовали изменить определение CommentAnchorBlot, чтобы учесть это требование? Я не видел других примеров в документации Quill, которые бы работали так же.


person benjismith    schedule 27.07.2017    source источник


Ответы (1)


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

Во-вторых, к вашему сведению, большую часть времени форматирование представляет собой поведение перезаписи (форматирование цвета текста до красного, а затем синего, делает его синим, а не [красным, синим]), но вы можете изменить это примерно так:

class Comment extends Parchment.Attributor.Class {
  constructor(attrName = 'comment', keyName = 'comment') {
    super(attrName, keyName, { scope: Parchment.Scope.INLINE_ATTRIBUTE });
  }

  add(node, value) {
    if (!this.canAdd(node, value)) return false;
    const array = Array.isArray(value) ? value : [value];
    array.forEach((id) => {
      node.classList.add(`${this.keyName}-${id}`);
    });
    return true;
  }

  remove(node, id) {
    if (id == null) {
      super.remove(node);
    } else {
      node.classList.remove(`${this.keyName}-${id}`);
      if (node.classList.length === 0) {
        node.removeAttribute('class');
      }
    }
  }

  value(node) {
    const prefix = `${this.keyName}-`;
    const list = _.filter(node.classList, (c) => {
      return c.startsWith(prefix);
    }).map((c) => {
      return c.slice(prefix.length);
    });
    return (list.length > 0) ? list : null;
  }
}

Quill.register({ 'formats/comment': new Comment() });
person jhchen    schedule 31.07.2017
comment
Ницца! Это выглядит очень многообещающе... Однако, когда я использую этот код, класс идентификатора комментария добавляется в родительский блок (абзац), а не просто оборачивает выделенный в данный момент текст в диапазон, который я предпочитаю... Делает это имеет смысл, или мне нужно собрать пример кода, чтобы воспроизвести проблему? - person benjismith; 01.08.2017
comment
На самом деле, я думаю, что просто опечатался в области видимости. Это должен быть INLINE_ATTRIBUTE, а не INLINE_ATTRIBUTOR. Проверить, работает ли это? Я отредактирую код в посте. - person jhchen; 01.08.2017
comment
Идеальный. Теперь работает как часы... Еще раз спасибо за вашу помощь! Я очень ценю это. - person benjismith; 01.08.2017
comment
У меня небольшой уточняющий вопрос... Как удалить один комментарий с помощью этой схемы? Прямо сейчас я звоню editor.formatText(index, size, { "comment" : false }), но это удаляет все комментарии из указанного диапазона, а не только конкретный комментарий... - person benjismith; 03.08.2017
comment
Я не могу понять, как вызвать функцию remove(node, id) для удаления только одного идентификатора комментария. Можете пояснить, как это работает? - person benjismith; 05.08.2017
comment
Если вы хотите пройти через API верхнего уровня, например formatText, то один из способов — использовать префикс, например «-», для обозначения удаления и обработки случая в add. Вот как работает formatText(0, 1, 'indent', '+1') (код в formats/indent.js). В качестве альтернативы, если вы сохраните атрибут комментария перед переходом в register, вы можете напрямую использовать remove. Наблюдатель мутаций Quill по-прежнему должен обнаруживать разницу, правильно обновлять и генерировать событие изменения текста. - person jhchen; 07.08.2017