forked from gridstack/gridstack.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdd-resizable-handle.ts
129 lines (115 loc) · 4.34 KB
/
dd-resizable-handle.ts
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
/**
* dd-resizable-handle.ts 10.3.1-dev
* Copyright (c) 2021-2022 Alain Dumesny - see GridStack root license
*/
import { isTouch, pointerdown, touchend, touchmove, touchstart } from './dd-touch';
import { GridItemHTMLElement } from './gridstack';
export interface DDResizableHandleOpt {
start?: (event) => void;
move?: (event) => void;
stop?: (event) => void;
}
export class DDResizableHandle {
/** @internal */
protected el: HTMLElement;
/** @internal true after we've moved enough pixels to start a resize */
protected moving = false;
/** @internal */
protected mouseDownEvent: MouseEvent;
/** @internal */
protected static prefix = 'ui-resizable-';
constructor(protected host: GridItemHTMLElement, protected dir: string, protected option: DDResizableHandleOpt) {
// create var event binding so we can easily remove and still look like TS methods (unlike anonymous functions)
this._mouseDown = this._mouseDown.bind(this);
this._mouseMove = this._mouseMove.bind(this);
this._mouseUp = this._mouseUp.bind(this);
this._keyEvent = this._keyEvent.bind(this);
this._init();
}
/** @internal */
protected _init(): DDResizableHandle {
const el = this.el = document.createElement('div');
el.classList.add('ui-resizable-handle');
el.classList.add(`${DDResizableHandle.prefix}${this.dir}`);
el.style.zIndex = '100';
el.style.userSelect = 'none';
this.host.appendChild(this.el);
this.el.addEventListener('mousedown', this._mouseDown);
if (isTouch) {
this.el.addEventListener('touchstart', touchstart);
this.el.addEventListener('pointerdown', pointerdown);
// this.el.style.touchAction = 'none'; // not needed unlike pointerdown doc comment
}
return this;
}
/** call this when resize handle needs to be removed and cleaned up */
public destroy(): DDResizableHandle {
if (this.moving) this._mouseUp(this.mouseDownEvent);
this.el.removeEventListener('mousedown', this._mouseDown);
if (isTouch) {
this.el.removeEventListener('touchstart', touchstart);
this.el.removeEventListener('pointerdown', pointerdown);
}
this.host.removeChild(this.el);
delete this.el;
delete this.host;
return this;
}
/** @internal called on mouse down on us: capture move on the entire document (mouse might not stay on us) until we release the mouse */
protected _mouseDown(e: MouseEvent): void {
this.mouseDownEvent = e;
document.addEventListener('mousemove', this._mouseMove, { capture: true, passive: true}); // capture, not bubble
document.addEventListener('mouseup', this._mouseUp, true);
if (isTouch) {
this.el.addEventListener('touchmove', touchmove);
this.el.addEventListener('touchend', touchend);
}
e.stopPropagation();
e.preventDefault();
}
/** @internal */
protected _mouseMove(e: MouseEvent): void {
let s = this.mouseDownEvent;
if (this.moving) {
this._triggerEvent('move', e);
} else if (Math.abs(e.x - s.x) + Math.abs(e.y - s.y) > 2) {
// don't start unless we've moved at least 3 pixels
this.moving = true;
this._triggerEvent('start', this.mouseDownEvent);
this._triggerEvent('move', e);
// now track keyboard events to cancel
document.addEventListener('keydown', this._keyEvent);
}
e.stopPropagation();
// e.preventDefault(); passive = true
}
/** @internal */
protected _mouseUp(e: MouseEvent): void {
if (this.moving) {
this._triggerEvent('stop', e);
document.removeEventListener('keydown', this._keyEvent);
}
document.removeEventListener('mousemove', this._mouseMove, true);
document.removeEventListener('mouseup', this._mouseUp, true);
if (isTouch) {
this.el.removeEventListener('touchmove', touchmove);
this.el.removeEventListener('touchend', touchend);
}
delete this.moving;
delete this.mouseDownEvent;
e.stopPropagation();
e.preventDefault();
}
/** @internal call when keys are being pressed - use Esc to cancel */
protected _keyEvent(e: KeyboardEvent): void {
if (e.key === 'Escape') {
this.host.gridstackNode?.grid?.engine.restoreInitial();
this._mouseUp(this.mouseDownEvent);
}
}
/** @internal */
protected _triggerEvent(name: string, event: MouseEvent): DDResizableHandle {
if (this.option[name]) this.option[name](event);
return this;
}
}