aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/LinkAnchorBox.tsx
blob: ff1e628854954bb05a4365aadcf811a8f277fc10 (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
import { action, computed, makeObservable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Utils, emptyFunction, setupMoveUpEvents } from '../../../Utils';
import { Doc } from '../../../fields/Doc';
import { NumCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
import { DragManager, dropActionType } from '../../util/DragManager';
import { LinkFollower } from '../../util/LinkFollower';
import { SelectionManager } from '../../util/SelectionManager';
import { ViewBoxBaseComponent } from '../DocComponent';
import { StyleProp } from '../StyleProvider';
import { FieldView, FieldViewProps } from './FieldView';
import './LinkAnchorBox.scss';
import { LinkInfo } from './LinkDocPreview';
const { MEDIUM_GRAY } = require('../global/globalCssVariables.module.scss'); // prettier-ignore
@observer
export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
    public static LayoutString(fieldKey: string) {
        return FieldView.LayoutString(LinkAnchorBox, fieldKey);
    }
    _doubleTap = false;
    _lastTap: number = 0;
    _ref = React.createRef<HTMLDivElement>();
    _isOpen = false;
    _timeout: NodeJS.Timeout | undefined;

    constructor(props: FieldViewProps) {
        super(props);
        makeObservable(this);
    }

    componentDidMount() {
        this._props.setContentViewBox?.(this);
    }

    @computed get linkSource() {
        return this.DocumentView?.().containerViewPath?.().lastElement().Document; // this._props.styleProvider?.(this.dataDoc, this._props, StyleProp.LinkSource);
    }

    onPointerDown = (e: React.PointerEvent) => {
        const linkSource = this.linkSource;
        linkSource &&
            setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, (e, doubleTap) => {
                if (doubleTap) LinkFollower.FollowLink(this.Document, linkSource, false);
                else this._props.select(false);
            });
    };
    onPointerMove = action((e: PointerEvent, down: number[], delta: number[]) => {
        const cdiv = this._ref?.current?.parentElement;
        if (!this._isOpen && cdiv) {
            const bounds = cdiv.getBoundingClientRect();
            const pt = Utils.getNearestPointInPerimeter(bounds.left, bounds.top, bounds.width, bounds.height, e.clientX, e.clientY);
            const separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY));
            if (separation > 100) {
                const dragData = new DragManager.DocumentDragData([this.Document]);
                dragData.dropAction = dropActionType.embed;
                dragData.dropPropertiesToRemove = ['link_anchor_1_x', 'link_anchor_1_y', 'link_anchor_2_x', 'link_anchor_2_y', 'onClick'];
                DragManager.StartDocumentDrag([this._ref.current!], dragData, pt[0], pt[1]);
                return true;
            } else {
                this.layoutDoc[this.fieldKey + '_x'] = ((pt[0] - bounds.left) / bounds.width) * 100;
                this.layoutDoc[this.fieldKey + '_y'] = ((pt[1] - bounds.top) / bounds.height) * 100;
                this.layoutDoc.link_autoMoveAnchors = false;
            }
        }
        return false;
    });

    specificContextMenu = (e: React.MouseEvent): void => {};

    render() {
        TraceMobx();
        const small = this._props.PanelWidth() <= 1; // this happens when rendered in a treeView
        const x = NumCast(this.layoutDoc[this.fieldKey + '_x'], 100);
        const y = NumCast(this.layoutDoc[this.fieldKey + '_y'], 100);
        const background = this._props.styleProvider?.(this.dataDoc, this._props, StyleProp.BackgroundColor + ':anchor');
        const anchor = this.fieldKey === 'link_anchor_1' ? 'link_anchor_2' : 'link_anchor_1';
        const anchorScale = !this.dataDoc[this.fieldKey + '_useSmallAnchor'] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : 0.25;
        const targetTitle = StrCast((this.dataDoc[anchor] as Doc)?.title);
        const selView = SelectionManager.Views.lastElement()?._props.LayoutTemplateString?.includes('link_anchor_1')
            ? 'link_anchor_1'
            : SelectionManager.Views.lastElement()?._props.LayoutTemplateString?.includes('link_anchor_2')
              ? 'link_anchor_2'
              : '';
        return (
            <div
                ref={this._ref}
                title={targetTitle}
                className={`linkAnchorBox-cont${small ? '-small' : ''}`}
                onPointerEnter={e =>
                    LinkInfo.SetLinkInfo({
                        DocumentView: this.DocumentView,
                        styleProvider: this._props.styleProvider,
                        linkSrc: this.linkSource,
                        linkDoc: this.Document,
                        showHeader: true,
                        location: [e.clientX, e.clientY + 20],
                        noPreview: false,
                    })
                }
                onPointerDown={this.onPointerDown}
                onContextMenu={this.specificContextMenu}
                style={{
                    border: selView && this.dataDoc[selView] === this.dataDoc[this.fieldKey] ? `solid ${MEDIUM_GRAY} 2px` : undefined,
                    background,
                    left: `calc(${x}% - ${small ? 2.5 : 7.5}px)`,
                    top: `calc(${y}% - ${small ? 2.5 : 7.5}px)`,
                    transform: `scale(${anchorScale})`,
                    cursor: 'grab',
                }}
            />
        );
    }
}