From 9b6dcdcd0628d5c79366880909d64fc47b7fc807 Mon Sep 17 00:00:00 2001 From: Reece Browne Date: Mon, 16 Dec 2024 19:07:15 +0000 Subject: [PATCH 1/3] Fix drag and drop bugs and clean up UI --- src/main/resources/static/css/dragdrop.css | 29 +++-- .../static/js/multitool/DragDropManager.js | 108 +++++++++++------- .../static/js/multitool/commands/move-page.js | 54 +++------ 3 files changed, 97 insertions(+), 94 deletions(-) diff --git a/src/main/resources/static/css/dragdrop.css b/src/main/resources/static/css/dragdrop.css index 65ec786f..560834e6 100644 --- a/src/main/resources/static/css/dragdrop.css +++ b/src/main/resources/static/css/dragdrop.css @@ -29,13 +29,9 @@ } .drag-manager_dragging { - width: 0px; - visibility: hidden; + opacity: 0.5; } -.drag-manager_draghover { - width: 375px !important; -} .drag-manager_draghover .insert-file-button-container { display: none !important; @@ -46,11 +42,11 @@ } html[dir="ltr"] .drag-manager_draghover img { - left: calc(50% + 62.5px) !important; + left: 80% !important; } html[dir="rtl"] .drag-manager_draghover img { - left: 125px; + left: 25px; } .drag-manager_dragging-container .hide-on-drag { @@ -58,14 +54,17 @@ html[dir="rtl"] .drag-manager_draghover img { } .drag-manager_endpoint { - width: 80px; - height: 100%; + width: 150px; + height: 250px; background-color: #ffffff10; transition: width 0.1s; animation: end-drop-expand 0.3s ease; display: flex; align-items: center; justify-content: center; + margin-left:16px; + border-radius: 8px; + } .drag-manager_endpoint svg { @@ -74,7 +73,8 @@ html[dir="rtl"] .drag-manager_draghover img { } .drag-manager_endpoint.drag-manager_draghover { - width: 150px !important; + width: 180px !important; + border: 2px solid darkgreen; } @keyframes end-drop-expand { @@ -85,3 +85,12 @@ html[dir="rtl"] .drag-manager_draghover img { width: 80px; } } +.moved-element { + background-color: darkgreen; + border-radius: 8px; + transition: background-color 0.5s ease-out; +} + +.moved-element.remove { + background-color: transparent; +} diff --git a/src/main/resources/static/js/multitool/DragDropManager.js b/src/main/resources/static/js/multitool/DragDropManager.js index ce79ee14..62093c11 100644 --- a/src/main/resources/static/js/multitool/DragDropManager.js +++ b/src/main/resources/static/js/multitool/DragDropManager.js @@ -1,7 +1,7 @@ class DragDropManager { constructor(id, wrapperId) { this.dragContainer = document.getElementById(id); - this.pageDirection = document.documentElement.getAttribute("dir"); + this.pageDirection = document.documentElement.getAttribute('dir'); this.wrapper = document.getElementById(wrapperId); this.pageDragging = false; this.hoveredEl = undefined; @@ -10,14 +10,15 @@ class DragDropManager { this.selectedPageElements = []; // Store selected pages for multi-page mode // Add CSS dynamically - const styleElement = document.createElement("link"); - styleElement.rel = "stylesheet"; - styleElement.href = "css/dragdrop.css"; + const styleElement = document.createElement('link'); + styleElement.rel = 'stylesheet'; + styleElement.href = 'css/dragdrop.css'; document.head.appendChild(styleElement); // Create the endpoint element - const div = document.createElement("div"); - div.classList.add("drag-manager_endpoint"); + const div = document.createElement('div'); + div.classList.add('page-container'); + div.classList.add('drag-manager_endpoint'); div.innerHTML = ` @@ -35,59 +36,62 @@ class DragDropManager { startDraggingPage(div) { if (window.selectPage) { // Multi-page drag logic - this.selectedPageElements = window.selectedPages.map((index) => { - const pageEl = document.getElementById(`page-container-${index}`); - if (pageEl) { - pageEl.initialTransform = pageEl.style.transform || "translate(0px, 0px)"; - } - return pageEl; - }).filter(Boolean); + this.selectedPageElements = window.selectedPages + .map((index) => { + const pageEl = document.getElementById(`page-container-${index}`); + if (pageEl) { + pageEl.initialTransform = pageEl.style.transform || 'translate(0px, 0px)'; + } + return pageEl; + }) + .filter(Boolean); if (this.selectedPageElements.length === 0) return; this.pageDragging = true; - this.draggedImageEl = document.createElement("div"); - this.draggedImageEl.classList.add("multidrag"); + this.draggedImageEl = document.createElement('div'); + this.draggedImageEl.classList.add('multidrag'); this.draggedImageEl.textContent = `${this.selectedPageElements.length} ${window.translations.dragDropMessage}`; - this.draggedImageEl.style.visibility = "hidden"; + this.draggedImageEl.style.visibility = 'hidden'; + this.dragContainer.appendChild(this.draggedImageEl); } else { // Single-page drag logic this.pageDragging = true; this.draggedEl = div; - const img = div.querySelector("img"); - div.classList.add("drag-manager_dragging"); - - const imgEl = document.createElement("img"); - imgEl.classList.add("dragged-img"); + const img = div.querySelector('img'); + div.classList.add('drag-manager_dragging'); + div.classList.remove('moved-element', 'remove'); + const imgEl = document.createElement('img'); + imgEl.classList.add('dragged-img'); imgEl.src = img.src; - imgEl.style.visibility = "hidden"; - imgEl.style.transform = `rotate(${img.style.rotate === "" ? "0deg" : img.style.rotate}) translate(-50%, -50%)`; + imgEl.style.visibility = 'hidden'; + imgEl.style.transform = `rotate(${img.style.rotate === '' ? '0deg' : img.style.rotate}) translate(-50%, -50%)`; this.draggedImageEl = imgEl; this.dragContainer.appendChild(imgEl); } // Common setup for both modes - window.addEventListener("mouseup", this.stopDraggingPage); - window.addEventListener("mousemove", this.onDragEl); - this.wrapper.classList.add("drag-manager_dragging-container"); + window.addEventListener('mouseup', this.stopDraggingPage); + window.addEventListener('mousemove', this.onDragEl); + this.wrapper.classList.add('drag-manager_dragging-container'); this.wrapper.appendChild(this.endInsertionElement); } onDragEl(mouseEvent) { - const { clientX, clientY } = mouseEvent; + const {clientX, clientY} = mouseEvent; if (this.draggedImageEl) { - this.draggedImageEl.style.visibility = "visible"; + this.draggedImageEl.style.visibility = 'visible'; this.draggedImageEl.style.left = `${clientX}px`; this.draggedImageEl.style.top = `${clientY}px`; } } stopDraggingPage() { - window.removeEventListener("mousemove", this.onDragEl); - this.wrapper.classList.remove("drag-manager_dragging-container"); + window.removeEventListener('mousemove', this.onDragEl); + this.wrapper.classList.remove('drag-manager_dragging-container'); this.wrapper.removeChild(this.endInsertionElement); - window.removeEventListener("mouseup", this.stopDraggingPage); + window.removeEventListener('mouseup', this.stopDraggingPage); if (this.draggedImageEl) { this.dragContainer.removeChild(this.draggedImageEl); @@ -96,38 +100,56 @@ class DragDropManager { if (window.selectPage) { // Multi-page drop logic - if (!this.hoveredEl) { + if (!this.hoveredEl || !this.hoveredEl.classList.contains('page-container')) { this.selectedPageElements.forEach((pageEl) => { - pageEl.style.transform = pageEl.initialTransform || "translate(0px, 0px)"; - pageEl.classList.remove("drag-manager_dragging"); + pageEl.style.transform = pageEl.initialTransform || 'translate(0px, 0px)'; + pageEl.classList.remove('drag-manager_dragging'); + this.pageDragging = false; + return; }); } else { this.selectedPageElements.forEach((pageEl) => { - pageEl.classList.remove("drag-manager_dragging"); + pageEl.classList.remove('drag-manager_dragging'); if (this.hoveredEl === this.endInsertionElement) { this.movePageTo(pageEl); } else { this.movePageTo(pageEl, this.hoveredEl); } + pageEl.classList.remove('moved-element', 'remove'); + pageEl.classList.add('moved-element'); + setTimeout(() => { + pageEl.classList.add('remove'); + }, 2000); }); } this.selectedPageElements = []; - window.resetPages() + window.resetPages(); } else { // Single-page drop logic + if (!this.hoveredEl || !this.hoveredEl.classList.contains('page-container')) { + this.draggedEl.style.transform = this.draggedEl.initialTransform || 'translate(0px, 0px)'; + this.draggedEl.classList.remove('drag-manager_dragging'); + this.pageDragging = false; + return; + } if (!this.hoveredEl) return; - this.draggedEl.classList.remove("drag-manager_dragging"); + this.draggedEl.classList.remove('drag-manager_dragging'); if (this.hoveredEl === this.endInsertionElement) { this.movePageTo(this.draggedEl); } else { this.movePageTo(this.draggedEl, this.hoveredEl); } + this.draggedEl.classList.remove('moved-element', 'remove'); + this.draggedEl.classList.add('moved-element'); + setTimeout(() => { + this.draggedEl.classList.add('remove'); + }, 2000); } this.pageDragging = false; } - setActions({ movePageTo }) { + setActions({movePageTo}) { this.movePageTo = movePageTo; } @@ -140,18 +162,18 @@ class DragDropManager { const onMouseEnter = () => { if (this.pageDragging) { this.hoveredEl = div; - div.classList.add("drag-manager_draghover"); + div.classList.add('drag-manager_draghover'); } }; const onMouseLeave = () => { this.hoveredEl = undefined; - div.classList.remove("drag-manager_draghover"); + div.classList.remove('drag-manager_draghover'); }; - div.addEventListener("dragstart", onDragStart); - div.addEventListener("mouseenter", onMouseEnter); - div.addEventListener("mouseleave", onMouseLeave); + div.addEventListener('dragstart', onDragStart); + div.addEventListener('mouseenter', onMouseEnter); + div.addEventListener('mouseleave', onMouseLeave); return div; } diff --git a/src/main/resources/static/js/multitool/commands/move-page.js b/src/main/resources/static/js/multitool/commands/move-page.js index 6f7b185e..738af950 100644 --- a/src/main/resources/static/js/multitool/commands/move-page.js +++ b/src/main/resources/static/js/multitool/commands/move-page.js @@ -1,13 +1,7 @@ -import { Command } from "./command.js"; +import {Command} from './command.js'; export class AbstractMovePageCommand extends Command { - constructor( - startElement, - endElement, - pagesContainer, - pagesContainerWrapper, - scrollTo = false - ) { + constructor(startElement, endElement, pagesContainer, pagesContainerWrapper, scrollTo = false) { super(); this.pagesContainer = pagesContainer; @@ -25,7 +19,7 @@ export class AbstractMovePageCommand extends Command { execute() { // Check & remove page number elements here too if they exist because Firefox doesn't fire the relevant event on page move. - const pageNumberElement = this.startElement.querySelector(".page-number"); + const pageNumberElement = this.startElement.querySelector('.page-number'); if (pageNumberElement) { this.startElement.removeChild(pageNumberElement); } @@ -38,11 +32,8 @@ export class AbstractMovePageCommand extends Command { } if (this.scrollTo) { - const { width } = this.startElement.getBoundingClientRect(); - const vector = - this.endIndex !== -1 && this.startIndex > this.endIndex - ? 0 - width - : width; + const {width} = this.startElement.getBoundingClientRect(); + const vector = this.endIndex !== -1 && this.startIndex > this.endIndex ? 0 - width : width; this.pagesContainerWrapper.scroll({ left: this.pagesContainerWrapper.scrollLeft + vector, @@ -52,7 +43,6 @@ export class AbstractMovePageCommand extends Command { undo() { // Requires overriding in child classes - } redo() { @@ -61,28 +51,19 @@ export class AbstractMovePageCommand extends Command { } export class MovePageUpCommand extends AbstractMovePageCommand { - constructor( - startElement, - endElement, - pagesContainer, - pagesContainerWrapper, - scrollTo = false - ) { + constructor(startElement, endElement, pagesContainer, pagesContainerWrapper, scrollTo = false) { super(startElement, endElement, pagesContainer, pagesContainerWrapper, scrollTo); } undo() { if (this.endElement) { this.pagesContainer.removeChild(this.endElement); - this.startElement.insertAdjacentElement("beforebegin", this.endElement); + this.startElement.insertAdjacentElement('beforebegin', this.endElement); } if (this.scrollTo) { - const { width } = this.startElement.getBoundingClientRect(); - const vector = - this.endIndex === -1 || this.startIndex <= this.endIndex - ? 0 - width - : width; + const {width} = this.startElement.getBoundingClientRect(); + const vector = this.endIndex === -1 || this.startIndex <= this.endIndex ? 0 - width : width; this.pagesContainerWrapper.scroll({ left: this.pagesContainerWrapper.scrollLeft - vector, @@ -96,13 +77,7 @@ export class MovePageUpCommand extends AbstractMovePageCommand { } export class MovePageDownCommand extends AbstractMovePageCommand { - constructor( - startElement, - endElement, - pagesContainer, - pagesContainerWrapper, - scrollTo = false - ) { + constructor(startElement, endElement, pagesContainer, pagesContainerWrapper, scrollTo = false) { super(startElement, endElement, pagesContainer, pagesContainerWrapper, scrollTo); } @@ -111,15 +86,12 @@ export class MovePageDownCommand extends AbstractMovePageCommand { if (this.startElement) { this.pagesContainer.removeChild(this.startElement); - previousElement.insertAdjacentElement("beforebegin", this.startElement); + previousElement.insertAdjacentElement('beforebegin', this.startElement); } if (this.scrollTo) { - const { width } = this.startElement.getBoundingClientRect(); - const vector = - this.endIndex === -1 || this.startIndex <= this.endIndex - ? 0 - width - : width; + const {width} = this.startElement.getBoundingClientRect(); + const vector = this.endIndex === -1 || this.startIndex <= this.endIndex ? 0 - width : width; this.pagesContainerWrapper.scroll({ left: this.pagesContainerWrapper.scrollLeft - vector, From 88079ddf7f46afd361dcf6b3f1e92fc39ba5f858 Mon Sep 17 00:00:00 2001 From: Reece Browne Date: Mon, 16 Dec 2024 19:37:15 +0000 Subject: [PATCH 2/3] Reduce opacity of dragged page on multidrag not just single --- src/main/resources/static/css/dragdrop.css | 2 +- src/main/resources/static/js/multitool/DragDropManager.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/resources/static/css/dragdrop.css b/src/main/resources/static/css/dragdrop.css index 560834e6..38da967f 100644 --- a/src/main/resources/static/css/dragdrop.css +++ b/src/main/resources/static/css/dragdrop.css @@ -29,7 +29,7 @@ } .drag-manager_dragging { - opacity: 0.5; + opacity: 0.2; } diff --git a/src/main/resources/static/js/multitool/DragDropManager.js b/src/main/resources/static/js/multitool/DragDropManager.js index 62093c11..251b5e81 100644 --- a/src/main/resources/static/js/multitool/DragDropManager.js +++ b/src/main/resources/static/js/multitool/DragDropManager.js @@ -41,6 +41,7 @@ class DragDropManager { const pageEl = document.getElementById(`page-container-${index}`); if (pageEl) { pageEl.initialTransform = pageEl.style.transform || 'translate(0px, 0px)'; + pageEl.classList.add('drag-manager_dragging'); } return pageEl; }) From 2bdda9acba6cf31759b7fcacedfafcc770e953ff Mon Sep 17 00:00:00 2001 From: Reece Browne Date: Tue, 17 Dec 2024 13:47:44 +0000 Subject: [PATCH 3/3] Fix drop on self bug Fix highlight fadeout bug UI tweaks --- src/main/resources/static/css/dragdrop.css | 12 ++-- .../static/js/multitool/DragDropManager.js | 56 +++++++++++++------ 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/src/main/resources/static/css/dragdrop.css b/src/main/resources/static/css/dragdrop.css index 38da967f..688cbca4 100644 --- a/src/main/resources/static/css/dragdrop.css +++ b/src/main/resources/static/css/dragdrop.css @@ -85,12 +85,12 @@ html[dir="rtl"] .drag-manager_draghover img { width: 80px; } } -.moved-element { - background-color: darkgreen; - border-radius: 8px; - transition: background-color 0.5s ease-out; +.moved-element img { + border: 8px solid #198754; + border-radius: 3px; + transition: border 0.5s ease-out; } -.moved-element.remove { - background-color: transparent; +.moved-element.remove img{ + border: 8px solid transparent; } diff --git a/src/main/resources/static/js/multitool/DragDropManager.js b/src/main/resources/static/js/multitool/DragDropManager.js index 251b5e81..747a7a3d 100644 --- a/src/main/resources/static/js/multitool/DragDropManager.js +++ b/src/main/resources/static/js/multitool/DragDropManager.js @@ -8,6 +8,7 @@ class DragDropManager { this.draggedImageEl = undefined; this.draggedEl = undefined; this.selectedPageElements = []; // Store selected pages for multi-page mode + this.elementTimeouts = new Map(); // Add CSS dynamically const styleElement = document.createElement('link'); @@ -101,55 +102,78 @@ class DragDropManager { if (window.selectPage) { // Multi-page drop logic - if (!this.hoveredEl || !this.hoveredEl.classList.contains('page-container')) { + if ( + !this.hoveredEl || + !this.hoveredEl.classList.contains('page-container') || + this.selectedPageElements.includes(this.hoveredEl) + ) { this.selectedPageElements.forEach((pageEl) => { pageEl.style.transform = pageEl.initialTransform || 'translate(0px, 0px)'; pageEl.classList.remove('drag-manager_dragging'); - this.pageDragging = false; - return; }); } else { this.selectedPageElements.forEach((pageEl) => { pageEl.classList.remove('drag-manager_dragging'); + if (this.hoveredEl === this.endInsertionElement) { this.movePageTo(pageEl); } else { this.movePageTo(pageEl, this.hoveredEl); } - pageEl.classList.remove('moved-element', 'remove'); - pageEl.classList.add('moved-element'); - setTimeout(() => { - pageEl.classList.add('remove'); - }, 2000); + + // Handle timeout for the current element + this.handleTimeoutForElement(pageEl); }); } this.selectedPageElements = []; window.resetPages(); } else { // Single-page drop logic - if (!this.hoveredEl || !this.hoveredEl.classList.contains('page-container')) { + if ( + !this.hoveredEl || + !this.hoveredEl.classList.contains('page-container') || + this.hoveredEl === this.draggedEl + ) { this.draggedEl.style.transform = this.draggedEl.initialTransform || 'translate(0px, 0px)'; this.draggedEl.classList.remove('drag-manager_dragging'); - this.pageDragging = false; return; } - if (!this.hoveredEl) return; + this.draggedEl.classList.remove('drag-manager_dragging'); + if (this.hoveredEl === this.endInsertionElement) { this.movePageTo(this.draggedEl); } else { this.movePageTo(this.draggedEl, this.hoveredEl); } - this.draggedEl.classList.remove('moved-element', 'remove'); - this.draggedEl.classList.add('moved-element'); - setTimeout(() => { - this.draggedEl.classList.add('remove'); - }, 2000); + + // Handle timeout for the current element + this.handleTimeoutForElement(this.draggedEl); } this.pageDragging = false; } + // Helper function to manage independent timeouts + handleTimeoutForElement(element) { + // Clear existing timeout if present + if (this.elementTimeouts.has(element)) { + clearTimeout(this.elementTimeouts.get(element)); + } + + // Add the moved-element class and set a new timeout + element.classList.remove('remove'); + element.classList.add('moved-element'); + + const timeoutId = setTimeout(() => { + element.classList.add('remove'); + this.elementTimeouts.delete(element); // Cleanup the timeout map + }, 2000); + + // Store the timeout ID for this element + this.elementTimeouts.set(element, timeoutId); + } + setActions({movePageTo}) { this.movePageTo = movePageTo; }