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
142
143
144
145
146
147
148
149
150
151
152
|
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 protected _canFade: boolean = true;
@observable public Pinned: boolean = false;
get width() { return this._mainCont.current ? this._mainCont.current.getBoundingClientRect().width : 0; }
get height() { return this._mainCont.current ? this._mainCont.current.getBoundingClientRect().height : 0; }
@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._canFade) {
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 = e.pageX - this._mainCont.current!.getBoundingClientRect().left;
this._offsetY = e.pageY - this._mainCont.current!.getBoundingClientRect().top;
e.stopPropagation();
e.preventDefault();
}
@action
protected dragging = (e: PointerEvent) => {
const width = this._mainCont.current!.getBoundingClientRect().width;
const height = this._mainCont.current!.getBoundingClientRect().height;
const left = e.pageX - this._offsetX;
const top = e.pageY - this._offsetY;
this._left = Math.min(Math.max(left, 0), window.innerWidth - width);
this._top = Math.min(Math.max(top, 0), window.innerHeight - height);
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 getDragger = () => {
return <div className="antimodeMenu-dragger" onPointerDown={this.dragStart} style={{ width: this.Pinned ? "20px" : "0px" }} />;
}
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>
);
}
protected getElementWithRows(rows: JSX.Element[], numRows: number, hasDragger: boolean = true) {
return (
<div className="antimodeMenu-cont with-rows" 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, height: 35 * numRows + "px" }}>
{rows}
{hasDragger ? <div className="antimodeMenu-dragger" onPointerDown={this.dragStart} style={{ width: this.Pinned ? "20px" : "0px" }} /> : <></>}
</div>
);
}
}
|