Skip to content
Snippets Groups Projects
CommentPlugin.js 2.25 KiB
Newer Older
chris's avatar
chris committed
import { minBy, maxBy, last } from 'lodash';
import { Plugin, PluginKey } from 'prosemirror-state';
import { Decoration, DecorationSet } from 'prosemirror-view';
import { DocumentHelpers } from 'wax-prosemirror-utilities';
chris's avatar
chris committed
const commentPlugin = new PluginKey('commentPlugin');

const getComment = state => {
chris's avatar
chris committed
  const commentMark = state.schema.marks.comment;
  const commentOnSelection = DocumentHelpers.findFragmentedMark(state, commentMark);

  // Don't allow Active comment if selection is not collapsed
  if (
    state.selection.from !== state.selection.to &&
    commentOnSelection &&
    commentOnSelection.attrs.conversation.length
  ) {
    return;
  }
chris's avatar
chris committed

chris's avatar
chris committed
  if (commentOnSelection) {
    const commentNodes = DocumentHelpers.findChildrenByMark(state.doc, commentMark, true);
chris's avatar
chris committed
    const allCommentsWithSameId = [];
chris's avatar
chris committed
    commentNodes.map(node => {
chris's avatar
chris committed
      node.node.marks.filter(mark => {
        if (mark.type.name === 'comment' && commentOnSelection.attrs.id === mark.attrs.id) {
chris's avatar
chris committed
          allCommentsWithSameId.push(node);
chris's avatar
chris committed
        }
      });
    });
chris's avatar
chris committed

chris's avatar
chris committed
    if (allCommentsWithSameId.length > 1) {
chris's avatar
chris committed
      const minPos = minBy(allCommentsWithSameId, 'pos');
      const maxPos = maxBy(allCommentsWithSameId, 'pos');
chris's avatar
chris committed

      return {
chris's avatar
chris committed
        from: minPos.pos,
        to: maxPos.pos + last(allCommentsWithSameId).node.nodeSize,
chris's avatar
chris committed
        attrs: commentOnSelection.attrs,
chris's avatar
chris committed
        contained: commentOnSelection.contained,
chris's avatar
chris committed
      };
    }
  }
  return commentOnSelection;
chris's avatar
chris committed

export default props => {
  return new Plugin({
    key: commentPlugin,
    state: {
      init: (_, state) => {
        return { comment: getComment(state) };
      },
      apply(tr, prev, _, newState) {
        const comment = getComment(newState);
        let createDecoration;
        if (comment) {
          createDecoration = DecorationSet.create(newState.doc, [
            Decoration.inline(comment.from, comment.to, {
chris's avatar
chris committed
              class: 'active-comment',
            }),
chris's avatar
chris committed
          createDecoration,
chris's avatar
chris committed
      },
chris's avatar
chris committed
    props: {
      decorations: state => {
        const commentPluginState = state && commentPlugin.getState(state);
        return commentPluginState.createDecoration;
chris's avatar
chris committed
      },
      setCommentActive: state => {},
    },
chris's avatar
chris committed
  });
};