Fix drag and drop bugs and clean up UI

This commit is contained in:
Reece Browne 2024-12-16 19:07:15 +00:00
parent 0a6b6453b2
commit 9b6dcdcd06
3 changed files with 97 additions and 94 deletions

View File

@ -29,13 +29,9 @@
} }
.drag-manager_dragging { .drag-manager_dragging {
width: 0px; opacity: 0.5;
visibility: hidden;
} }
.drag-manager_draghover {
width: 375px !important;
}
.drag-manager_draghover .insert-file-button-container { .drag-manager_draghover .insert-file-button-container {
display: none !important; display: none !important;
@ -46,11 +42,11 @@
} }
html[dir="ltr"] .drag-manager_draghover img { html[dir="ltr"] .drag-manager_draghover img {
left: calc(50% + 62.5px) !important; left: 80% !important;
} }
html[dir="rtl"] .drag-manager_draghover img { html[dir="rtl"] .drag-manager_draghover img {
left: 125px; left: 25px;
} }
.drag-manager_dragging-container .hide-on-drag { .drag-manager_dragging-container .hide-on-drag {
@ -58,14 +54,17 @@ html[dir="rtl"] .drag-manager_draghover img {
} }
.drag-manager_endpoint { .drag-manager_endpoint {
width: 80px; width: 150px;
height: 100%; height: 250px;
background-color: #ffffff10; background-color: #ffffff10;
transition: width 0.1s; transition: width 0.1s;
animation: end-drop-expand 0.3s ease; animation: end-drop-expand 0.3s ease;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
margin-left:16px;
border-radius: 8px;
} }
.drag-manager_endpoint svg { .drag-manager_endpoint svg {
@ -74,7 +73,8 @@ html[dir="rtl"] .drag-manager_draghover img {
} }
.drag-manager_endpoint.drag-manager_draghover { .drag-manager_endpoint.drag-manager_draghover {
width: 150px !important; width: 180px !important;
border: 2px solid darkgreen;
} }
@keyframes end-drop-expand { @keyframes end-drop-expand {
@ -85,3 +85,12 @@ html[dir="rtl"] .drag-manager_draghover img {
width: 80px; width: 80px;
} }
} }
.moved-element {
background-color: darkgreen;
border-radius: 8px;
transition: background-color 0.5s ease-out;
}
.moved-element.remove {
background-color: transparent;
}

View File

@ -1,7 +1,7 @@
class DragDropManager { class DragDropManager {
constructor(id, wrapperId) { constructor(id, wrapperId) {
this.dragContainer = document.getElementById(id); this.dragContainer = document.getElementById(id);
this.pageDirection = document.documentElement.getAttribute("dir"); this.pageDirection = document.documentElement.getAttribute('dir');
this.wrapper = document.getElementById(wrapperId); this.wrapper = document.getElementById(wrapperId);
this.pageDragging = false; this.pageDragging = false;
this.hoveredEl = undefined; this.hoveredEl = undefined;
@ -10,14 +10,15 @@ class DragDropManager {
this.selectedPageElements = []; // Store selected pages for multi-page mode this.selectedPageElements = []; // Store selected pages for multi-page mode
// Add CSS dynamically // Add CSS dynamically
const styleElement = document.createElement("link"); const styleElement = document.createElement('link');
styleElement.rel = "stylesheet"; styleElement.rel = 'stylesheet';
styleElement.href = "css/dragdrop.css"; styleElement.href = 'css/dragdrop.css';
document.head.appendChild(styleElement); document.head.appendChild(styleElement);
// Create the endpoint element // Create the endpoint element
const div = document.createElement("div"); const div = document.createElement('div');
div.classList.add("drag-manager_endpoint"); div.classList.add('page-container');
div.classList.add('drag-manager_endpoint');
div.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-earmark-arrow-down" viewBox="0 0 16 16"> div.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-earmark-arrow-down" viewBox="0 0 16 16">
<path d="M8.5 6.5a.5.5 0 0 0-1 0v3.793L6.354 9.146a.5.5 0 1 0-.708.708l2 2a.5.5 0 0 0 .708 0l2-2a.5.5 0 0 0-.708-.708L8.5 10.293V6.5z"/> <path d="M8.5 6.5a.5.5 0 0 0-1 0v3.793L6.354 9.146a.5.5 0 1 0-.708.708l2 2a.5.5 0 0 0 .708 0l2-2a.5.5 0 0 0-.708-.708L8.5 10.293V6.5z"/>
<path d="M14 14V4.5L9.5 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2zM9.5 3A1.5 1.5 0 0 0 11 4.5h2V14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h5.5v2z"/> <path d="M14 14V4.5L9.5 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2zM9.5 3A1.5 1.5 0 0 0 11 4.5h2V14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h5.5v2z"/>
@ -35,59 +36,62 @@ class DragDropManager {
startDraggingPage(div) { startDraggingPage(div) {
if (window.selectPage) { if (window.selectPage) {
// Multi-page drag logic // Multi-page drag logic
this.selectedPageElements = window.selectedPages.map((index) => { this.selectedPageElements = window.selectedPages
const pageEl = document.getElementById(`page-container-${index}`); .map((index) => {
if (pageEl) { const pageEl = document.getElementById(`page-container-${index}`);
pageEl.initialTransform = pageEl.style.transform || "translate(0px, 0px)"; if (pageEl) {
} pageEl.initialTransform = pageEl.style.transform || 'translate(0px, 0px)';
return pageEl; }
}).filter(Boolean); return pageEl;
})
.filter(Boolean);
if (this.selectedPageElements.length === 0) return; if (this.selectedPageElements.length === 0) return;
this.pageDragging = true; this.pageDragging = true;
this.draggedImageEl = document.createElement("div"); this.draggedImageEl = document.createElement('div');
this.draggedImageEl.classList.add("multidrag"); this.draggedImageEl.classList.add('multidrag');
this.draggedImageEl.textContent = `${this.selectedPageElements.length} ${window.translations.dragDropMessage}`; this.draggedImageEl.textContent = `${this.selectedPageElements.length} ${window.translations.dragDropMessage}`;
this.draggedImageEl.style.visibility = "hidden"; this.draggedImageEl.style.visibility = 'hidden';
this.dragContainer.appendChild(this.draggedImageEl); this.dragContainer.appendChild(this.draggedImageEl);
} else { } else {
// Single-page drag logic // Single-page drag logic
this.pageDragging = true; this.pageDragging = true;
this.draggedEl = div; this.draggedEl = div;
const img = div.querySelector("img"); const img = div.querySelector('img');
div.classList.add("drag-manager_dragging"); div.classList.add('drag-manager_dragging');
div.classList.remove('moved-element', 'remove');
const imgEl = document.createElement("img"); const imgEl = document.createElement('img');
imgEl.classList.add("dragged-img"); imgEl.classList.add('dragged-img');
imgEl.src = img.src; imgEl.src = img.src;
imgEl.style.visibility = "hidden"; imgEl.style.visibility = 'hidden';
imgEl.style.transform = `rotate(${img.style.rotate === "" ? "0deg" : img.style.rotate}) translate(-50%, -50%)`; imgEl.style.transform = `rotate(${img.style.rotate === '' ? '0deg' : img.style.rotate}) translate(-50%, -50%)`;
this.draggedImageEl = imgEl; this.draggedImageEl = imgEl;
this.dragContainer.appendChild(imgEl); this.dragContainer.appendChild(imgEl);
} }
// Common setup for both modes // Common setup for both modes
window.addEventListener("mouseup", this.stopDraggingPage); window.addEventListener('mouseup', this.stopDraggingPage);
window.addEventListener("mousemove", this.onDragEl); window.addEventListener('mousemove', this.onDragEl);
this.wrapper.classList.add("drag-manager_dragging-container"); this.wrapper.classList.add('drag-manager_dragging-container');
this.wrapper.appendChild(this.endInsertionElement); this.wrapper.appendChild(this.endInsertionElement);
} }
onDragEl(mouseEvent) { onDragEl(mouseEvent) {
const { clientX, clientY } = mouseEvent; const {clientX, clientY} = mouseEvent;
if (this.draggedImageEl) { if (this.draggedImageEl) {
this.draggedImageEl.style.visibility = "visible"; this.draggedImageEl.style.visibility = 'visible';
this.draggedImageEl.style.left = `${clientX}px`; this.draggedImageEl.style.left = `${clientX}px`;
this.draggedImageEl.style.top = `${clientY}px`; this.draggedImageEl.style.top = `${clientY}px`;
} }
} }
stopDraggingPage() { stopDraggingPage() {
window.removeEventListener("mousemove", this.onDragEl); window.removeEventListener('mousemove', this.onDragEl);
this.wrapper.classList.remove("drag-manager_dragging-container"); this.wrapper.classList.remove('drag-manager_dragging-container');
this.wrapper.removeChild(this.endInsertionElement); this.wrapper.removeChild(this.endInsertionElement);
window.removeEventListener("mouseup", this.stopDraggingPage); window.removeEventListener('mouseup', this.stopDraggingPage);
if (this.draggedImageEl) { if (this.draggedImageEl) {
this.dragContainer.removeChild(this.draggedImageEl); this.dragContainer.removeChild(this.draggedImageEl);
@ -96,38 +100,56 @@ class DragDropManager {
if (window.selectPage) { if (window.selectPage) {
// Multi-page drop logic // Multi-page drop logic
if (!this.hoveredEl) { if (!this.hoveredEl || !this.hoveredEl.classList.contains('page-container')) {
this.selectedPageElements.forEach((pageEl) => { this.selectedPageElements.forEach((pageEl) => {
pageEl.style.transform = pageEl.initialTransform || "translate(0px, 0px)"; pageEl.style.transform = pageEl.initialTransform || 'translate(0px, 0px)';
pageEl.classList.remove("drag-manager_dragging"); pageEl.classList.remove('drag-manager_dragging');
this.pageDragging = false;
return;
}); });
} else { } else {
this.selectedPageElements.forEach((pageEl) => { this.selectedPageElements.forEach((pageEl) => {
pageEl.classList.remove("drag-manager_dragging"); pageEl.classList.remove('drag-manager_dragging');
if (this.hoveredEl === this.endInsertionElement) { if (this.hoveredEl === this.endInsertionElement) {
this.movePageTo(pageEl); this.movePageTo(pageEl);
} else { } else {
this.movePageTo(pageEl, this.hoveredEl); this.movePageTo(pageEl, this.hoveredEl);
} }
pageEl.classList.remove('moved-element', 'remove');
pageEl.classList.add('moved-element');
setTimeout(() => {
pageEl.classList.add('remove');
}, 2000);
}); });
} }
this.selectedPageElements = []; this.selectedPageElements = [];
window.resetPages() window.resetPages();
} else { } else {
// Single-page drop logic // 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; if (!this.hoveredEl) return;
this.draggedEl.classList.remove("drag-manager_dragging"); this.draggedEl.classList.remove('drag-manager_dragging');
if (this.hoveredEl === this.endInsertionElement) { if (this.hoveredEl === this.endInsertionElement) {
this.movePageTo(this.draggedEl); this.movePageTo(this.draggedEl);
} else { } else {
this.movePageTo(this.draggedEl, this.hoveredEl); 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; this.pageDragging = false;
} }
setActions({ movePageTo }) { setActions({movePageTo}) {
this.movePageTo = movePageTo; this.movePageTo = movePageTo;
} }
@ -140,18 +162,18 @@ class DragDropManager {
const onMouseEnter = () => { const onMouseEnter = () => {
if (this.pageDragging) { if (this.pageDragging) {
this.hoveredEl = div; this.hoveredEl = div;
div.classList.add("drag-manager_draghover"); div.classList.add('drag-manager_draghover');
} }
}; };
const onMouseLeave = () => { const onMouseLeave = () => {
this.hoveredEl = undefined; this.hoveredEl = undefined;
div.classList.remove("drag-manager_draghover"); div.classList.remove('drag-manager_draghover');
}; };
div.addEventListener("dragstart", onDragStart); div.addEventListener('dragstart', onDragStart);
div.addEventListener("mouseenter", onMouseEnter); div.addEventListener('mouseenter', onMouseEnter);
div.addEventListener("mouseleave", onMouseLeave); div.addEventListener('mouseleave', onMouseLeave);
return div; return div;
} }

View File

@ -1,13 +1,7 @@
import { Command } from "./command.js"; import {Command} from './command.js';
export class AbstractMovePageCommand extends Command { export class AbstractMovePageCommand extends Command {
constructor( constructor(startElement, endElement, pagesContainer, pagesContainerWrapper, scrollTo = false) {
startElement,
endElement,
pagesContainer,
pagesContainerWrapper,
scrollTo = false
) {
super(); super();
this.pagesContainer = pagesContainer; this.pagesContainer = pagesContainer;
@ -25,7 +19,7 @@ export class AbstractMovePageCommand extends Command {
execute() { execute() {
// Check & remove page number elements here too if they exist because Firefox doesn't fire the relevant event on page move. // 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) { if (pageNumberElement) {
this.startElement.removeChild(pageNumberElement); this.startElement.removeChild(pageNumberElement);
} }
@ -38,11 +32,8 @@ export class AbstractMovePageCommand extends Command {
} }
if (this.scrollTo) { if (this.scrollTo) {
const { width } = this.startElement.getBoundingClientRect(); const {width} = this.startElement.getBoundingClientRect();
const vector = const vector = this.endIndex !== -1 && this.startIndex > this.endIndex ? 0 - width : width;
this.endIndex !== -1 && this.startIndex > this.endIndex
? 0 - width
: width;
this.pagesContainerWrapper.scroll({ this.pagesContainerWrapper.scroll({
left: this.pagesContainerWrapper.scrollLeft + vector, left: this.pagesContainerWrapper.scrollLeft + vector,
@ -52,7 +43,6 @@ export class AbstractMovePageCommand extends Command {
undo() { undo() {
// Requires overriding in child classes // Requires overriding in child classes
} }
redo() { redo() {
@ -61,28 +51,19 @@ export class AbstractMovePageCommand extends Command {
} }
export class MovePageUpCommand extends AbstractMovePageCommand { export class MovePageUpCommand extends AbstractMovePageCommand {
constructor( constructor(startElement, endElement, pagesContainer, pagesContainerWrapper, scrollTo = false) {
startElement,
endElement,
pagesContainer,
pagesContainerWrapper,
scrollTo = false
) {
super(startElement, endElement, pagesContainer, pagesContainerWrapper, scrollTo); super(startElement, endElement, pagesContainer, pagesContainerWrapper, scrollTo);
} }
undo() { undo() {
if (this.endElement) { if (this.endElement) {
this.pagesContainer.removeChild(this.endElement); this.pagesContainer.removeChild(this.endElement);
this.startElement.insertAdjacentElement("beforebegin", this.endElement); this.startElement.insertAdjacentElement('beforebegin', this.endElement);
} }
if (this.scrollTo) { if (this.scrollTo) {
const { width } = this.startElement.getBoundingClientRect(); const {width} = this.startElement.getBoundingClientRect();
const vector = const vector = this.endIndex === -1 || this.startIndex <= this.endIndex ? 0 - width : width;
this.endIndex === -1 || this.startIndex <= this.endIndex
? 0 - width
: width;
this.pagesContainerWrapper.scroll({ this.pagesContainerWrapper.scroll({
left: this.pagesContainerWrapper.scrollLeft - vector, left: this.pagesContainerWrapper.scrollLeft - vector,
@ -96,13 +77,7 @@ export class MovePageUpCommand extends AbstractMovePageCommand {
} }
export class MovePageDownCommand extends AbstractMovePageCommand { export class MovePageDownCommand extends AbstractMovePageCommand {
constructor( constructor(startElement, endElement, pagesContainer, pagesContainerWrapper, scrollTo = false) {
startElement,
endElement,
pagesContainer,
pagesContainerWrapper,
scrollTo = false
) {
super(startElement, endElement, pagesContainer, pagesContainerWrapper, scrollTo); super(startElement, endElement, pagesContainer, pagesContainerWrapper, scrollTo);
} }
@ -111,15 +86,12 @@ export class MovePageDownCommand extends AbstractMovePageCommand {
if (this.startElement) { if (this.startElement) {
this.pagesContainer.removeChild(this.startElement); this.pagesContainer.removeChild(this.startElement);
previousElement.insertAdjacentElement("beforebegin", this.startElement); previousElement.insertAdjacentElement('beforebegin', this.startElement);
} }
if (this.scrollTo) { if (this.scrollTo) {
const { width } = this.startElement.getBoundingClientRect(); const {width} = this.startElement.getBoundingClientRect();
const vector = const vector = this.endIndex === -1 || this.startIndex <= this.endIndex ? 0 - width : width;
this.endIndex === -1 || this.startIndex <= this.endIndex
? 0 - width
: width;
this.pagesContainerWrapper.scroll({ this.pagesContainerWrapper.scroll({
left: this.pagesContainerWrapper.scrollLeft - vector, left: this.pagesContainerWrapper.scrollLeft - vector,