-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
(Feature) Add drag-and-drop arrangement for JPG to PDF conversion #323
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,9 @@ import { | |
| compressImageFile, | ||
| } from '../utils/image-compress.js'; | ||
|
|
||
| // @ts-ignore | ||
| import Sortable from 'sortablejs'; | ||
|
|
||
| const SUPPORTED_FORMATS = '.jpg,.jpeg,.jp2,.jpx'; | ||
| const SUPPORTED_MIME_TYPES = ['image/jpeg', 'image/jpg', 'image/jp2']; | ||
|
|
||
|
|
@@ -76,6 +79,10 @@ function initializePage() { | |
| document.getElementById('back-to-tools')?.addEventListener('click', () => { | ||
| window.location.href = import.meta.env.BASE_URL; | ||
| }); | ||
|
|
||
| setTimeout(() => { | ||
| initializeFileListSortable(); | ||
| }, 0); | ||
| } | ||
|
|
||
| function handleFileUpload(e: Event) { | ||
|
|
@@ -134,7 +141,8 @@ function updateUI() { | |
| files.forEach((file, index) => { | ||
| const fileDiv = document.createElement('div'); | ||
| fileDiv.className = | ||
| 'flex items-center justify-between bg-gray-700 p-3 rounded-lg text-sm'; | ||
| 'flex items-center justify-between bg-gray-700 p-3 rounded-lg text-sm draggable-file'; | ||
| fileDiv.setAttribute('data-index', index.toString()); | ||
|
|
||
| const infoContainer = document.createElement('div'); | ||
| infoContainer.className = 'flex items-center gap-2 overflow-hidden'; | ||
|
|
@@ -149,25 +157,59 @@ function updateUI() { | |
|
|
||
| infoContainer.append(nameSpan, sizeSpan); | ||
|
|
||
| const rightGroup = document.createElement('div'); | ||
| rightGroup.className = 'flex items-center gap-1 ml-4'; | ||
|
|
||
| const dragHandle = document.createElement('div'); | ||
| dragHandle.className = | ||
| 'drag-handle cursor-move text-gray-400 hover:text-white p-1 rounded transition-colors'; | ||
| dragHandle.innerHTML = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="9" cy="5" r="1"/><circle cx="15" cy="5" r="1"/><circle cx="9" cy="12" r="1"/><circle cx="15" cy="12" r="1"/><circle cx="9" cy="19" r="1"/><circle cx="15" cy="19" r="1"/></svg>`; | ||
|
Comment on lines
+163
to
+166
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make drag handle keyboard-accessible. The reorder control is a non-focusable 🧰 Tools🪛 ast-grep (0.41.1)[warning] 165-165: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify. (unsafe-html-content-assignment) [warning] 165-165: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first. (dom-content-modification) 🤖 Prompt for AI Agents |
||
|
|
||
| const removeBtn = document.createElement('button'); | ||
| removeBtn.className = | ||
| 'ml-4 text-red-400 hover:text-red-300 flex-shrink-0'; | ||
| removeBtn.className = 'text-red-400 hover:text-red-300 flex-shrink-0'; | ||
| removeBtn.innerHTML = '<i data-lucide="trash-2" class="w-4 h-4"></i>'; | ||
| removeBtn.onclick = () => { | ||
| files = files.filter((_, i) => i !== index); | ||
| updateUI(); | ||
| }; | ||
|
|
||
| fileDiv.append(infoContainer, removeBtn); | ||
| rightGroup.append(dragHandle, removeBtn); | ||
| fileDiv.append(infoContainer, rightGroup); | ||
| fileDisplayArea.appendChild(fileDiv); | ||
| }); | ||
| createIcons({ icons }); | ||
| initializeFileListSortable(); | ||
| } else { | ||
| fileControls.classList.add('hidden'); | ||
| optionsDiv.classList.add('hidden'); | ||
| } | ||
| } | ||
|
|
||
| function initializeFileListSortable() { | ||
| const fileDisplayArea = document.getElementById('file-display-area'); | ||
| if (!fileDisplayArea) return; | ||
| if ((fileDisplayArea as any)._sortableInstance) { | ||
| (fileDisplayArea as any)._sortableInstance.destroy(); | ||
| } | ||
| (fileDisplayArea as any)._sortableInstance = Sortable.create( | ||
| fileDisplayArea, | ||
| { | ||
| handle: '.drag-handle', | ||
| animation: 150, | ||
| ghostClass: 'sortable-ghost', | ||
| chosenClass: 'sortable-chosen', | ||
| dragClass: 'sortable-drag', | ||
| onEnd: function (evt: any) { | ||
| if (evt.oldIndex !== evt.newIndex) { | ||
| const moved = files.splice(evt.oldIndex, 1)[0]; | ||
| files.splice(evt.newIndex, 0, moved); | ||
| updateUI(); | ||
| } | ||
| }, | ||
| } | ||
| ); | ||
| } | ||
|
|
||
| async function ensurePyMuPDF(): Promise<any> { | ||
| if (!pymupdf) { | ||
| pymupdf = await loadPyMuPDF(); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.