odious
Version:
Odious fundamentally revolutionizes the software development paradigm by seamlessly integrating deployment considerations into the development process, thus empowering developers of all skill levels to create and deploy applications with unprecedented eas
105 lines (79 loc) • 4.01 kB
JavaScript
// updated screen to client
export default class Movable {
windowComponent;
cardElement;
dragHandle;
#dragging = false;
#previousX = 0;
#previousY = 0;
finalCoordinate; // log the final command
constructor(windowComponent) {
this.windowComponent = windowComponent;
this.cardElement = this.windowComponent.shadowRoot.querySelector(".card");
this.dragHandle = this.cardElement.querySelector(".card-header");
this.mouseDownHandler = this.mouseDownHandler.bind(this);
this.mouseMoveHandler = this.mouseMoveHandler.bind(this);
this.mouseUpHandler = this.mouseUpHandler.bind(this);
}
start() {
const position = window.getComputedStyle(this.cardElement).position;
if (!position === "absolute") this.cardElement.style.position = "absolute"; // Ensure the element is absolutely positioned
this.dragHandle.addEventListener("mousedown", this.mouseDownHandler),
document.addEventListener("mouseup", this.mouseUpHandler);
return () => this.stop();
}
stop() {
this.dragHandle.removeEventListener("mousedown", this.mouseDownHandler);
document.removeEventListener("mouseup", this.mouseUpHandler);
document.removeEventListener("mousemove", this.mouseMoveHandler);
}
mouseDownHandler(event) {
// event.stopPropagation(); // Prevents the event from bubbling up the DOM tree, preventing any parent handlers from being notified of the event.
event.preventDefault(); // Prevent prevent the default behavior - default action should not be taken as it normally would be.
this.#dragging = true;
// Initialize previousN
// NOTE: a sister call must be made at the end of mouseMoveHandler
this.#previousX = event.clientX;
this.#previousY = event.clientY;
document.addEventListener("mousemove", this.mouseMoveHandler);
}
mouseMoveHandler(event) {
event.stopPropagation();
event.preventDefault();
if (this.#dragging) {
// Calculate Relative Delta
// NOTE: this depends on "Update previousN" and we just substract to get a -n or +n from the last few dozen miliseconds
let deltaX = this.#previousX - event.clientX;
let deltaY = this.#previousY - event.clientY;
// Apply scale transformation if any to delta (which uses screen pixels)
// NOTE: deltaN calculations use screenN which is an untransformed pixel, but we maybe zoomed in/out so we account for that.
const scale = this.cardElement.getBoundingClientRect().width / this.cardElement.offsetWidth;
deltaX = deltaX / scale;
deltaY = deltaY / scale;
// RARE: if there is no style set, initialize values
if (this.cardElement.style.left === "") this.cardElement.style.left = this.cardElement.offsetLeft + "px";
if (this.cardElement.style.top === "") this.cardElement.style.top = this.cardElement.offsetTop + "px";
// Apply the delta to element being dragged
// NOTE: we are only adding the updated delta to existing x and y - think of it as matching the motion that the cursor made since the last sampling of previousN
let x = parseFloat(this.cardElement.style.left) - deltaX;
let y = parseFloat(this.cardElement.style.top) - deltaY;
// Apply the updated x and y to the element
// NOTE: x and y are updated by the scaled delta
// TODO: should only assign to attribute
this.cardElement.style.left = `${x}px`;
this.cardElement.style.top = `${y}px`;
this.finalCoordinate = { id: this.windowComponent.id, left: x, top: y };
// WARN: you must use floating point numbers, otherwise motion is inaccurate
this.windowComponent.source.root.commander.windowMove(
{ id: this.windowComponent.id, left: x, top: y }
);
// Update previousN - get ready for next update
this.#previousX = event.clientX;
this.#previousY = event.clientY;
}
}
mouseUpHandler(event) {
this.#dragging = false;
document.removeEventListener("mousemove", this.mouseMoveHandler);
}
}