2926 bug undo feature of multitool delete your upload file (#3101)

# Description of Changes

Please provide a summary of the changes, including:

- What was changed
Added support for single page and multi page undo/redo drag and drop in
multitool
removed selecting pages from undo/redo stack

- Why the change was made
Drag and drop was not supported fully with undo/redo functionality


Closes #(2926)

---

## [Checklist]


### General

- [X] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [X] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [X] I have performed a self-review of my code
- [X] My changes generate no new warnings

### Documentation

### UI Changes

I Will start a demo in PR so people can try the new functionality

[numberedpages.pdf](https://github.com/user-attachments/files/19043978/numberedpages.pdf)
Please feel free to use this PDF to aid with trying out the new
functionality

### Testing (if applicable)


- [X] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
This commit is contained in:
ConnorYoh 2025-03-05 08:40:03 +00:00 committed by GitHub
parent 58edc777c0
commit e6abffe1a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 64 additions and 74 deletions

View File

@ -39,7 +39,7 @@ class DragDropManager {
// Multi-page drag logic
this.selectedPageElements = window.selectedPages
.map((index) => {
const pageEl = document.getElementById(`page-container-${index}`);
const pageEl = Array.from(this.wrapper.childNodes)[index];
if (pageEl) {
pageEl.initialTransform = pageEl.style.transform || 'translate(0px, 0px)';
pageEl.classList.add('drag-manager_dragging');
@ -114,13 +114,15 @@ class DragDropManager {
} 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);
}
this.movePageTo(
this.selectedPageElements,
this.hoveredEl === this.endInsertionElement
? null
: this.hoveredEl);
this.selectedPageElements.forEach((pageEl) => {
// Handle timeout for the current element
this.handleTimeoutForElement(pageEl);
});

View File

@ -35,8 +35,7 @@ class PdfActionsManager {
const sibling = imgContainer.previousSibling;
if (sibling) {
let movePageCommand = this.movePageTo(imgContainer, sibling, true, true);
this._pushUndoClearRedo(movePageCommand);
this.movePageTo(imgContainer, sibling, true);
}
}
@ -44,12 +43,11 @@ class PdfActionsManager {
var imgContainer = this.getPageContainer(e.target);
const sibling = imgContainer.nextSibling;
if (sibling) {
let movePageCommand = this.movePageTo(
this.movePageTo(
imgContainer,
sibling.nextSibling,
true
);
this._pushUndoClearRedo(movePageCommand);
}
}
@ -185,8 +183,6 @@ class PdfActionsManager {
const pageNumber = Array.from(div.parentNode.children).indexOf(div) + 1;
let selectPageCommand = new SelectPageCommand(pageNumber, selectCheckbox);
selectPageCommand.execute();
this._pushUndoClearRedo(selectPageCommand);
};
const insertFileButtonContainer = document.createElement("div");

View File

@ -1,4 +1,4 @@
import { MovePageUpCommand, MovePageDownCommand } from './commands/move-page.js';
import { MovePageCommand } from './commands/move-page.js';
import { RemoveSelectedCommand } from './commands/remove.js';
import { RotateAllCommand, RotateElementCommand } from './commands/rotate.js';
import { SplitAllCommand } from './commands/split.js';
@ -6,6 +6,7 @@ import { UndoManager } from './UndoManager.js';
import { PageBreakCommand } from './commands/page-break.js';
import { AddFilesCommand } from './commands/add-page.js';
import { DecryptFile } from '../DecryptFiles.js';
import { CommandSequence } from './commands/commands-sequence.js';
class PdfContainer {
fileName;
@ -109,27 +110,41 @@ class PdfContainer {
downloadBtn.disabled = true;
}
movePageTo(startElement, endElement, scrollTo = false, moveUp = false) {
let movePageCommand;
if (moveUp) {
movePageCommand = new MovePageUpCommand(
startElement,
movePagesTo(startElements, endElement, scrollTo = false) {
let commands = [];
startElements.forEach((page) => {
let command = new MovePageCommand(
page,
endElement,
this.pagesContainer,
this.pagesContainerWrapper,
scrollTo
);
} else {
movePageCommand = new MovePageDownCommand(
startElement,
endElement,
this.pagesContainer,
this.pagesContainerWrapper,
scrollTo
);
)
command.execute();
commands.push(command);
})
let commandSequence = new CommandSequence(commands);
this.undoManager.pushUndoClearRedo(commandSequence);
return commandSequence;
}
movePageTo(startElements, endElement, scrollTo = false) {
if (Array.isArray(startElements)){
return this.movePagesTo(startElements, endElement, scrollTo = false);
}
let movePageCommand = new MovePageCommand(
startElements,
endElement,
this.pagesContainer,
this.pagesContainerWrapper,
scrollTo
);
movePageCommand.execute();
this.undoManager.pushUndoClearRedo(movePageCommand);
return movePageCommand;
}

View File

@ -34,12 +34,10 @@ export class AddFilesCommand extends Command {
if (this.pagesContainer.childElementCount === 0) {
const filenameInput = document.getElementById('filename-input');
const filenameParagraph = document.getElementById('filename');
const downloadBtn = document.getElementById('export-button');
filenameInput.disabled = true;
filenameInput.value = '';
filenameParagraph.innerText = '';
downloadBtn.disabled = true;
}

View File

@ -0,0 +1,20 @@
import {Command} from './command.js';
export class CommandSequence extends Command {
constructor(commands) {
super();
this.commands = commands;
}
execute() {
this.commands.forEach((command) => command.execute())
}
undo() {
this.commands.slice().reverse().forEach((command) => command.undo())
}
redo() {
this.execute();
}
}

View File

@ -1,6 +1,6 @@
import {Command} from './command.js';
export class AbstractMovePageCommand extends Command {
export class MovePageCommand extends Command {
constructor(startElement, endElement, pagesContainer, pagesContainerWrapper, scrollTo = false) {
super();
@ -16,7 +16,6 @@ export class AbstractMovePageCommand extends Command {
this.scrollTo = scrollTo;
this.pagesContainerWrapper = pagesContainerWrapper;
}
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');
@ -42,51 +41,11 @@ export class AbstractMovePageCommand extends Command {
}
undo() {
// Requires overriding in child classes
}
redo() {
this.execute();
}
}
export class MovePageUpCommand extends AbstractMovePageCommand {
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);
}
if (this.scrollTo) {
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,
});
}
}
redo() {
this.execute();
}
}
export class MovePageDownCommand extends AbstractMovePageCommand {
constructor(startElement, endElement, pagesContainer, pagesContainerWrapper, scrollTo = false) {
super(startElement, endElement, pagesContainer, pagesContainerWrapper, scrollTo);
}
undo() {
let previousElement = this.startElement.previousSibling;
if (this.startElement) {
this.pagesContainer.removeChild(this.startElement);
previousElement.insertAdjacentElement('beforebegin', this.startElement);
let previousNeighbour = Array.from(this.pagesContainer.childNodes)[this.startIndex];
previousNeighbour?.insertAdjacentElement('beforebegin', this.startElement)
?? this.pagesContainer.append(this.startElement);
}
if (this.scrollTo) {