From 75db6c80aadbd893a98a98490cdc85eb0cbe874f Mon Sep 17 00:00:00 2001
From: ConnorYoh <40631091+ConnorYoh@users.noreply.github.com>
Date: Thu, 20 Mar 2025 00:06:47 +0000
Subject: [PATCH 01/26] 2385 feature request pdf multi tool to use new file
input box (#3201)
# Description of Changes
Please provide a summary of the changes, including:
- Multitool now makes use of the common file input.
- deleted multitool file input
- moved tool bar to floating at the bottom of the view window
Closes #(2385)
---
## Checklist
### General
- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings
### Documentation
- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)
### UI Changes (if applicable)
- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)
### Testing (if applicable)
- [ ] 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.
---
src/main/resources/static/css/multi-tool.css | 38 ++--
src/main/resources/static/js/fileInput.js | 9 +-
.../static/js/multitool/PdfContainer.js | 42 ++--
.../static/js/multitool/fileInput.js | 114 ----------
.../resources/templates/fragments/common.html | 4 +-
src/main/resources/templates/multi-tool.html | 201 +++++++++---------
6 files changed, 163 insertions(+), 245 deletions(-)
delete mode 100644 src/main/resources/static/js/multitool/fileInput.js
diff --git a/src/main/resources/static/css/multi-tool.css b/src/main/resources/static/css/multi-tool.css
index 99e15f231..a540b9d0a 100644
--- a/src/main/resources/static/css/multi-tool.css
+++ b/src/main/resources/static/css/multi-tool.css
@@ -14,26 +14,30 @@ label {
border-radius: 16px !important;
padding: 0.75rem;
border: 1px solid var(--theme-color-outline-variant);
+ flex-grow: 5;
}
.mt-action-bar {
display: flex;
gap: 10px;
align-items: start;
- background-color: var(--md-sys-color-surface-5);
border: none;
backdrop-filter: blur(2px);
top: 10px;
- z-index: 10;
+ z-index: 11;
padding: 1.25rem;
border-radius: 2rem;
margin: 0px 25px;
+ justify-content:center;
}
+
.mt-action-bar>* {
padding-bottom: 0.5rem;
}
-
+.mt-file-uploader {
+ width:100%
+}
.mt-action-bar svg,
.mt-action-btn svg {
width: 20px;
@@ -42,21 +46,29 @@ label {
.mt-action-bar .mt-filename {
width: 100%;
+ display: flex;
+ gap: 10px;
}
.mt-action-btn {
+ position: sticky;
+ bottom: 10%;
+ margin: auto;
+ margin-bottom: 25px;
+ border-radius: 2rem;
+ z-index: 12;
+ background-color: var(--md-sys-color-surface-container-low) ;
display: flex;
gap: 10px;
- align-items: start;
- top: 10px;
- z-index: 10;
padding: 12px 0px 0px;
- width: 100%;
+ width: fit-content;
+ justify-content: center;
+ padding: 10px 20px
}
.mt-action-btn .btn {
- width: 3rem;
- height: 3rem;
+ width: 3.5rem;
+ height: 3.5rem;
border-radius: 20px;
padding: 0;
}
@@ -64,7 +76,7 @@ label {
.bg-card {
background-color: var(--md-sys-color-surface-5);
border-radius: 3rem;
- padding: 25px 0 0;
+ padding: 25px 0;
}
#pages-container-wrapper {
@@ -73,7 +85,7 @@ label {
flex-direction: column;
padding: 1rem;
border-radius: 25px;
- overflow-y: auto;
+ overflow-y: clip;
overflow-x: auto;
min-height: 275px;
margin: 0 0 30px 0;
@@ -136,10 +148,6 @@ label {
display: none;
}
-/* Pushes the last item to the left */
-.page-container:last-child {
- margin-right: auto;
-}
.page-container:last-child:lang(ar),
/* Arabic */
diff --git a/src/main/resources/static/js/fileInput.js b/src/main/resources/static/js/fileInput.js
index 91ad372ce..9f27360f9 100644
--- a/src/main/resources/static/js/fileInput.js
+++ b/src/main/resources/static/js/fileInput.js
@@ -34,8 +34,10 @@ function setupFileInput(chooser) {
const filesSelected = chooser.getAttribute('data-bs-files-selected');
const pdfPrompt = chooser.getAttribute('data-bs-pdf-prompt');
const inputContainerId = chooser.getAttribute('data-bs-element-container-id');
+ const showUploads = chooser.getAttribute('data-bs-show-uploads') === "true";
let inputContainer = document.getElementById(inputContainerId);
+ const input = document.getElementById(elementId);
if (inputContainer.id === 'pdf-upload-input-container') {
inputContainer.querySelector('#dragAndDrop').innerHTML = window.fileInput.dragAndDropPDF;
@@ -46,6 +48,11 @@ function setupFileInput(chooser) {
let overlay;
let dragCounter = 0;
+ input.addEventListener('reset', (e) => {
+ allFiles = [];
+ input.value = null;
+ });
+
inputContainer.addEventListener('click', (e) => {
let inputBtn = document.getElementById(elementId);
inputBtn.click();
@@ -353,7 +360,7 @@ function setupFileInput(chooser) {
}
function showOrHideSelectedFilesContainer(files) {
- if (files && files.length > 0) {
+ if (showUploads && files && files.length > 0) {
chooser.style.setProperty('--selected-files-display', 'flex');
} else {
chooser.style.setProperty('--selected-files-display', 'none');
diff --git a/src/main/resources/static/js/multitool/PdfContainer.js b/src/main/resources/static/js/multitool/PdfContainer.js
index f9955a71c..72dd7f4c0 100644
--- a/src/main/resources/static/js/multitool/PdfContainer.js
+++ b/src/main/resources/static/js/multitool/PdfContainer.js
@@ -72,6 +72,7 @@ class PdfContainer {
window.addFilesBlankAll = this.addFilesBlankAll;
window.removeAllElements = this.removeAllElements;
window.resetPages = this.resetPages;
+ window.selectAll = false;
let undoBtn = document.getElementById('undo-btn');
let redoBtn = document.getElementById('redo-btn');
@@ -129,6 +130,10 @@ class PdfContainer {
return commandSequence;
}
+ showButton(button, show) {
+ button.classList.toggle('hidden', !show);
+ }
+
movePageTo(startElements, endElement, scrollTo = false) {
if (Array.isArray(startElements)){
@@ -176,8 +181,10 @@ class PdfContainer {
if (files.length > 0) {
pages = await this.addFilesFromFiles(files, nextSiblingElement, pages);
this.updateFilename(files[0].name);
- const selectAll = document.getElementById('select-pages-container');
- selectAll.classList.toggle('hidden', false);
+
+ if(window.selectPage){
+ this.showButton(document.getElementById('select-pages-container'), true);
+ }
}
resolve(pages);
};
@@ -191,9 +198,8 @@ class PdfContainer {
const pages = await this.addFilesFromFiles(files, nextSiblingElement, []);
this.updateFilename(files[0]?.name || 'untitled');
- const selectAll = document.getElementById('select-pages-container');
- if (selectAll) {
- selectAll.classList.remove('hidden');
+ if(window.selectPage) {
+ this.showButton(document.getElementById('select-pages-container'), true);
}
return pages;
@@ -433,12 +439,12 @@ class PdfContainer {
const selectIcon = document.getElementById('select-All-Container');
const deselectIcon = document.getElementById('deselect-All-Container');
- if (selectIcon.style.display === 'none') {
- selectIcon.style.display = 'inline';
- deselectIcon.style.display = 'none';
+ if (!window.selectAll) {
+ this.showButton(selectIcon, true);
+ this.showButton(deselectIcon, false);
} else {
- selectIcon.style.display = 'none';
- deselectIcon.style.display = 'inline';
+ this.showButton(selectIcon, false);
+ this.showButton(deselectIcon, true);
}
checkboxes.forEach((checkbox) => {
checkbox.checked = window.selectAll;
@@ -846,8 +852,20 @@ class PdfContainer {
deleteButton.classList.toggle('hidden', !window.selectPage);
const selectedPages = document.getElementById('selected-pages-display');
selectedPages.classList.toggle('hidden', !window.selectPage);
- const selectAll = document.getElementById('select-All-Container');
- selectAll.classList.toggle('hidden', !window.selectPage);
+ if(!window.selectPage)
+ {
+ this.showButton(document.getElementById('deselect-All-Container'), false);
+ this.showButton(document.getElementById('select-All-Container'), false);
+ }
+ else if(window.selectAll){
+ this.showButton(document.getElementById('deselect-All-Container'), true);
+ this.showButton(document.getElementById('select-All-Container'), false);
+ }
+ else{
+ this.showButton(document.getElementById('deselect-All-Container'), false);
+ this.showButton(document.getElementById('select-All-Container'), true);
+ }
+
const exportSelected = document.getElementById('export-selected-button');
exportSelected.classList.toggle('hidden', !window.selectPage);
const selectPagesButton = document.getElementById('select-pages-button');
diff --git a/src/main/resources/static/js/multitool/fileInput.js b/src/main/resources/static/js/multitool/fileInput.js
deleted file mode 100644
index ec7fa4c1e..000000000
--- a/src/main/resources/static/js/multitool/fileInput.js
+++ /dev/null
@@ -1,114 +0,0 @@
-class FileDragManager {
- overlay;
- dragCounter;
- updateFilename;
-
- constructor(cb = null) {
- this.dragCounter = 0;
- this.setCallback(cb);
-
- // Prevent default behavior for drag events
- ['dragenter', 'dragover', 'dragleave', 'drop'].forEach((eventName) => {
- document.body.addEventListener(eventName, preventDefaults, false);
- });
-
- function preventDefaults(e) {
- e.preventDefault();
- e.stopPropagation();
- }
-
- this.dragenterListener = this.dragenterListener.bind(this);
- this.dragleaveListener = this.dragleaveListener.bind(this);
- this.dropListener = this.dropListener.bind(this);
-
- document.body.addEventListener('dragenter', this.dragenterListener);
- document.body.addEventListener('dragleave', this.dragleaveListener);
- // Add drop event listener
- document.body.addEventListener('drop', this.dropListener);
- }
-
- setActions({updateFilename}) {
- this.updateFilename = updateFilename;
- }
-
- setCallback(cb) {
- if (cb) {
- this.callback = cb;
- } else {
- this.callback = (files) => console.warn('FileDragManager not set');
- }
- }
-
- dragenterListener() {
- this.dragCounter++;
- if (!this.overlay) {
- // Create and show the overlay
- this.overlay = document.createElement('div');
- this.overlay.style.position = 'fixed';
- this.overlay.style.top = 0;
- this.overlay.style.left = 0;
- this.overlay.style.width = '100%';
- this.overlay.style.height = '100%';
- this.overlay.style.background = 'rgba(0, 0, 0, 0.5)';
- this.overlay.style.color = '#fff';
- this.overlay.style.zIndex = '1000';
- this.overlay.style.display = 'flex';
- this.overlay.style.alignItems = 'center';
- this.overlay.style.justifyContent = 'center';
- this.overlay.style.pointerEvents = 'none';
- this.overlay.innerHTML = '
Drop files anywhere to upload
';
- document.getElementById('content-wrap').appendChild(this.overlay);
- }
- }
-
- dragleaveListener() {
- this.dragCounter--;
- if (this.dragCounter === 0) {
- // Hide and remove the overlay
- if (this.overlay) {
- this.overlay.remove();
- this.overlay = null;
- }
- }
- }
-
- dropListener(e) {
- const dt = e.dataTransfer;
- const files = dt.files;
- this.callback(files)
- .catch((err) => {
- console.error(err);
- //maybe
- })
- .finally(() => {
- // Hide and remove the overlay
- if (this.overlay) {
- this.overlay.remove();
- this.overlay = null;
- }
-
- this.updateFilename(files ? files[0].name : '');
- });
- }
-
- async addImageFile(file, nextSiblingElement) {
- const div = document.createElement('div');
- div.classList.add('page-container');
-
- var img = document.createElement('img');
- img.classList.add('page-image');
- img.src = URL.createObjectURL(file);
- div.appendChild(img);
-
- this.pdfAdapters.forEach((adapter) => {
- adapter.adapt?.(div);
- });
- if (nextSiblingElement) {
- this.pagesContainer.insertBefore(div, nextSiblingElement);
- } else {
- this.pagesContainer.appendChild(div);
- }
- }
-}
-
-export default FileDragManager;
diff --git a/src/main/resources/templates/fragments/common.html b/src/main/resources/templates/fragments/common.html
index 1e801d06f..2a8e95012 100644
--- a/src/main/resources/templates/fragments/common.html
+++ b/src/main/resources/templates/fragments/common.html
@@ -196,7 +196,7 @@
+ th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}, remoteCall=${remoteCall} ?: true, disableMultipleFiles=${disableMultipleFiles} ?: false, showUploads=${showUploads} ?: true, notRequired=${notRequired} ?: false">
+ th:attr="data-bs-unique-id=${name}, data-bs-element-id=${name+'-input'}, data-bs-element-container-id=${name+'-input-container'}, data-bs-show-uploads=${showUploads}, data-bs-files-selected=#{filesSelected}, data-bs-pdf-prompt=#{pdfPrompt}">