Skip to content
Snippets Groups Projects
CommentComponent.js 4.6 KiB
Newer Older
chris's avatar
chris committed
import React, {
  useContext,
  useState,
  useEffect,
  useMemo,
chris's avatar
chris committed
  Fragment,
  useCallback
chris's avatar
chris committed
} from "react";
import styled from "styled-components";
chris's avatar
chris committed
import { WaxContext } from "wax-prosemirror-core";
chris's avatar
chris committed
import { DocumentHelpers } from "wax-prosemirror-utilities";
chris's avatar
chris committed
import CommentsBoxList from "./CommentsBoxList";
chris's avatar
chris committed
import { each } from "lodash";
export default ({ area }) => {
chris's avatar
chris committed
  const { view: { main }, app, activeView } = useContext(WaxContext);
  const activeCommentPlugin = app.PmPlugins.get("activeComment");
chris's avatar
chris committed
  const [comments, setComments] = useState([]);
chris's avatar
chris committed
  const [position, setPosition] = useState();

  const setTops = useCallback(() => {
    const result = [];
    const boxes = [];
    let commentEl = null;
    let annotationTop = 0;
    let boxHeight = 0;
    let top = 0;
    const allCommentsTop = {};

    each(comments[area], (comment, pos) => {
      const WaxSurface = main.dom.getBoundingClientRect();
      const { attrs: { id } } = comment;
chris's avatar
chris committed
      const activeComment = activeCommentPlugin.getState(activeView.state)
        .comment;

chris's avatar
chris committed
      let isActive = false;
      if (activeComment && comment.attrs.id === activeComment.attrs.id)
        isActive = true;
chris's avatar
chris committed

      //annotation top
      if (area === "main") {
        commentEl = document.querySelector(`span[data-id="${id}"]`);
chris's avatar
chris committed
        if (commentEl)
          annotationTop =
            commentEl.getBoundingClientRect().top - WaxSurface.top;
chris's avatar
chris committed
      } else {
        const panelWrapper = document.getElementsByClassName("panelWrapper");
        const panelWrapperHeight = panelWrapper[0].getBoundingClientRect()
          .height;
        commentEl = document
          .querySelector("#notes-container")
          .querySelector(`span[data-id="${id}"]`);
chris's avatar
chris committed
        if (commentEl)
          annotationTop =
            commentEl.getBoundingClientRect().top - panelWrapperHeight - 50;
chris's avatar
chris committed
      }

      // get height of this comment box
      const boxEl = document.querySelector(`div[data-comment="comment-${id}"]`);
      if (boxEl) boxHeight = parseInt(boxEl.offsetHeight);

      // keep the elements to add the tops to at the end
      boxes.push(boxEl);

      // where the box should move to
      top = annotationTop;

      // if the above comment box has already taken up the height, move down
      if (pos > 0) {
        const previousBox = comments[area][pos - 1];
        const previousEndHeight = previousBox.endHeight;
        if (annotationTop < previousEndHeight) {
          top = previousEndHeight + 2;
        }
      }
      // store where the box ends to be aware of overlaps in the next box
      comment.endHeight = top + boxHeight + 2;
      result[pos] = top;

      // if active, move as many boxes above as needed to bring it to the annotation's height
      if (isActive) {
        comment.endHeight = annotationTop + boxHeight + 2;
        result[pos] = annotationTop;

        let b = true;
        let i = pos;

        // first one active, none above
chris's avatar
chris committed

        while (b) {
          const boxAbove = comments[area][i - 1];
          const boxAboveEnds = boxAbove.endHeight;
          const currentTop = result[i];

          const doesOverlap = boxAboveEnds > currentTop;
          if (doesOverlap) {
            const overlap = boxAboveEnds - currentTop;
            result[i - 1] -= overlap;
          }

          if (!doesOverlap) b = false;
          if (i <= 1) b = false;
          i -= 1;
        }
      }

      allCommentsTop[id] = result[pos];
chris's avatar
chris committed
    });
    console.log(allCommentsTop);
chris's avatar
chris committed
    return allCommentsTop;
  });

chris's avatar
chris committed
  useEffect(
    () => {
chris's avatar
chris committed
      setComments(updateComments(main));
chris's avatar
chris committed
      setPosition(setTops());
chris's avatar
chris committed
    [JSON.stringify(updateComments(main)), JSON.stringify(setTops())]
chris's avatar
chris committed
  );

  const CommentComponent = useMemo(
chris's avatar
chris committed
    () => (
      <CommentsBoxList
        comments={comments[area] || []}
        area={area}
        view={main}
chris's avatar
chris committed
        position={position}
chris's avatar
chris committed
    [comments[area] || [], position]
chris's avatar
chris committed
  );
  return <Fragment>{CommentComponent}</Fragment>;
chris's avatar
chris committed
const updateComments = view => {
chris's avatar
chris committed
  if (view) {
    const nodes = DocumentHelpers.findChildrenByMark(
      view.state.doc,
      view.state.schema.marks.comment,
      true
    );

    const allComments = nodes.map(node => {
      return node.node.marks.filter(comment => {
        return comment.type.name === "comment";
      });
    });

    const groupedComments = {};
    allComments.forEach(comment => {
      if (!groupedComments[comment[0].attrs.group]) {
        groupedComments[comment[0].attrs.group] = [comment[0]];
      } else {
        groupedComments[comment[0].attrs.group].push(comment[0]);
      }
    });
    return groupedComments;
chris's avatar
chris committed
  }
  return [];
};