Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions front/assets/css/pylon-chat.css
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
width: calc(100% + 30px);
height: calc(100% + 30px);
cursor: grab;
touch-action: none;
background: transparent;
z-index: 2;
}
Expand Down
59 changes: 39 additions & 20 deletions front/assets/js/pylon_chat_draggable.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,24 @@ function makeContainerDraggable(container) {
const overlay = ensureDragOverlay(container);
let dragState = null;

const onMouseMove = (event) => {
const finishDrag = () => {
if (!dragState) return;

container.classList.remove(DRAGGING_CLASS);

if (dragState.didMove) {
const finalRect = container.getBoundingClientRect();
safeWritePosition({ x: finalRect.left, y: finalRect.top });
} else {
togglePylonChat();
}

dragState = null;
};

const onPointerMove = (event) => {
if (!dragState || event.pointerId !== dragState.pointerId) return;

if (!dragState.didMove) {
const deltaX = Math.abs(event.clientX - dragState.startClientX);
const deltaY = Math.abs(event.clientY - dragState.startClientY);
Expand All @@ -143,41 +158,45 @@ function makeContainerDraggable(container) {
}
};

const onMouseUp = () => {
if (!dragState) return;

document.removeEventListener("mousemove", onMouseMove);
document.removeEventListener("mouseup", onMouseUp);
container.classList.remove(DRAGGING_CLASS);

if (dragState.didMove) {
const finalRect = container.getBoundingClientRect();
safeWritePosition({ x: finalRect.left, y: finalRect.top });
} else {
togglePylonChat();
}
const onPointerUp = (event) => {
if (!dragState || event.pointerId !== dragState.pointerId) return;
finishDrag();
};

dragState = null;
const onPointerCancel = (event) => {
if (!dragState || event.pointerId !== dragState.pointerId) return;
finishDrag();
};

const onMouseDown = (event) => {
if (event.button !== 0 || dragState) return;
const onPointerDown = (event) => {
if (dragState) return;
if (event.pointerType === "mouse" && event.button !== 0) return;

const rect = container.getBoundingClientRect();
dragState = {
pointerId: event.pointerId,
pointerOffsetX: event.clientX - rect.left,
pointerOffsetY: event.clientY - rect.top,
startClientX: event.clientX,
startClientY: event.clientY,
didMove: false
};

document.addEventListener("mousemove", onMouseMove);
document.addEventListener("mouseup", onMouseUp);
if (typeof overlay.setPointerCapture === "function") {
try {
overlay.setPointerCapture(event.pointerId);
} catch {
// Not supported on all browsers
}
}

event.preventDefault();
};

overlay.addEventListener("mousedown", onMouseDown);
overlay.addEventListener("pointerdown", onPointerDown);
overlay.addEventListener("pointermove", onPointerMove);
overlay.addEventListener("pointerup", onPointerUp);
overlay.addEventListener("pointercancel", onPointerCancel);
}

export function initPylonChatDraggable() {
Expand Down
62 changes: 19 additions & 43 deletions front/assets/js/pylon_chat_draggable.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,45 +72,19 @@ function createContainer({
return container;
}

function createMouseEvent(type, { x, y, button = 0 } = {}) {
const mouseEventInit = {
function createPointerEvent(type, { x, y, button = 0, pointerId = 1, pointerType = "mouse" } = {}) {
const pointerEventInit = {
bubbles: true,
cancelable: true,
clientX: x,
clientY: y,
button
button,
pointerId,
pointerType
};

if (typeof window.MouseEvent === "function") {
return new window.MouseEvent(type, mouseEventInit);
}

if (typeof MouseEvent === "function") {
return new MouseEvent(type, mouseEventInit);
}

if (typeof document.createEvent === "function") {
const legacyMouseEvent = document.createEvent("MouseEvents");
if (typeof legacyMouseEvent.initMouseEvent === "function") {
legacyMouseEvent.initMouseEvent(
type,
true,
true,
window,
0,
0,
0,
x || 0,
y || 0,
false,
false,
false,
false,
button,
null
);
return legacyMouseEvent;
}
if (typeof window.PointerEvent === "function") {
return new window.PointerEvent(type, pointerEventInit);
}

const fallbackEvent =
Expand All @@ -125,17 +99,19 @@ function createMouseEvent(type, { x, y, button = 0 } = {}) {
Object.defineProperty(fallbackEvent, "clientX", { value: x || 0 });
Object.defineProperty(fallbackEvent, "clientY", { value: y || 0 });
Object.defineProperty(fallbackEvent, "button", { value: button });
Object.defineProperty(fallbackEvent, "pointerId", { value: pointerId });
Object.defineProperty(fallbackEvent, "pointerType", { value: pointerType });

return fallbackEvent;
}

function dispatchMouseEvent(target, type, { x, y, button = 0 } = {}) {
target.dispatchEvent(createMouseEvent(type, { x, y, button }));
function dispatchPointerEvent(target, type, { x, y, button = 0, pointerId = 1, pointerType = "mouse" } = {}) {
target.dispatchEvent(createPointerEvent(type, { x, y, button, pointerId, pointerType }));
}

function clickOverlay(overlay, x = 100, y = 100) {
dispatchMouseEvent(overlay, "mousedown", { x, y });
dispatchMouseEvent(document, "mouseup", { x, y });
dispatchPointerEvent(overlay, "pointerdown", { x, y });
dispatchPointerEvent(overlay, "pointerup", { x, y });
}

function createChatWindow({
Expand Down Expand Up @@ -265,18 +241,18 @@ describe("initPylonChatDraggable", () => {
initPylonChatDraggable();
const overlay = container.querySelector(`.${OVERLAY_CLASS}`);

dispatchMouseEvent(overlay, "mousedown", { x: 100, y: 100 });
dispatchMouseEvent(document, "mousemove", { x: 103, y: 103 });
dispatchMouseEvent(document, "mouseup", { x: 103, y: 103 });
dispatchPointerEvent(overlay, "pointerdown", { x: 100, y: 100 });
dispatchPointerEvent(overlay, "pointermove", { x: 103, y: 103 });
dispatchPointerEvent(overlay, "pointerup", { x: 103, y: 103 });

expect(window.Pylon.calledOnce).to.equal(true);
expect(window.localStorage.getItem(STORAGE_KEY)).to.equal(null);

window.Pylon.resetHistory();

dispatchMouseEvent(overlay, "mousedown", { x: 100, y: 100 });
dispatchMouseEvent(document, "mousemove", { x: 106, y: 106 });
dispatchMouseEvent(document, "mouseup", { x: 106, y: 106 });
dispatchPointerEvent(overlay, "pointerdown", { x: 100, y: 100 });
dispatchPointerEvent(overlay, "pointermove", { x: 106, y: 106 });
dispatchPointerEvent(overlay, "pointerup", { x: 106, y: 106 });

expect(window.Pylon.called).to.equal(false);

Expand Down