view src/dad.js @ 10:ad2f6525c4c5

minor fix
author Franklin Schmidt <fschmidt@gmail.com>
date Thu, 11 May 2023 13:07:00 -0600
parents eee8862be4c7
children 80ef70ca5860
line wrap: on
line source

'use strict';

let dad = {};

{
	// override these if needed
	dad.whatToDrag = function(draggable) {
		return draggable;
	};
	dad.onDrop = function(event) {};
	dad.onDropped = function(event) {};
	dad.onEnter = function(event) {};
	dad.onLeave = 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 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({
						original: original,
						dragging: dragging,
						dropzone: dropzone,
						mouseEvent: 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()) ) {
						dropzone = dz;
						dad.onEnter({
							original: original,
							dragging: dragging,
							dropzone: dropzone,
							mouseEvent: event,
						});
						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({
			original: original,
			dragging: dragging,
			dropzone: dropzone,
			mouseEvent: event,
		});
		if( dropzone ) {
			dad.onLeave({
				original: original,
				dragging: dragging,
				dropzone: dropzone,
				mouseEvent: event,
			});
		}
		original.removeAttribute('dad-original');
		dragging.parentNode.removeChild(dragging);
		document.removeEventListener('mousemove',onMouseMove);
		document.removeEventListener('mouseup',onMouseUp);
		document.removeEventListener('mousemove',onMouseMove);
		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.heigh = `${rect.heigh}px`;
		original.parentNode.appendChild(dragging);
	}

	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.addEventListener('mousedown',onMouseDown);
		el.addEventListener('touchstart',onTouchStart);
	};

	dad.setDropzone = function(el) {
		el.setAttribute('dad-dropzone','');
	};
}