diff src/dad.js @ 0:8f4df159f06b

start public repo
author Franklin Schmidt <fschmidt@gmail.com>
date Fri, 11 Jul 2025 20:57:49 -0600
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/dad.js	Fri Jul 11 20:57:49 2025 -0600
@@ -0,0 +1,155 @@
+'use strict';
+
+let dad = {};
+
+{
+	// override these if needed
+	dad.whatToDrag = function(draggable) {
+		return draggable;
+	};
+	dad.onStart = function(event) {};
+	dad.onEnter = function(event) {};
+	dad.onLeave = function(event) {};
+	dad.onDrop = function(event) {};
+	dad.onDropped = function(event) {};
+
+
+	let original = null;
+	let dragging = null;
+	let dropzone = null;
+	let touchX, touchY;
+
+	function isIn(x,y,rect) {
+		return rect.x <= x && x <= rect.x+rect.width && rect.y <= y && y <= rect.y+rect.height;
+	}
+
+	function myEvent(mouseEvent) {
+		return {
+			original: original,
+			dragging: dragging,
+			dropzone: dropzone,
+			mouseEvent: mouseEvent,
+		};
+	}
+
+	function onMouseMove(event) {
+		{
+			event.preventDefault();
+			let rect = dragging.getBoundingClientRect();
+			dragging.style.left = `${rect.x+event.movementX}px`;
+			let y = rect.y + event.movementY;
+			if( y < 0 ) {
+				window.scrollBy( 0, y );
+			} else if( y + rect.height > window.innerHeight ) {
+				window.scrollBy( 0, y + rect.height - window.innerHeight );
+			}
+			dragging.style.top = `${y}px`;
+		}
+		{
+			let x = event.clientX;
+			let y = event.clientY;
+			if( !(dropzone && isIn(x,y,dropzone.getBoundingClientRect())) ) {
+				if( dropzone ) {
+					dad.onLeave(myEvent(event));
+					dropzone = null;
+				}
+				let dropzones = document.querySelectorAll('[dad-dropzone]');
+				for( let i=0; i<dropzones.length; i++ ) {
+					let dz = dropzones[i];
+					if( dz === dragging )
+						continue;
+					if( isIn(x,y,dz.getBoundingClientRect()) ) {
+						let old = dropzone;
+						dropzone = dz;
+						let f = dad.onEnter(myEvent(event));
+						if( f === false )
+							dropzone = old;
+						else
+							break;
+					}
+				}
+			}
+		}
+	}
+
+	function onTouchMove(event) {
+		let touches = event.touches;
+		if( touches.length !== 1 )
+			return;
+		let touch = touches[0];
+		let x = touch.clientX;
+		let y = touch.clientY;
+		event.clientX = x;
+		event.clientY = y;
+		event.movementX = x - touchX;
+		event.movementY = y - touchY;
+		touchX = x;
+		touchY = y;
+		onMouseMove(event);
+	}
+
+	function onMouseUp(event) {
+		dad.onDrop(myEvent(event));
+		if( dropzone ) {
+			dad.onLeave(myEvent(event));
+		}
+		original.removeAttribute('dad-original');
+		dragging.parentNode.removeChild(dragging);
+		document.removeEventListener('mousemove',onMouseMove);
+		document.removeEventListener('mouseup',onMouseUp);
+		document.removeEventListener('touchmove',onTouchMove);
+		document.removeEventListener('touchend',onMouseUp);
+		original.scrollIntoViewIfNeeded(false);
+		let droppedEvent = {
+			original: original,
+		};
+		original = null;
+		dragging = null;
+		dropzone = null;
+		dad.onDropped(droppedEvent);
+	}
+
+	function start(event) {
+		original = dad.whatToDrag(event.target);
+		dragging = original.cloneNode(true);
+		original.setAttribute('dad-original','');
+		dragging.setAttribute('dad-dragging','');
+		let rect = original.getBoundingClientRect();
+		dragging.style.left = `${rect.x}px`;
+		dragging.style.top = `${rect.y}px`;
+		dragging.style.width = `${rect.width}px`;
+		dragging.style.height = `${rect.height}px`;
+		original.parentNode.appendChild(dragging);
+		dad.onStart(myEvent(event));
+	}
+
+	function onMouseDown(event) {
+		start(event);
+		document.addEventListener('mousemove',onMouseMove);
+		document.addEventListener('mouseup',onMouseUp);
+	}
+
+	function onTouchStart(event) {
+		event.preventDefault();
+		start(event);
+		let touches = event.touches;
+		if( touches.length !== 1 )
+			return;
+		let touch = touches[0];
+		touchX = touch.clientX;
+		touchY = touch.clientY;
+		document.addEventListener('touchmove',onTouchMove);
+		document.addEventListener('touchend',onMouseUp);
+	}
+
+	dad.setDraggable = function(el) {
+		el.setAttribute('dad-drag','');
+		el.setAttribute('draggable','false');
+		el.addEventListener('mousedown',onMouseDown);
+		el.addEventListener('touchstart',onTouchStart);
+	};
+
+	dad.setDropzone = function(el) {
+		el.setAttribute('dad-dropzone','');
+	};
+}