aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/pdf/Annotation.tsx
blob: b1d1d82930774089801dc86a6fafae26a19b2b28 (plain)
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import React = require("react");
import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import { Doc, DocListCast, Opt } from "../../../fields/Doc";
import { Id } from "../../../fields/FieldSymbols";
import { List } from "../../../fields/List";
import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
import { LinkManager } from "../../util/LinkManager";
import { undoBatch } from "../../util/UndoManager";
import { FieldViewProps } from "../nodes/FieldView";
import { AnchorMenu } from "./AnchorMenu";
import "./Annotation.scss";

interface IAnnotationProps extends FieldViewProps {
    anno: Doc;
    dataDoc: Doc;
    fieldKey: string;
    showInfo: (anno: Opt<Doc>) => void;
    pointerEvents?: string;
}
@observer
export
    class Annotation extends React.Component<IAnnotationProps> {
    render() {
        return DocListCast(this.props.anno.textInlineAnnotations).map(a => <RegionAnnotation pointerEvents={this.props.pointerEvents} {...this.props} document={a} key={a[Id]} />);
    }
}

interface IRegionAnnotationProps extends IAnnotationProps {
    document: Doc;
    pointerEvents?: string;
}
@observer
class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
    private _disposers: { [name: string]: IReactionDisposer } = {};
    private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();

    @observable _brushed: boolean = false;
    @computed get annoTextRegion() { return Cast(this.props.document.annoTextRegion, Doc, null) || this.props.document; }

    componentDidMount() {
        this._disposers.brush = reaction(
            () => this.annoTextRegion && Doc.isBrushedHighlightedDegree(this.annoTextRegion),
            brushed => brushed !== undefined && runInAction(() => this._brushed = brushed !== 0)
        );
    }

    componentWillUnmount() {
        Object.values(this._disposers).forEach(disposer => disposer?.());
    }

    @undoBatch
    deleteAnnotation = () => {
        const docAnnotations = DocListCast(this.props.dataDoc[this.props.fieldKey]);
        this.props.dataDoc[this.props.fieldKey] = new List<Doc>(docAnnotations.filter(a => a !== this.annoTextRegion));
        AnchorMenu.Instance.fadeOut(true);
        this.props.select(false);
    }

    @undoBatch
    pinToPres = () => this.props.pinToPres(this.annoTextRegion)

    @undoBatch
    makePushpin = () => this.annoTextRegion.isPushpin = !this.annoTextRegion.isPushpin

    isPushpin = () => BoolCast(this.annoTextRegion.isPushpin);

    @action
    onPointerDown = (e: React.PointerEvent) => {
        if (e.button === 2 || e.ctrlKey) {
            AnchorMenu.Instance.Status = "annotation";
            AnchorMenu.Instance.Delete = this.deleteAnnotation.bind(this);
            AnchorMenu.Instance.Pinned = false;
            AnchorMenu.Instance.AddTag = this.addTag.bind(this);
            AnchorMenu.Instance.PinToPres = this.pinToPres;
            AnchorMenu.Instance.MakePushpin = this.makePushpin;
            AnchorMenu.Instance.IsPushpin = this.isPushpin;
            AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
            e.stopPropagation();
        }
        else if (e.button === 0) {
            e.stopPropagation();
            LinkManager.FollowLink(undefined, this.annoTextRegion, this.props, false);
        }
    }

    addTag = (key: string, value: string): boolean => {
        const valNum = parseInt(value);
        this.annoTextRegion[key] = isNaN(valNum) ? value : valNum;
        return true;
    }

    render() {
        return (<div className="htmlAnnotation" onPointerEnter={() => this.props.showInfo(this.props.anno)} onPointerLeave={() => this.props.showInfo(undefined)} onPointerDown={this.onPointerDown} ref={this._mainCont}
            style={{
                left: NumCast(this.props.document.x),
                top: NumCast(this.props.document.y),
                width: NumCast(this.props.document._width),
                height: NumCast(this.props.document._height),
                opacity: this._brushed ? 0.5 : undefined,
                pointerEvents: this.props.pointerEvents as any,
                backgroundColor: this._brushed ? "orange" : StrCast(this.props.document.backgroundColor),
            }} >
        </div>);
    }
}