export class Draggable {
el: HTMLElement;
reparent: boolean = true;
lockSize: boolean = true;
onBeforeDragStart: () => void = null;
onAfterDragEnd: () => void = null;
customMoveHandler: (x: number, y: number) => void = null;
constructor(el: HTMLElement) {
this.el = el;
}
}
// If we're currently dragging something (a window or a file), this is the DOM object we're dragging
// dragging_offset_x and dragging_offset_y are the difference between the objec's top left point and the cursor position
// this is then added to the cursor position when the mouse is moved
export var dragging: Draggable = null;
export var dragging_offset_x = 0, dragging_offset_y = 0;
// If we have pressed down a window's title bar but haven't yet started dragging it, it's the drag candidate
// This is needed because we don't yet know whether we want to start dragging the window or click an element in the titlebar
// Once the mouse has moved sufficiently far away from dragging_candidate_x or y, we start dragging
export var dragging_candidate: Draggable = null;
export var dragging_candidate_x, dragging_candidate_y;
export function set_dragging_candidate(e, candidate: Draggable) {
dragging_candidate = candidate;
dragging_candidate_x = e.clientX;
dragging_candidate_y = e.clientY;
}
// Start dragging the 'obj' DOM element
// e is a DOM event, this should only get called in response of a DOM event
export function begin_drag(e, d: Draggable) {
if (d.onBeforeDragStart)
d.onBeforeDragStart()
set_iframe_enabled(false);
dragging = d;
dragging_candidate = null;
dragging.el.classList.add("dragged");
var elemRect = dragging.el.getBoundingClientRect();
dragging_offset_x = e.clientX - elemRect.left;
dragging_offset_y = -e.clientY + elemRect.top;
if (dragging.lockSize) {
dragging.el.style.left = (e.clientX - dragging_offset_x) + "px";
dragging.el.style.top = (e.clientY + dragging_offset_y) + "px";
dragging.el.style.width = elemRect.width + "px";
dragging.el.style.height = elemRect.height + "px";
dragging.el.style.position = "absolute";
}
if (dragging.reparent)
document.body.appendChild(dragging.el);
}
export function end_drag(_e) {
set_iframe_enabled(true);
dragging.el.classList.remove("dragged");
if (dragging.onAfterDragEnd)
dragging.onAfterDragEnd();
dragging = null;
}
function set_iframe_enabled(en) {
const frames = document.getElementsByTagName('iframe');
for (var i = 0; i < frames.length; i++)
frames.item(i).hidden = !en;
}
document.body.onmouseup = (_e) => {
if (dragging_candidate)
dragging_candidate = null;
if (dragging)
end_drag(_e);
}
document.body.onmousemove = (e) => {
if (dragging) {
const x = e.clientX - dragging_offset_x;
const y = e.clientY + dragging_offset_y;
if (dragging.customMoveHandler) {
dragging.customMoveHandler(x, y)
} else {
dragging.el.style.left = x + "px";
dragging.el.style.top = y + "px";
}
}
else if (dragging_candidate) {
var d = Math.abs(e.clientX - dragging_candidate_x) + Math.abs(e.clientY - dragging_candidate_y);
if (d > 15)
begin_drag(e, dragging_candidate);
}
}
export function oncontextmenu_hook(e) {
if (dragging) {
end_drag(e);
e.preventDefault();
}
}