aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/AntimodeMenu.tsx
blob: 408df8bc20e20ddff47f64caafb4d8416e160dd5 (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
import React = require("react");
import { observer } from "mobx-react";
import { observable, action } from "mobx";
import "./AntimodeMenu.scss";

/**
 * This is an abstract class that serves as the base for a PDF-style or Marquee-style
 * menu. To use this class, look at PDFMenu.tsx or MarqueeOptionsMenu.tsx for an example.
 */
export default abstract class AntimodeMenu extends React.Component {
    protected _offsetY: number = 0;
    protected _offsetX: number = 0;
    protected _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
    protected _dragging: boolean = false;

    @observable protected _top: number = -300;
    @observable protected _left: number = -300;
    @observable protected _opacity: number = 1;
    @observable protected _transition: string = "opacity 0.5s";
    @observable protected _transitionDelay: string = "";

    @observable public Pinned: boolean = false;

    @action
    /**
     * @param x
     * @param y
     * @param forceJump: If the menu is pinned down, do you want to force it to jump to the new location?
     * Called when you want the menu to show up at a location
     */
    public jumpTo = (x: number, y: number, forceJump: boolean = false) => {
        if (!this.Pinned || forceJump) {
            this._transition = this._transitionDelay = "";
            this._opacity = 1;
            this._left = x;
            this._top = y;
        }
    }

    @action
    /**
     * @param forceOut: Do you want the menu to disappear immediately or to slowly fadeout?
     * Called when you want the menu to disappear
     */
    public fadeOut = (forceOut: boolean) => {
        if (!this.Pinned) {
            if (this._opacity === 0.2) {
                this._transition = "opacity 0.1s";
                this._transitionDelay = "";
                this._opacity = 0;
                this._left = this._top = -300;
            }

            if (forceOut) {
                this._transition = "";
                this._transitionDelay = "";
                this._opacity = 0;
                this._left = this._top = -300;
            }
        }
    }

    @action
    protected pointerLeave = (e: React.PointerEvent) => {
        if (!this.Pinned) {
            this._transition = "opacity 0.5s";
            this._transitionDelay = "1s";
            this._opacity = 0.2;
            setTimeout(() => this.fadeOut(false), 3000);
        }
    }

    @action
    protected pointerEntered = (e: React.PointerEvent) => {
        this._transition = "opacity 0.1s";
        this._transitionDelay = "";
        this._opacity = 1;
    }

    @action
    protected togglePin = (e: React.MouseEvent) => {
        this.Pinned = !this.Pinned;
    }

    protected dragStart = (e: React.PointerEvent) => {
        document.removeEventListener("pointermove", this.dragging);
        document.addEventListener("pointermove", this.dragging);
        document.removeEventListener("pointerup", this.dragEnd);
        document.addEventListener("pointerup", this.dragEnd);

        this._offsetX = this._mainCont.current!.getBoundingClientRect().width - e.nativeEvent.offsetX;
        this._offsetY = e.nativeEvent.offsetY;

        e.stopPropagation();
        e.preventDefault();
    }

    @action
    protected dragging = (e: PointerEvent) => {
        this._left = e.pageX - this._offsetX;
        this._top = e.pageY - this._offsetY;

        e.stopPropagation();
        e.preventDefault();
    }

    protected dragEnd = (e: PointerEvent) => {
        document.removeEventListener("pointermove", this.dragging);
        document.removeEventListener("pointerup", this.dragEnd);
        e.stopPropagation();
        e.preventDefault();
    }

    protected handleContextMenu = (e: React.MouseEvent) => {
        e.stopPropagation();
        e.preventDefault();
    }

    protected getElement(buttons: JSX.Element[]) {
        return (
            <div className="antimodeMenu-cont" onPointerLeave={this.pointerLeave} onPointerEnter={this.pointerEntered} ref={this._mainCont} onContextMenu={this.handleContextMenu}
                style={{ left: this._left, top: this._top, opacity: this._opacity, transition: this._transition, transitionDelay: this._transitionDelay }}>
                {buttons}
                <div className="antimodeMenu-dragger" onPointerDown={this.dragStart} style={{ width: this.Pinned ? "20px" : "0px" }} />
            </div>
        );
    }
}