Reshape multitool U

Replace MultiToolFile input with common file input
This commit is contained in:
Connor Yoh 2025-03-18 15:32:16 +00:00
parent 99c4003b20
commit 695025ba13
3 changed files with 122 additions and 222 deletions

View File

@ -14,26 +14,30 @@ label {
border-radius: 16px !important; border-radius: 16px !important;
padding: 0.75rem; padding: 0.75rem;
border: 1px solid var(--theme-color-outline-variant); border: 1px solid var(--theme-color-outline-variant);
flex-grow: 5;
} }
.mt-action-bar { .mt-action-bar {
display: flex; display: flex;
gap: 10px; gap: 10px;
align-items: start; align-items: start;
background-color: var(--md-sys-color-surface-5);
border: none; border: none;
backdrop-filter: blur(2px); backdrop-filter: blur(2px);
top: 10px; top: 10px;
z-index: 10; z-index: 11;
padding: 1.25rem; padding: 1.25rem;
border-radius: 2rem; border-radius: 2rem;
margin: 0px 25px; margin: 0px 25px;
justify-content:center;
} }
.mt-action-bar>* { .mt-action-bar>* {
padding-bottom: 0.5rem; padding-bottom: 0.5rem;
} }
.mt-file-uploader {
width:100%
}
.mt-action-bar svg, .mt-action-bar svg,
.mt-action-btn svg { .mt-action-btn svg {
width: 20px; width: 20px;
@ -42,16 +46,22 @@ label {
.mt-action-bar .mt-filename { .mt-action-bar .mt-filename {
width: 100%; width: 100%;
display: flex;
gap: 10px;
} }
.mt-action-btn { .mt-action-btn {
position: fixed;
bottom: 8%;
background-color: var(--md-sys-color-surface-container-low) ;
display: flex; display: flex;
gap: 10px; gap: 10px;
align-items: start; z-index: 12;
top: 10px;
z-index: 10;
padding: 12px 0px 0px; padding: 12px 0px 0px;
width: 100%; width: fit-content;
justify-content: center;
border-radius: 2rem;
padding: 10px 20px
} }
.mt-action-btn .btn { .mt-action-btn .btn {

View File

@ -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 = '<p>Drop files anywhere to upload</p>';
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;

View File

@ -14,111 +14,112 @@
<br><br> <br><br>
<div class="container"> <div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="mt-action-btn">
<button id="undo-btn" th:title="#{multiTool.undo}" class="btn btn-secondary" onclick="undo()"
disabled>
<span class="material-symbols-rounded">
undo
</span>
</button>
<button id="redo-btn" class="btn btn-secondary" th:title="#{multiTool.redo}" onclick="redo()"
disabled>
<span class="material-symbols-rounded">
redo
</span>
</button>
<button class="btn btn-secondary enable-on-file" th:title="#{multiTool.rotateLeft}"
onclick="rotateAll(-90)" disabled>
<span class="material-symbols-rounded">
rotate_left
</span>
</button>
<button class="btn btn-secondary enable-on-file" th:title="#{multiTool.rotateRight}"
onclick="rotateAll(90)" disabled>
<span class="material-symbols-rounded">
rotate_right
</span>
</button>
<button class="btn btn-secondary enable-on-file" th:title="#{multiTool.split}" onclick="splitAll()"
disabled>
<span class="material-symbols-rounded">
cut
</span>
</button>
<button class="btn btn-secondary enable-on-file" th:title="#{multiTool.insertPageBreak}"
onclick="addFilesBlankAll()" disabled>
<span class="material-symbols-rounded">
insert_page_break
</span>
</button>
<button id="select-pages-container" th:title="#{multiTool.selectPages}"
class="btn btn-secondary enable-on-file" onclick="toggleSelectPageVisibility()" disabled>
<span id="select-pages-button" class="material-symbols-rounded">
event_list
</span>
</button>
<button id="deselect-All-Container" th:title="#{multiTool.deselectAll}"
class="btn btn-secondary enable-on-file hidden" onclick="toggleSelectAll()" disabled>
<span class="material-symbols-rounded" id="deselect-icon">deselect</span>
</button>
<button id="select-All-Container" th:title="#{multiTool.selectAll}"
class="btn btn-secondary enable-on-file hidden" onclick="toggleSelectAll()" disabled>
<span class="material-symbols-rounded" id="select-icon">select_all</span>
</button>
<div class="button-container">
<button id="delete-button" th:title="#{multiTool.deleteSelected}"
class="btn btn-danger delete hidden" onclick="deleteSelected()">
<span class="material-symbols-rounded">delete</span>
</button>
</div>
</div>
<div class="col-md-12"> <div class="col-md-12">
<div class="bg-card"> <div class="bg-card">
<div class="tool-header"> <div class="tool-header">
<span class="material-symbols-rounded tool-header-icon advance">construction</span> <span class="material-symbols-rounded tool-header-icon advance">construction</span>
<span class="tool-header-text" th:text="#{multiTool.header}"></span> <span class="tool-header-text" th:text="#{multiTool.header}"></span>
</div> </div>
<div class="mt-action-bar d-flex flex-wrap"> <div class="mt-action-bar d-flex flex-wrap">
<div class="mt-filename"> <div class="mt-filename">
<label for="filename-input" th:text="#{multiTool.uploadPrompts}">Filename</label> <input type="text" class="form-control" id="filename-input"
<input type="text" class="form-control" id="filename-input" th:placeholder="#{multiTool.uploadPrompts}">
th:placeholder="#{multiTool.uploadPrompts}">
</div>
<div class="mt-action-btn">
<button class="btn btn-primary" th:title="#{multiTool.addFile}" onclick="addFiles()">
<span class="material-symbols-rounded">
add
</span>
</button>
<button class="btn btn-secondary enable-on-file" th:title="#{multiTool.rotateLeft}"
onclick="rotateAll(-90)" disabled>
<span class="material-symbols-rounded">
rotate_left
</span>
</button>
<button class="btn btn-secondary enable-on-file" th:title="#{multiTool.rotateRight}"
onclick="rotateAll(90)" disabled>
<span class="material-symbols-rounded">
rotate_right
</span>
</button>
<button class="btn btn-secondary enable-on-file" th:title="#{multiTool.split}" onclick="splitAll()"
disabled>
<span class="material-symbols-rounded">
cut
</span>
</button>
<button class="btn btn-secondary enable-on-file" th:title="#{multiTool.insertPageBreak}"
onclick="addFilesBlankAll()" disabled>
<span class="material-symbols-rounded">
insert_page_break
</span>
</button>
<button id="undo-btn" th:title="#{multiTool.undo}" class="btn btn-secondary" onclick="undo()"
disabled>
<span class="material-symbols-rounded">
undo
</span>
</button>
<button id="redo-btn" class="btn btn-secondary" th:title="#{multiTool.redo}" onclick="redo()" <button id="export-selected-button" th:title="#{multiTool.downloadSelected}"
disabled> style="border-color: green; color:#b2e3a8; background: rgba(24, 122, 5, 1)"
<span class="material-symbols-rounded"> class="btn btn-primary enable-on-file hidden" onclick="exportPdf(true)" disabled>
redo <span class="material-symbols-rounded">
</span> file_save
</button> </span>
</button> </button>
<button id="select-pages-container" th:title="#{multiTool.selectPages}" <button style="border-color: green; color:#b2e3a8; background: rgba(24, 122, 5, 1)"
class="btn btn-secondary enable-on-file" onclick="toggleSelectPageVisibility()" disabled> th:title="#{multiTool.downloadAll}" id="export-button" class="btn btn-primary enable-on-file"
<span id="select-pages-button" class="material-symbols-rounded"> onclick="exportPdf(false)" disabled>
event_list <span class="material-symbols-rounded">
</span> download
</button> </span>
<button id="deselect-All-Container" th:title="#{multiTool.deselectAll}" </button>
class="btn btn-secondary enable-on-file hidden" onclick="toggleSelectAll()" disabled> </div>
<span class="material-symbols-rounded" id="deselect-icon">deselect</span> <div class="mt-file-uploader">
</button> <div
<button id="select-All-Container" th:title="#{multiTool.selectAll}" th:replace="~{fragments/common :: fileSelector(name='pdf-upload', multipleInputsForSingleRequest=false, accept='application/pdf', showUploads=false)}">
class="btn btn-secondary enable-on-file hidden" onclick="toggleSelectAll()" disabled>
<span class="material-symbols-rounded" id="select-icon">select_all</span>
</button>
<div class="button-container">
<button id="delete-button" th:title="#{multiTool.deleteSelected}"
class="btn btn-danger delete hidden" onclick="deleteSelected()">
<span class="material-symbols-rounded">delete</span>
</button>
</div>
<div style="margin-left:auto">
<button id="export-selected-button" th:title="#{multiTool.downloadSelected}"
style="border-color: green; color:#b2e3a8; background: rgba(24, 122, 5, 1)"
class="btn btn-primary enable-on-file hidden" onclick="exportPdf(true)" disabled>
<span class="material-symbols-rounded">
file_save
</span>
</button>
<button style="border-color: green; color:#b2e3a8; background: rgba(24, 122, 5, 1)"
th:title="#{multiTool.downloadAll}" id="export-button" class="btn btn-primary enable-on-file"
onclick="exportPdf(false)" disabled>
<span class="material-symbols-rounded">
download
</span>
</button>
</div>
</div>
<div id="selected-pages-display" class="selected-pages-container hidden">
<div style="display:flex; height:3rem; margin-right:1rem">
<h5 th:text="#{multiTool.selectedPages}" style="white-space: nowrap; margin-right: 1rem;">Selected
Pages</h5>
<input type="text" id="csv-input" class="form-control" style="height:2.5rem" placeholder="1,3,5-10"
value="">
</div>
<ul id="selected-pages-list" class="pages-list"></ul>
</div> </div>
</div> </div>
<div id="selected-pages-display" class="selected-pages-container hidden">
<div style="display:flex; height:3rem; margin-right:1rem">
<h5 th:text="#{multiTool.selectedPages}" style="white-space: nowrap; margin-right: 1rem;">Selected
Pages</h5>
<input type="text" id="csv-input" class="form-control" style="height:2.5rem" placeholder="1,3,5-10"
value="">
</div>
<ul id="selected-pages-list" class="pages-list"></ul>
</div>
</div>
<div class="multi-tool-container"> <div class="multi-tool-container">
<div class="d-flex flex-wrap" id="pages-container-wrapper"> <div class="d-flex flex-wrap" id="pages-container-wrapper">
<div id="pages-container"> <div id="pages-container">
@ -180,16 +181,21 @@
import DragDropManager from "./js/multitool/DragDropManager.js"; import DragDropManager from "./js/multitool/DragDropManager.js";
import ImageHighlighter from "./js/multitool/ImageHighlighter.js"; import ImageHighlighter from "./js/multitool/ImageHighlighter.js";
import PdfActionsManager from './js/multitool/PdfActionsManager.js'; import PdfActionsManager from './js/multitool/PdfActionsManager.js';
import FileDragManager from './js/multitool/fileInput.js';
// enables drag and drop // enables drag and drop
const pdfUpload = document.querySelector("input[name=pdf-upload]");
pdfUpload.addEventListener("change", async (e) => {
if (!e.target.files) return;
await pdfContainer.handleDroppedFiles( e.target.files);
e.target.value = null;
});
var undoManager = new UndoManager(); var undoManager = new UndoManager();
const dragDropManager = new DragDropManager('drag-container', 'pages-container'); const dragDropManager = new DragDropManager('drag-container', 'pages-container');
// enables image highlight on click // enables image highlight on click
const imageHighlighter = new ImageHighlighter('image-highlighter'); const imageHighlighter = new ImageHighlighter('image-highlighter');
// enables the default action buttons on each file // enables the default action buttons on each file
const pdfActionsManager = new PdfActionsManager('pages-container', undoManager); const pdfActionsManager = new PdfActionsManager('pages-container', undoManager);
const fileDragManager = new FileDragManager();
// Scroll the wrapper horizontally // Scroll the wrapper horizontally
// Automatically exposes rotateAll, addFiles and exportPdf to the window for the global buttons. // Automatically exposes rotateAll, addFiles and exportPdf to the window for the global buttons.
@ -199,13 +205,11 @@
[ [
dragDropManager, dragDropManager,
imageHighlighter, imageHighlighter,
pdfActionsManager, pdfActionsManager
fileDragManager
], ],
undoManager undoManager
) )
fileDragManager.setCallback(async (files) => pdfContainer.handleDroppedFiles(files));
document.addEventListener('keydown', function (event) { document.addEventListener('keydown', function (event) {
let targetElementId = event.target.id; let targetElementId = event.target.id;