aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/MainOverlayTextBox.tsx
blob: 1933ab94b0f6cf53e101d4146baff54ac19475f2 (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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import { action, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import "normalize.css";
import * as React from 'react';
import { Doc } from '../../new_fields/Doc';
import { BoolCast } from '../../new_fields/Types';
import { emptyFunction, returnTrue, returnZero, Utils } from '../../Utils';
import { DragManager } from '../util/DragManager';
import { Transform } from '../util/Transform';
import { CollectionDockingView } from './collections/CollectionDockingView';
import "./MainOverlayTextBox.scss";
import { FormattedTextBox } from './nodes/FormattedTextBox';

interface MainOverlayTextBoxProps {
}

@observer
export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps> {
    public static Instance: MainOverlayTextBox;
    @observable _textXf: () => Transform = () => Transform.Identity();
    public TextFieldKey: string = "data";
    private _textColor: string | null = null;
    private _textHideOnLeave?: boolean;
    private _textTargetDiv: HTMLDivElement | undefined;
    private _textProxyDiv: React.RefObject<HTMLDivElement>;
    private _textBottom: boolean | undefined;
    private _textAutoHeight: boolean | undefined;
    private _setouterdiv = (outerdiv: HTMLElement | null) => { this._outerdiv = outerdiv; this.updateTooltip(); };
    private _outerdiv: HTMLElement | null = null;
    private _textBox: FormattedTextBox | undefined;
    private _tooltip?: HTMLElement;
    @observable public TextDoc?: Doc;

    updateTooltip = () => {
        this._outerdiv && this._tooltip && !this._outerdiv.contains(this._tooltip) && this._outerdiv.appendChild(this._tooltip);
    }

    constructor(props: MainOverlayTextBoxProps) {
        super(props);
        this._textProxyDiv = React.createRef();
        MainOverlayTextBox.Instance = this;
        reaction(() => FormattedTextBox.InputBoxOverlay,
            (box?: FormattedTextBox) => {
                this._textBox = box;
                if (box) {
                    this.TextDoc = box.props.DataDoc;
                    let sxf = Utils.GetScreenTransform(box ? box.CurrentDiv : undefined);
                    let xf = () => { box.props.ScreenToLocalTransform(); return new Transform(-sxf.translateX, -sxf.translateY, 1 / sxf.scale); };
                    this.setTextDoc(box.props.fieldKey, box.CurrentDiv, xf, BoolCast(box.props.Document.autoHeight, false) || box.props.height === "min-content");
                }
                else {
                    this.TextDoc = undefined;
                    this.setTextDoc();
                }
            });
    }

    @action
    private setTextDoc(textFieldKey?: string, div?: HTMLDivElement, tx?: () => Transform, autoHeight?: boolean) {
        if (this._textTargetDiv) {
            this._textTargetDiv.style.color = this._textColor;
        }
        this._textAutoHeight = autoHeight;
        this.TextFieldKey = textFieldKey!;
        let txf = tx ? tx : () => Transform.Identity();
        this._textXf = txf;
        this._textTargetDiv = div;
        this._textHideOnLeave = FormattedTextBox.InputBoxOverlay && FormattedTextBox.InputBoxOverlay.props.hideOnLeave;
        if (div) {
            this._textBottom = div.parentElement && div.parentElement.style.bottom ? true : false;
            this._textColor = (getComputedStyle(div) as any).color;
            div.style.color = "transparent";
        }
    }

    @action
    textScroll = (e: React.UIEvent) => {
        if (this._textProxyDiv.current && this._textTargetDiv) {
            this._textTargetDiv.scrollTop = (e as any)._targetInst.stateNode.scrollTop;
        }
    }

    textBoxDown = (e: React.PointerEvent) => {
        if (e.button !== 0 || e.metaKey || e.altKey) {
            document.addEventListener("pointermove", this.textBoxMove);
            document.addEventListener('pointerup', this.textBoxUp);
        }
    }
    @action
    textBoxMove = (e: PointerEvent) => {
        if ((e.movementX > 1 || e.movementY > 1) && FormattedTextBox.InputBoxOverlay) {
            document.removeEventListener("pointermove", this.textBoxMove);
            document.removeEventListener('pointerup', this.textBoxUp);
            let dragData = new DragManager.DocumentDragData([FormattedTextBox.InputBoxOverlay.props.Document], [FormattedTextBox.InputBoxOverlay.props.DataDoc]);
            const [left, top] = this._textXf().inverse().transformPoint(0, 0);
            dragData.xOffset = e.clientX - left;
            dragData.yOffset = e.clientY - top;
            DragManager.StartDocumentDrag([this._textTargetDiv!], dragData, e.clientX, e.clientY, {
                handlers: {
                    dragComplete: action(emptyFunction),
                },
                hideSource: false
            });
        }
    }
    textBoxUp = (e: PointerEvent) => {
        document.removeEventListener("pointermove", this.textBoxMove);
        document.removeEventListener('pointerup', this.textBoxUp);
    }

    addDocTab = (doc: Doc, dataDoc: Doc, location: string) => {
        if (true) { // location === "onRight") { need to figure out stack to add "inTab"
            CollectionDockingView.Instance.AddRightSplit(doc, dataDoc);
        }
    }
    render() {
        this.TextDoc;
        if (FormattedTextBox.InputBoxOverlay && this._textTargetDiv) {
            let textRect = this._textTargetDiv.getBoundingClientRect();
            let s = this._textXf().Scale;
            let location = this._textBottom ? textRect.bottom : textRect.top;
            let hgt = this._textAutoHeight || this._textBottom ? "auto" : this._textTargetDiv.clientHeight;
            return <div ref={this._setouterdiv} className="mainOverlayTextBox-unscaled_div" style={{ transform: `translate(${textRect.left}px, ${location}px)` }} >
                <div className="mainOverlayTextBox-textInput" style={{ transform: `scale(${1 / s},${1 / s})`, width: "auto", height: "0px" }} >
                    <div className="mainOverlayTextBox-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll}
                        style={{ width: `${textRect.width * s}px`, height: "0px" }}>
                        <div style={{ height: hgt, width: "100%", position: "absolute", bottom: this._textBottom ? "0px" : undefined }}>
                            <FormattedTextBox color={`${this._textColor}`} fieldKey={this.TextFieldKey} fieldExt="" hideOnLeave={this._textHideOnLeave} isOverlay={true}
                                Document={FormattedTextBox.InputBoxOverlay.props.Document}
                                DataDoc={FormattedTextBox.InputBoxOverlay.props.DataDoc}
                                isSelected={returnTrue} select={emptyFunction} renderDepth={0} selectOnLoad={true}
                                ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue}
                                ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction} addDocTab={this.addDocTab} outer_div={(tooltip: HTMLElement) => { this._tooltip = tooltip; this.updateTooltip(); }} />
                        </div>
                    </div>
                </div>
            </ div>;
        }
        else return (null);
    }
}