- Add TypeScript type definitions for merge and alternate-merge Web Workers - Implement custom rotation angle input with increment/decrement controls for rotate tool - Extend delete-pages tool to render page thumbnails for better UX - Hide number input spin buttons across all number inputs via CSS - Refactor rotateAll function to accept angle parameter instead of direction multiplier - Update fileHandler to support delete-pages tool thumbnail rendering - Improve type safety in alternate-merge logic with proper interface definitions - Enhance rotate tool UI with custom angle input field and adjustment buttons
112 lines
3.6 KiB
TypeScript
112 lines
3.6 KiB
TypeScript
import { showLoader, hideLoader, showAlert } from '../ui.js';
|
|
import { downloadFile } from '../utils/helpers.js';
|
|
import { state } from '../state.js';
|
|
|
|
import { PDFDocument as PDFLibDocument } from 'pdf-lib';
|
|
|
|
export async function deletePages() {
|
|
// @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
|
|
const pageInput = document.getElementById('pages-to-delete').value;
|
|
if (!pageInput) {
|
|
showAlert('Invalid Input', 'Please enter page numbers to delete.');
|
|
return;
|
|
}
|
|
showLoader('Deleting pages...');
|
|
try {
|
|
const totalPages = state.pdfDoc.getPageCount();
|
|
const indicesToDelete = new Set();
|
|
const ranges = pageInput.split(',');
|
|
|
|
for (const range of ranges) {
|
|
const trimmedRange = range.trim();
|
|
if (trimmedRange.includes('-')) {
|
|
const [start, end] = trimmedRange.split('-').map(Number);
|
|
if (
|
|
isNaN(start) ||
|
|
isNaN(end) ||
|
|
start < 1 ||
|
|
end > totalPages ||
|
|
start > end
|
|
)
|
|
continue;
|
|
for (let i = start; i <= end; i++) indicesToDelete.add(i - 1);
|
|
} else {
|
|
const pageNum = Number(trimmedRange);
|
|
if (isNaN(pageNum) || pageNum < 1 || pageNum > totalPages) continue;
|
|
indicesToDelete.add(pageNum - 1);
|
|
}
|
|
}
|
|
|
|
if (indicesToDelete.size === 0) {
|
|
showAlert('Invalid Input', 'No valid pages selected for deletion.');
|
|
hideLoader();
|
|
return;
|
|
}
|
|
if (indicesToDelete.size >= totalPages) {
|
|
showAlert('Invalid Input', 'You cannot delete all pages.');
|
|
hideLoader();
|
|
return;
|
|
}
|
|
|
|
const indicesToKeep = Array.from(
|
|
{ length: totalPages },
|
|
(_, i) => i
|
|
).filter((index) => !indicesToDelete.has(index));
|
|
const newPdf = await PDFLibDocument.create();
|
|
const copiedPages = await newPdf.copyPages(state.pdfDoc, indicesToKeep);
|
|
copiedPages.forEach((page: any) => newPdf.addPage(page));
|
|
|
|
const newPdfBytes = await newPdf.save();
|
|
downloadFile(
|
|
new Blob([new Uint8Array(newPdfBytes)], { type: 'application/pdf' }),
|
|
'deleted-pages.pdf'
|
|
);
|
|
} catch (e) {
|
|
console.error(e);
|
|
showAlert('Error', 'Could not delete pages.');
|
|
} finally {
|
|
hideLoader();
|
|
}
|
|
}
|
|
|
|
export function setupDeletePagesTool() {
|
|
const input = document.getElementById('pages-to-delete') as HTMLInputElement;
|
|
if (!input) return;
|
|
|
|
const updateHighlights = () => {
|
|
const val = input.value;
|
|
const pagesToDelete = new Set<number>();
|
|
|
|
const parts = val.split(',');
|
|
for (const part of parts) {
|
|
const trimmed = part.trim();
|
|
if (trimmed.includes('-')) {
|
|
const [start, end] = trimmed.split('-').map(Number);
|
|
if (!isNaN(start) && !isNaN(end) && start <= end) {
|
|
for (let i = start; i <= end; i++) pagesToDelete.add(i);
|
|
}
|
|
} else {
|
|
const num = Number(trimmed);
|
|
if (!isNaN(num)) pagesToDelete.add(num);
|
|
}
|
|
}
|
|
|
|
const thumbnails = document.querySelectorAll('#delete-pages-preview .page-thumbnail');
|
|
thumbnails.forEach((thumb) => {
|
|
const pageNum = parseInt((thumb as HTMLElement).dataset.pageNumber || '0');
|
|
const innerContainer = thumb.querySelector('div.relative');
|
|
|
|
if (pagesToDelete.has(pageNum)) {
|
|
innerContainer?.classList.add('border-red-500');
|
|
innerContainer?.classList.remove('border-gray-600');
|
|
} else {
|
|
innerContainer?.classList.remove('border-red-500');
|
|
innerContainer?.classList.add('border-gray-600');
|
|
}
|
|
});
|
|
};
|
|
|
|
input.addEventListener('input', updateHighlights);
|
|
updateHighlights();
|
|
}
|