export function layoutComments()

in frontend/src/js/components/viewer/CommentPanel/CommentPanel.tsx [79:142]


export function layoutComments(groups: CommentGroup[], margin: number, rootCommentId?: string): { [commentId: string]: number } {
  if(groups.length === 0) {
    return {};
  }

  // If a comment has been focused (ie selected by the user or the last one they have previously selected) then we want
  // it to be next to the line it is attached to. Find the group that contains the focused comment.
  const root = groups.find(({ comments }) => comments.some(({ id }) => id === rootCommentId));
  const modifiedGroups = ([] as CommentGroup[]);

  let ixRoot: number | undefined = undefined;
  let offset = groups[0].top;

  // Lay out the comment groups in order
  groups.forEach((group, ix) => {
    const modifiedGroup = { ...group };

    if(root !== undefined && group.top === root.top) {
      ixRoot = ix;
      
      // Pull up the root group (if needed) so the focused comment is in the center
      const beforeRootInGroup = takeWhile(root.comments, ({ id }) => id !== rootCommentId);
      const offsetWithinGroup = sumBy(beforeRootInGroup, 'height') + (margin * beforeRootInGroup.length);

      modifiedGroup.top -= offsetWithinGroup;
    } else if(group.top < offset) {
      // Push down the group so that it doesn't overlap with the previous group
      modifiedGroup.top = offset;
    }

    modifiedGroups.push(modifiedGroup);
    offset = (modifiedGroup.top + modifiedGroup.height) + margin;
  });

  if(ixRoot !== undefined) {
    // Pull up any groups above the now relocated root group
    let offset = modifiedGroups[ixRoot].top;

    for(let i = (ixRoot - 1); i >= 0; i--) {
      const group = modifiedGroups[i];
      const overlap = (group.top + group.height) - offset;

      if(overlap > 0) {
        group.top -= overlap;
      }

      offset = group.top;
    }
  }

  // Collapse each group into the individual comments and their positions
  const ret = ({} as { [commentId: string]: number });

  for(const group of modifiedGroups) {
    let offset = 0;
    
    for(const comment of group.comments) {
      ret[comment.id] = group.top + offset;
      offset += comment.height + margin;
    }
  }

  return ret;
}