File size: 2,750 Bytes
3d7d9b5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import React, { useState } from "react";
import { AnnotationPath } from "../types";
import { useAppStore } from "../store";

export const AnnotationNode = ({ ann }: { ann: AnnotationPath }) => {
  const {
    zoom,
    selectedNodeIds,
    setSelectedNodeIds,
    updateSelectedNodes,
    isAnnotationMode,
    isClickThrough,
    annotations,
    images,
    textNotes,
  } = useAppStore();
  const [isDragging, setIsDragging] = useState(false);
  const isSelected = selectedNodeIds.includes(ann.id);

  const handlePointerDown = (e: React.PointerEvent) => {
    if (isAnnotationMode || isClickThrough) return;

    if (!isSelected) {
      let idsToSelect = [ann.id];
      if (ann.groupId) {
        const groupAnnotations = annotations
          .filter((a) => a.groupId === ann.groupId)
          .map((a) => a.id);
        const groupImages = images
          .filter((i) => i.groupId === ann.groupId)
          .map((i) => i.id);
        const groupNotes = textNotes
          .filter((n) => n.groupId === ann.groupId)
          .map((n) => n.id);
        idsToSelect = [...groupAnnotations, ...groupImages, ...groupNotes];
      }

      if (e.shiftKey) {
        setSelectedNodeIds(
          Array.from(new Set([...selectedNodeIds, ...idsToSelect])),
        );
      } else {
        setSelectedNodeIds(idsToSelect);
      }
    }

    setIsDragging(true);
    e.currentTarget.setPointerCapture(e.pointerId);
  };

  const handlePointerMove = (e: React.PointerEvent) => {
    if (isDragging) {
      e.stopPropagation();
      updateSelectedNodes(e.movementX / zoom, e.movementY / zoom, ann.id);
    }
  };

  const handlePointerUp = (e: React.PointerEvent) => {
    if (isDragging) {
      setIsDragging(false);
      e.currentTarget.releasePointerCapture(e.pointerId);
      e.stopPropagation();
    }
  };

  return (
    <polyline

      points={ann.points.map((p) => `${p.x},${p.y}`).join(" ")}

      fill="none"

      stroke={isSelected ? "#0A84FF" : ann.color}

      strokeWidth={

        ann.strokeWidth * zoom * (ann.isHighlighter ? 3 : 1) +

        (isSelected ? 2 : 0)

      }

      strokeLinecap={ann.isHighlighter ? "square" : "round"}

      strokeLinejoin={ann.isHighlighter ? "miter" : "round"}

      opacity={ann.isHighlighter ? 0.4 : 1}

      style={{

        ...(ann.isHighlighter ? { mixBlendMode: "screen" } : {}),

        pointerEvents: isAnnotationMode || isClickThrough ? "none" : "stroke",

        cursor: "default",

      }}

      onPointerDown={handlePointerDown}

      onPointerMove={handlePointerMove}

      onPointerUp={handlePointerUp}

      onDoubleClick={(e) => {

        e.stopPropagation();

      }}

    />
  );
};