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

View File

@ -35,8 +35,7 @@ class PdfActionsManager {
const sibling = imgContainer.previousSibling; const sibling = imgContainer.previousSibling;
if (sibling) { if (sibling) {
let movePageCommand = this.movePageTo(imgContainer, sibling, true, true); this.movePageTo(imgContainer, sibling, true);
this._pushUndoClearRedo(movePageCommand);
} }
} }
@ -44,12 +43,11 @@ class PdfActionsManager {
var imgContainer = this.getPageContainer(e.target); var imgContainer = this.getPageContainer(e.target);
const sibling = imgContainer.nextSibling; const sibling = imgContainer.nextSibling;
if (sibling) { if (sibling) {
let movePageCommand = this.movePageTo( this.movePageTo(
imgContainer, imgContainer,
sibling.nextSibling, sibling.nextSibling,
true true
); );
this._pushUndoClearRedo(movePageCommand);
} }
} }
@ -185,8 +183,6 @@ class PdfActionsManager {
const pageNumber = Array.from(div.parentNode.children).indexOf(div) + 1; const pageNumber = Array.from(div.parentNode.children).indexOf(div) + 1;
let selectPageCommand = new SelectPageCommand(pageNumber, selectCheckbox); let selectPageCommand = new SelectPageCommand(pageNumber, selectCheckbox);
selectPageCommand.execute(); selectPageCommand.execute();
this._pushUndoClearRedo(selectPageCommand);
}; };
const insertFileButtonContainer = document.createElement("div"); 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 { RemoveSelectedCommand } from './commands/remove.js';
import { RotateAllCommand, RotateElementCommand } from './commands/rotate.js'; import { RotateAllCommand, RotateElementCommand } from './commands/rotate.js';
import { SplitAllCommand } from './commands/split.js'; import { SplitAllCommand } from './commands/split.js';
@ -6,6 +6,7 @@ import { UndoManager } from './UndoManager.js';
import { PageBreakCommand } from './commands/page-break.js'; import { PageBreakCommand } from './commands/page-break.js';
import { AddFilesCommand } from './commands/add-page.js'; import { AddFilesCommand } from './commands/add-page.js';
import { DecryptFile } from '../DecryptFiles.js'; import { DecryptFile } from '../DecryptFiles.js';
import { CommandSequence } from './commands/commands-sequence.js';
class PdfContainer { class PdfContainer {
fileName; fileName;
@ -109,27 +110,41 @@ class PdfContainer {
downloadBtn.disabled = true; downloadBtn.disabled = true;
} }
movePageTo(startElement, endElement, scrollTo = false, moveUp = false) { movePagesTo(startElements, endElement, scrollTo = false) {
let movePageCommand; let commands = [];
if (moveUp) { startElements.forEach((page) => {
movePageCommand = new MovePageUpCommand( let command = new MovePageCommand(
startElement, page,
endElement, endElement,
this.pagesContainer, this.pagesContainer,
this.pagesContainerWrapper, this.pagesContainerWrapper,
scrollTo scrollTo
); )
} else { command.execute();
movePageCommand = new MovePageDownCommand( commands.push(command);
startElement, })
endElement,
this.pagesContainer, let commandSequence = new CommandSequence(commands);
this.pagesContainerWrapper, this.undoManager.pushUndoClearRedo(commandSequence);
scrollTo 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(); movePageCommand.execute();
this.undoManager.pushUndoClearRedo(movePageCommand);
return movePageCommand; return movePageCommand;
} }

View File

@ -34,12 +34,10 @@ export class AddFilesCommand extends Command {
if (this.pagesContainer.childElementCount === 0) { if (this.pagesContainer.childElementCount === 0) {
const filenameInput = document.getElementById('filename-input'); const filenameInput = document.getElementById('filename-input');
const filenameParagraph = document.getElementById('filename');
const downloadBtn = document.getElementById('export-button'); const downloadBtn = document.getElementById('export-button');
filenameInput.disabled = true; filenameInput.disabled = true;
filenameInput.value = ''; filenameInput.value = '';
filenameParagraph.innerText = '';
downloadBtn.disabled = true; 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'; import {Command} from './command.js';
export class AbstractMovePageCommand extends Command { export class MovePageCommand extends Command {
constructor(startElement, endElement, pagesContainer, pagesContainerWrapper, scrollTo = false) { constructor(startElement, endElement, pagesContainer, pagesContainerWrapper, scrollTo = false) {
super(); super();
@ -16,7 +16,6 @@ export class AbstractMovePageCommand extends Command {
this.scrollTo = scrollTo; this.scrollTo = scrollTo;
this.pagesContainerWrapper = pagesContainerWrapper; this.pagesContainerWrapper = pagesContainerWrapper;
} }
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');
@ -42,51 +41,11 @@ export class AbstractMovePageCommand extends Command {
} }
undo() { 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) { if (this.startElement) {
this.pagesContainer.removeChild(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) { if (this.scrollTo) {