aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/pdf/Annotation.tsx
blob: db6b1f011c549ce7aabc6e420d067747da390737 (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
107
108
109
110
111
112
113
114
115
116
117
import React = require('react');
import { action, computed } 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, DocCast, NumCast, StrCast } from '../../../fields/Types';
import { LinkFollower } from '../../util/LinkFollower';
import { undoBatch } from '../../util/UndoManager';
import { OpenWhere } from '../nodes/DocumentView';
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?: () => Opt<string>;
}
@observer
export class Annotation extends React.Component<IAnnotationProps> {
    render() {
        return (
            <div style={{ display: this.props.anno.textCopied && !Doc.isBrushedHighlightedDegree(this.props.anno) ? 'none' : undefined }}>
                {DocListCast(this.props.anno.textInlineAnnotations).map(a => (
                    <RegionAnnotation pointerEvents={this.props.pointerEvents} {...this.props} document={a} key={a[Id]} />
                ))}
            </div>
        );
    }
}

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

    @computed get annoTextRegion() {
        return Cast(this.props.document.annoTextRegion, Doc, null) || this.props.document;
    }

    @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
    makeTargretToggle = () => (this.annoTextRegion.followLinkToggle = !this.annoTextRegion.followLinkToggle);

    isTargetToggler = () => BoolCast(this.annoTextRegion.followLinkToggle);
    @undoBatch
    showTargetTrail = (anchor: Doc) => {
        const trail = DocCast(anchor.presTrail);
        if (trail) {
            Doc.ActivePresentation = trail;
            this.props.addDocTab(trail, OpenWhere.replaceRight);
        }
    };

    @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.PinToPres = this.pinToPres;
            AnchorMenu.Instance.MakeTargetToggle = this.makeTargretToggle;
            AnchorMenu.Instance.IsTargetToggler = this.isTargetToggler;
            AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(this.annoTextRegion);
            AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
            e.stopPropagation();
        } else if (e.button === 0) {
            e.stopPropagation();
            LinkFollower.FollowLink(undefined, this.annoTextRegion, false);
        }
    };

    render() {
        const brushed = this.annoTextRegion && Doc.isBrushedHighlightedDegree(this.annoTextRegion);
        return (
            <div
                className="htmlAnnotation"
                ref={this._mainCont}
                onPointerEnter={action(() => {
                    Doc.BrushDoc(this.props.anno);
                    this.props.showInfo(this.props.anno);
                })}
                onPointerLeave={action(() => {
                    Doc.UnBrushDoc(this.props.anno);
                    this.props.showInfo(undefined);
                })}
                onPointerDown={this.onPointerDown}
                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: brushed === Doc.DocBrushStatus.highlighted ? 0.5 : undefined,
                    pointerEvents: this.props.pointerEvents?.() as any,
                    outline: brushed === Doc.DocBrushStatus.linkHighlighted ? 'solid 1px lightBlue' : undefined,
                    backgroundColor: brushed === Doc.DocBrushStatus.highlighted ? 'orange' : StrCast(this.props.document.backgroundColor),
                }}
            />
        );
    }
}