mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-26 06:09:23 +00:00
working split
This commit is contained in:
parent
abed82cc6b
commit
d5e1a3eccb
@ -1,335 +0,0 @@
|
|||||||
import { Command, CommandSequence } from '../hooks/useUndoRedo';
|
|
||||||
import { PDFDocument, PDFPage } from '../types/pageEditor';
|
|
||||||
|
|
||||||
// Base class for page operations
|
|
||||||
abstract class PageCommand implements Command {
|
|
||||||
protected pdfDocument: PDFDocument;
|
|
||||||
protected setPdfDocument: (doc: PDFDocument) => void;
|
|
||||||
protected previousState: PDFDocument;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
pdfDocument: PDFDocument,
|
|
||||||
setPdfDocument: (doc: PDFDocument) => void
|
|
||||||
) {
|
|
||||||
this.pdfDocument = pdfDocument;
|
|
||||||
this.setPdfDocument = setPdfDocument;
|
|
||||||
this.previousState = JSON.parse(JSON.stringify(pdfDocument)); // Deep clone
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract execute(): void;
|
|
||||||
abstract description: string;
|
|
||||||
|
|
||||||
undo(): void {
|
|
||||||
this.setPdfDocument(this.previousState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rotate pages command
|
|
||||||
export class RotatePagesCommand extends PageCommand {
|
|
||||||
private pageIds: string[];
|
|
||||||
private rotation: number;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
pdfDocument: PDFDocument,
|
|
||||||
setPdfDocument: (doc: PDFDocument) => void,
|
|
||||||
pageIds: string[],
|
|
||||||
rotation: number
|
|
||||||
) {
|
|
||||||
super(pdfDocument, setPdfDocument);
|
|
||||||
this.pageIds = pageIds;
|
|
||||||
this.rotation = rotation;
|
|
||||||
}
|
|
||||||
|
|
||||||
execute(): void {
|
|
||||||
const updatedPages = this.pdfDocument.pages.map(page => {
|
|
||||||
if (this.pageIds.includes(page.id)) {
|
|
||||||
return { ...page, rotation: page.rotation + this.rotation };
|
|
||||||
}
|
|
||||||
return page;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.setPdfDocument({
|
|
||||||
...this.pdfDocument,
|
|
||||||
pages: updatedPages,
|
|
||||||
totalPages: updatedPages.length
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get description(): string {
|
|
||||||
const direction = this.rotation > 0 ? 'right' : 'left';
|
|
||||||
return `Rotate ${this.pageIds.length} page(s) ${direction}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete pages command
|
|
||||||
export class DeletePagesCommand extends PageCommand {
|
|
||||||
private pageIds: string[];
|
|
||||||
private deletedPages: PDFPage[];
|
|
||||||
private deletedPositions: Map<string, number>;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
pdfDocument: PDFDocument,
|
|
||||||
setPdfDocument: (doc: PDFDocument) => void,
|
|
||||||
pageIds: string[]
|
|
||||||
) {
|
|
||||||
super(pdfDocument, setPdfDocument);
|
|
||||||
this.pageIds = pageIds;
|
|
||||||
this.deletedPages = [];
|
|
||||||
this.deletedPositions = new Map();
|
|
||||||
}
|
|
||||||
|
|
||||||
execute(): void {
|
|
||||||
// Store deleted pages and their positions for undo
|
|
||||||
this.deletedPages = this.pdfDocument.pages.filter(page =>
|
|
||||||
this.pageIds.includes(page.id)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.deletedPages.forEach(page => {
|
|
||||||
const index = this.pdfDocument.pages.findIndex(p => p.id === page.id);
|
|
||||||
this.deletedPositions.set(page.id, index);
|
|
||||||
});
|
|
||||||
|
|
||||||
const updatedPages = this.pdfDocument.pages
|
|
||||||
.filter(page => !this.pageIds.includes(page.id))
|
|
||||||
.map((page, index) => ({ ...page, pageNumber: index + 1 }));
|
|
||||||
|
|
||||||
this.setPdfDocument({
|
|
||||||
...this.pdfDocument,
|
|
||||||
pages: updatedPages,
|
|
||||||
totalPages: updatedPages.length
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
undo(): void {
|
|
||||||
// Simply restore to the previous state (before deletion)
|
|
||||||
this.setPdfDocument(this.previousState);
|
|
||||||
}
|
|
||||||
|
|
||||||
get description(): string {
|
|
||||||
return `Delete ${this.pageIds.length} page(s)`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move pages command
|
|
||||||
export class MovePagesCommand extends PageCommand {
|
|
||||||
private pageIds: string[];
|
|
||||||
private targetIndex: number;
|
|
||||||
private originalIndices: Map<string, number>;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
pdfDocument: PDFDocument,
|
|
||||||
setPdfDocument: (doc: PDFDocument) => void,
|
|
||||||
pageIds: string[],
|
|
||||||
targetIndex: number
|
|
||||||
) {
|
|
||||||
super(pdfDocument, setPdfDocument);
|
|
||||||
this.pageIds = pageIds;
|
|
||||||
this.targetIndex = targetIndex;
|
|
||||||
this.originalIndices = new Map();
|
|
||||||
}
|
|
||||||
|
|
||||||
execute(): void {
|
|
||||||
// Store original positions
|
|
||||||
this.pageIds.forEach(pageId => {
|
|
||||||
const index = this.pdfDocument.pages.findIndex(p => p.id === pageId);
|
|
||||||
this.originalIndices.set(pageId, index);
|
|
||||||
});
|
|
||||||
|
|
||||||
let newPages = [...this.pdfDocument.pages];
|
|
||||||
const pagesToMove = this.pageIds
|
|
||||||
.map(id => this.pdfDocument.pages.find(p => p.id === id))
|
|
||||||
.filter((page): page is PDFPage => page !== undefined);
|
|
||||||
|
|
||||||
// Remove pages to move
|
|
||||||
newPages = newPages.filter(page => !this.pageIds.includes(page.id));
|
|
||||||
|
|
||||||
// Insert pages at target position
|
|
||||||
newPages.splice(this.targetIndex, 0, ...pagesToMove);
|
|
||||||
|
|
||||||
// Update page numbers
|
|
||||||
newPages = newPages.map((page, index) => ({
|
|
||||||
...page,
|
|
||||||
pageNumber: index + 1
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.setPdfDocument({
|
|
||||||
...this.pdfDocument,
|
|
||||||
pages: newPages,
|
|
||||||
totalPages: newPages.length
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get description(): string {
|
|
||||||
return `Move ${this.pageIds.length} page(s)`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reorder single page command (for drag-and-drop)
|
|
||||||
export class ReorderPageCommand extends PageCommand {
|
|
||||||
private pageId: string;
|
|
||||||
private targetIndex: number;
|
|
||||||
private originalIndex: number;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
pdfDocument: PDFDocument,
|
|
||||||
setPdfDocument: (doc: PDFDocument) => void,
|
|
||||||
pageId: string,
|
|
||||||
targetIndex: number
|
|
||||||
) {
|
|
||||||
super(pdfDocument, setPdfDocument);
|
|
||||||
this.pageId = pageId;
|
|
||||||
this.targetIndex = targetIndex;
|
|
||||||
this.originalIndex = pdfDocument.pages.findIndex(p => p.id === pageId);
|
|
||||||
}
|
|
||||||
|
|
||||||
execute(): void {
|
|
||||||
const newPages = [...this.pdfDocument.pages];
|
|
||||||
const [movedPage] = newPages.splice(this.originalIndex, 1);
|
|
||||||
newPages.splice(this.targetIndex, 0, movedPage);
|
|
||||||
|
|
||||||
// Update page numbers
|
|
||||||
const updatedPages = newPages.map((page, index) => ({
|
|
||||||
...page,
|
|
||||||
pageNumber: index + 1
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.setPdfDocument({
|
|
||||||
...this.pdfDocument,
|
|
||||||
pages: updatedPages,
|
|
||||||
totalPages: updatedPages.length
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get description(): string {
|
|
||||||
return `Reorder page ${this.originalIndex + 1} to position ${this.targetIndex + 1}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Toggle split markers command
|
|
||||||
export class ToggleSplitCommand extends PageCommand {
|
|
||||||
private pageIds: string[];
|
|
||||||
private previousSplitStates: Map<string, boolean>;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
pdfDocument: PDFDocument,
|
|
||||||
setPdfDocument: (doc: PDFDocument) => void,
|
|
||||||
pageIds: string[]
|
|
||||||
) {
|
|
||||||
super(pdfDocument, setPdfDocument);
|
|
||||||
this.pageIds = pageIds;
|
|
||||||
this.previousSplitStates = new Map();
|
|
||||||
}
|
|
||||||
|
|
||||||
execute(): void {
|
|
||||||
// Store previous split states
|
|
||||||
this.pageIds.forEach(pageId => {
|
|
||||||
const page = this.pdfDocument.pages.find(p => p.id === pageId);
|
|
||||||
if (page) {
|
|
||||||
this.previousSplitStates.set(pageId, !!page.splitBefore);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const updatedPages = this.pdfDocument.pages.map(page => {
|
|
||||||
if (this.pageIds.includes(page.id)) {
|
|
||||||
return { ...page, splitBefore: !page.splitBefore };
|
|
||||||
}
|
|
||||||
return page;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.setPdfDocument({
|
|
||||||
...this.pdfDocument,
|
|
||||||
pages: updatedPages,
|
|
||||||
totalPages: updatedPages.length
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
undo(): void {
|
|
||||||
const updatedPages = this.pdfDocument.pages.map(page => {
|
|
||||||
if (this.pageIds.includes(page.id)) {
|
|
||||||
const previousState = this.previousSplitStates.get(page.id);
|
|
||||||
return { ...page, splitBefore: previousState };
|
|
||||||
}
|
|
||||||
return page;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.setPdfDocument({
|
|
||||||
...this.pdfDocument,
|
|
||||||
pages: updatedPages,
|
|
||||||
totalPages: updatedPages.length
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get description(): string {
|
|
||||||
return `Toggle split markers for ${this.pageIds.length} page(s)`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add pages command (for inserting new files)
|
|
||||||
export class AddPagesCommand extends PageCommand {
|
|
||||||
private newPages: PDFPage[];
|
|
||||||
private insertIndex: number;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
pdfDocument: PDFDocument,
|
|
||||||
setPdfDocument: (doc: PDFDocument) => void,
|
|
||||||
newPages: PDFPage[],
|
|
||||||
insertIndex: number = -1 // -1 means append to end
|
|
||||||
) {
|
|
||||||
super(pdfDocument, setPdfDocument);
|
|
||||||
this.newPages = newPages;
|
|
||||||
this.insertIndex = insertIndex === -1 ? pdfDocument.pages.length : insertIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
execute(): void {
|
|
||||||
const newPagesArray = [...this.pdfDocument.pages];
|
|
||||||
newPagesArray.splice(this.insertIndex, 0, ...this.newPages);
|
|
||||||
|
|
||||||
// Update page numbers for all pages
|
|
||||||
const updatedPages = newPagesArray.map((page, index) => ({
|
|
||||||
...page,
|
|
||||||
pageNumber: index + 1
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.setPdfDocument({
|
|
||||||
...this.pdfDocument,
|
|
||||||
pages: updatedPages,
|
|
||||||
totalPages: updatedPages.length
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
undo(): void {
|
|
||||||
const updatedPages = this.pdfDocument.pages
|
|
||||||
.filter(page => !this.newPages.some(newPage => newPage.id === page.id))
|
|
||||||
.map((page, index) => ({ ...page, pageNumber: index + 1 }));
|
|
||||||
|
|
||||||
this.setPdfDocument({
|
|
||||||
...this.pdfDocument,
|
|
||||||
pages: updatedPages,
|
|
||||||
totalPages: updatedPages.length
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get description(): string {
|
|
||||||
return `Add ${this.newPages.length} page(s)`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command sequence for bulk operations
|
|
||||||
export class PageCommandSequence implements CommandSequence {
|
|
||||||
commands: Command[];
|
|
||||||
description: string;
|
|
||||||
|
|
||||||
constructor(commands: Command[], description?: string) {
|
|
||||||
this.commands = commands;
|
|
||||||
this.description = description || `Execute ${commands.length} operations`;
|
|
||||||
}
|
|
||||||
|
|
||||||
execute(): void {
|
|
||||||
this.commands.forEach(command => command.execute());
|
|
||||||
}
|
|
||||||
|
|
||||||
undo(): void {
|
|
||||||
// Undo in reverse order
|
|
||||||
[...this.commands].reverse().forEach(command => command.undo());
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,7 +6,7 @@ import styles from './PageEditor.module.css';
|
|||||||
|
|
||||||
interface DragDropItem {
|
interface DragDropItem {
|
||||||
id: string;
|
id: string;
|
||||||
splitBefore?: boolean;
|
splitAfter?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DragDropGridProps<T extends DragDropItem> {
|
interface DragDropGridProps<T extends DragDropItem> {
|
||||||
@ -128,14 +128,13 @@ const DragDropGrid = <T extends DragDropItem>({
|
|||||||
justifyContent: 'flex-start',
|
justifyContent: 'flex-start',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
position: 'relative'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{rowItems.map((item, itemIndex) => {
|
{rowItems.map((item, itemIndex) => {
|
||||||
const actualIndex = startIndex + itemIndex;
|
const actualIndex = startIndex + itemIndex;
|
||||||
return (
|
return (
|
||||||
<React.Fragment key={item.id}>
|
<React.Fragment key={item.id}>
|
||||||
{/* Split marker */}
|
|
||||||
{renderSplitMarker && item.splitBefore && actualIndex > 0 && renderSplitMarker(item, actualIndex)}
|
|
||||||
{/* Item */}
|
{/* Item */}
|
||||||
{renderItem(item, actualIndex, itemRefs)}
|
{renderItem(item, actualIndex, itemRefs)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
@ -365,9 +365,16 @@ const PageEditor = ({
|
|||||||
const [isAnimating, setIsAnimating] = useState(false);
|
const [isAnimating, setIsAnimating] = useState(false);
|
||||||
const [csvInput, setCsvInput] = useState('');
|
const [csvInput, setCsvInput] = useState('');
|
||||||
|
|
||||||
|
// Position-based split tracking (replaces page-based splitAfter)
|
||||||
|
const [splitPositions, setSplitPositions] = useState<Set<number>>(new Set());
|
||||||
|
|
||||||
|
// Grid container ref for positioning split indicators
|
||||||
|
const gridContainerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
// Export state
|
// Export state
|
||||||
const [exportLoading, setExportLoading] = useState(false);
|
const [exportLoading, setExportLoading] = useState(false);
|
||||||
|
|
||||||
|
|
||||||
// DOM-first command handlers
|
// DOM-first command handlers
|
||||||
const handleRotatePages = useCallback((pageIds: string[], rotation: number) => {
|
const handleRotatePages = useCallback((pageIds: string[], rotation: number) => {
|
||||||
pageIds.forEach(pageId => {
|
pageIds.forEach(pageId => {
|
||||||
@ -445,30 +452,22 @@ const PageEditor = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ToggleSplitCommand {
|
class ToggleSplitCommand {
|
||||||
constructor(public pageIds: string[]) {}
|
constructor(public position: number) {}
|
||||||
execute() {
|
execute() {
|
||||||
if (!displayDocument) return;
|
if (!displayDocument) return;
|
||||||
|
|
||||||
console.log('Toggle split:', this.pageIds);
|
console.log('Toggle split at position:', this.position);
|
||||||
|
|
||||||
// Create new pages array with toggled split markers
|
// Toggle the split position in the splitPositions set
|
||||||
const newPages = displayDocument.pages.map(page => {
|
setSplitPositions(prev => {
|
||||||
if (this.pageIds.includes(page.id)) {
|
const newPositions = new Set(prev);
|
||||||
return {
|
if (newPositions.has(this.position)) {
|
||||||
...page,
|
newPositions.delete(this.position);
|
||||||
splitAfter: !page.splitAfter
|
} else {
|
||||||
};
|
newPositions.add(this.position);
|
||||||
}
|
}
|
||||||
return page;
|
return newPositions;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update the document with new split markers
|
|
||||||
const updatedDocument: PDFDocument = {
|
|
||||||
...displayDocument,
|
|
||||||
pages: newPages,
|
|
||||||
};
|
|
||||||
|
|
||||||
setEditedDocument(updatedDocument);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -482,6 +481,7 @@ const PageEditor = ({
|
|||||||
// Interface functions for parent component
|
// Interface functions for parent component
|
||||||
const displayDocument = editedDocument || mergedPdfDocument;
|
const displayDocument = editedDocument || mergedPdfDocument;
|
||||||
|
|
||||||
|
|
||||||
const handleUndo = useCallback(() => {
|
const handleUndo = useCallback(() => {
|
||||||
undoManagerRef.current.undo();
|
undoManagerRef.current.undo();
|
||||||
}, []);
|
}, []);
|
||||||
@ -510,18 +510,17 @@ const PageEditor = ({
|
|||||||
const handleSplit = useCallback(() => {
|
const handleSplit = useCallback(() => {
|
||||||
if (!displayDocument || selectedPageNumbers.length === 0) return;
|
if (!displayDocument || selectedPageNumbers.length === 0) return;
|
||||||
|
|
||||||
console.log('Toggle split markers at selected pages:', selectedPageNumbers);
|
console.log('Toggle split markers at selected page positions:', selectedPageNumbers);
|
||||||
|
|
||||||
// Get page IDs for selected pages
|
// Convert page numbers to positions (0-based indices)
|
||||||
const selectedPageIds = selectedPageNumbers.map(pageNum => {
|
selectedPageNumbers.forEach(pageNum => {
|
||||||
const page = displayDocument.pages.find(p => p.pageNumber === pageNum);
|
const pageIndex = displayDocument.pages.findIndex(p => p.pageNumber === pageNum);
|
||||||
return page?.id || '';
|
if (pageIndex !== -1 && pageIndex < displayDocument.pages.length - 1) {
|
||||||
}).filter(id => id);
|
// Only allow splits before the last page
|
||||||
|
const command = new ToggleSplitCommand(pageIndex);
|
||||||
if (selectedPageIds.length > 0) {
|
command.execute();
|
||||||
const command = new ToggleSplitCommand(selectedPageIds);
|
}
|
||||||
command.execute();
|
});
|
||||||
}
|
|
||||||
}, [selectedPageNumbers, displayDocument]);
|
}, [selectedPageNumbers, displayDocument]);
|
||||||
|
|
||||||
const handleReorderPages = useCallback((sourcePageNumber: number, targetIndex: number, selectedPages?: number[]) => {
|
const handleReorderPages = useCallback((sourcePageNumber: number, targetIndex: number, selectedPages?: number[]) => {
|
||||||
@ -592,7 +591,8 @@ const PageEditor = ({
|
|||||||
console.log('Applying DOM changes before export...');
|
console.log('Applying DOM changes before export...');
|
||||||
const processedDocuments = documentManipulationService.applyDOMChangesToDocument(
|
const processedDocuments = documentManipulationService.applyDOMChangesToDocument(
|
||||||
mergedPdfDocument || displayDocument, // Original order
|
mergedPdfDocument || displayDocument, // Original order
|
||||||
displayDocument // Current display order (includes reordering)
|
displayDocument, // Current display order (includes reordering)
|
||||||
|
splitPositions // Position-based splits
|
||||||
);
|
);
|
||||||
|
|
||||||
// For selected pages export, we work with the first document (or single document)
|
// For selected pages export, we work with the first document (or single document)
|
||||||
@ -620,7 +620,7 @@ const PageEditor = ({
|
|||||||
console.error('Export failed:', error);
|
console.error('Export failed:', error);
|
||||||
setExportLoading(false);
|
setExportLoading(false);
|
||||||
}
|
}
|
||||||
}, [displayDocument, selectedPageNumbers, mergedPdfDocument]);
|
}, [displayDocument, selectedPageNumbers, mergedPdfDocument, splitPositions]);
|
||||||
|
|
||||||
const onExportAll = useCallback(async () => {
|
const onExportAll = useCallback(async () => {
|
||||||
if (!displayDocument) return;
|
if (!displayDocument) return;
|
||||||
@ -631,7 +631,8 @@ const PageEditor = ({
|
|||||||
console.log('Applying DOM changes before export...');
|
console.log('Applying DOM changes before export...');
|
||||||
const processedDocuments = documentManipulationService.applyDOMChangesToDocument(
|
const processedDocuments = documentManipulationService.applyDOMChangesToDocument(
|
||||||
mergedPdfDocument || displayDocument, // Original order
|
mergedPdfDocument || displayDocument, // Original order
|
||||||
displayDocument // Current display order (includes reordering)
|
displayDocument, // Current display order (includes reordering)
|
||||||
|
splitPositions // Position-based splits
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 2: Check if we have multiple documents (splits) or single document
|
// Step 2: Check if we have multiple documents (splits) or single document
|
||||||
@ -676,7 +677,7 @@ const PageEditor = ({
|
|||||||
console.error('Export failed:', error);
|
console.error('Export failed:', error);
|
||||||
setExportLoading(false);
|
setExportLoading(false);
|
||||||
}
|
}
|
||||||
}, [displayDocument, mergedPdfDocument]);
|
}, [displayDocument, mergedPdfDocument, splitPositions]);
|
||||||
|
|
||||||
// Apply DOM changes to document state using dedicated service
|
// Apply DOM changes to document state using dedicated service
|
||||||
const applyChanges = useCallback(() => {
|
const applyChanges = useCallback(() => {
|
||||||
@ -685,7 +686,8 @@ const PageEditor = ({
|
|||||||
// Pass current display document (which includes reordering) to get both reordering AND DOM changes
|
// Pass current display document (which includes reordering) to get both reordering AND DOM changes
|
||||||
const processedDocuments = documentManipulationService.applyDOMChangesToDocument(
|
const processedDocuments = documentManipulationService.applyDOMChangesToDocument(
|
||||||
mergedPdfDocument || displayDocument, // Original order
|
mergedPdfDocument || displayDocument, // Original order
|
||||||
displayDocument // Current display order (includes reordering)
|
displayDocument, // Current display order (includes reordering)
|
||||||
|
splitPositions // Position-based splits
|
||||||
);
|
);
|
||||||
|
|
||||||
// For apply changes, we only set the first document if it's an array (splits shouldn't affect document state)
|
// For apply changes, we only set the first document if it's an array (splits shouldn't affect document state)
|
||||||
@ -693,7 +695,7 @@ const PageEditor = ({
|
|||||||
setEditedDocument(documentToSet);
|
setEditedDocument(documentToSet);
|
||||||
|
|
||||||
console.log('Changes applied to document');
|
console.log('Changes applied to document');
|
||||||
}, [displayDocument, mergedPdfDocument]);
|
}, [displayDocument, mergedPdfDocument, splitPositions]);
|
||||||
|
|
||||||
|
|
||||||
const closePdf = useCallback(() => {
|
const closePdf = useCallback(() => {
|
||||||
@ -767,7 +769,7 @@ const PageEditor = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{displayDocument && (
|
{displayDocument && (
|
||||||
<Box p={0}>
|
<Box ref={gridContainerRef} p={0} style={{ position: 'relative' }}>
|
||||||
{/* File name and basic controls */}
|
{/* File name and basic controls */}
|
||||||
<Group mb="md" p="md" justify="space-between">
|
<Group mb="md" p="md" justify="space-between">
|
||||||
<TextInput
|
<TextInput
|
||||||
@ -805,6 +807,48 @@ const PageEditor = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Split Lines Overlay */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
pointerEvents: 'none',
|
||||||
|
zIndex: 10
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Array.from(splitPositions).map((position) => {
|
||||||
|
// Calculate the split line position based on grid layout
|
||||||
|
const ITEM_WIDTH = 320; // 20rem
|
||||||
|
const ITEM_HEIGHT = 340; // 20rem + gap
|
||||||
|
const ITEM_GAP = 24; // 1.5rem
|
||||||
|
const ITEMS_PER_ROW = 4; // Default, could be dynamic
|
||||||
|
|
||||||
|
const row = Math.floor(position / ITEMS_PER_ROW);
|
||||||
|
const col = position % ITEMS_PER_ROW;
|
||||||
|
|
||||||
|
// Position after the current item
|
||||||
|
const leftPosition = (col + 1) * (ITEM_WIDTH + ITEM_GAP) - ITEM_GAP / 2;
|
||||||
|
const topPosition = row * ITEM_HEIGHT + 100; // Offset for header controls
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={`split-${position}`}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
left: leftPosition,
|
||||||
|
top: topPosition,
|
||||||
|
width: '1px',
|
||||||
|
height: '320px', // Match item height
|
||||||
|
borderLeft: '1px dashed #3b82f6'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Pages Grid */}
|
{/* Pages Grid */}
|
||||||
<DragDropGrid
|
<DragDropGrid
|
||||||
items={displayedPages}
|
items={displayedPages}
|
||||||
@ -835,12 +879,15 @@ const PageEditor = ({
|
|||||||
ToggleSplitCommand={ToggleSplitCommand}
|
ToggleSplitCommand={ToggleSplitCommand}
|
||||||
pdfDocument={displayDocument}
|
pdfDocument={displayDocument}
|
||||||
setPdfDocument={setEditedDocument}
|
setPdfDocument={setEditedDocument}
|
||||||
|
splitPositions={splitPositions}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
<NavigationWarningModal />
|
<NavigationWarningModal />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
@ -39,6 +39,7 @@ interface PageThumbnailProps {
|
|||||||
ToggleSplitCommand: any;
|
ToggleSplitCommand: any;
|
||||||
pdfDocument: PDFDocument;
|
pdfDocument: PDFDocument;
|
||||||
setPdfDocument: (doc: PDFDocument) => void;
|
setPdfDocument: (doc: PDFDocument) => void;
|
||||||
|
splitPositions: Set<number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PageThumbnail: React.FC<PageThumbnailProps> = ({
|
const PageThumbnail: React.FC<PageThumbnailProps> = ({
|
||||||
@ -62,6 +63,7 @@ const PageThumbnail: React.FC<PageThumbnailProps> = ({
|
|||||||
ToggleSplitCommand,
|
ToggleSplitCommand,
|
||||||
pdfDocument,
|
pdfDocument,
|
||||||
setPdfDocument,
|
setPdfDocument,
|
||||||
|
splitPositions,
|
||||||
}: PageThumbnailProps) => {
|
}: PageThumbnailProps) => {
|
||||||
const [thumbnailUrl, setThumbnailUrl] = useState<string | null>(page.thumbnail);
|
const [thumbnailUrl, setThumbnailUrl] = useState<string | null>(page.thumbnail);
|
||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
@ -195,13 +197,14 @@ const PageThumbnail: React.FC<PageThumbnailProps> = ({
|
|||||||
const handleSplit = useCallback((e: React.MouseEvent) => {
|
const handleSplit = useCallback((e: React.MouseEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
// Create a command to toggle split marker
|
// Create a command to toggle split at this position
|
||||||
const command = new ToggleSplitCommand([page.id]);
|
const command = new ToggleSplitCommand(index);
|
||||||
onExecuteCommand(command);
|
onExecuteCommand(command);
|
||||||
|
|
||||||
const action = page.splitAfter ? 'removed' : 'added';
|
const hasSplit = splitPositions.has(index);
|
||||||
onSetStatus(`Split marker ${action} after page ${page.pageNumber}`);
|
const action = hasSplit ? 'removed' : 'added';
|
||||||
}, [page.pageNumber, page.id, page.splitAfter, onExecuteCommand, onSetStatus, ToggleSplitCommand]);
|
onSetStatus(`Split marker ${action} after position ${index + 1}`);
|
||||||
|
}, [index, splitPositions, onExecuteCommand, onSetStatus, ToggleSplitCommand]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -432,21 +435,6 @@ const PageThumbnail: React.FC<PageThumbnailProps> = ({
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Split indicator - shows where document will be split */}
|
|
||||||
{page.splitAfter && (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
right: '-8px',
|
|
||||||
top: '50%',
|
|
||||||
transform: 'translateY(-50%)',
|
|
||||||
width: '2px',
|
|
||||||
height: '60px',
|
|
||||||
backgroundColor: '#3b82f6',
|
|
||||||
zIndex: 5,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -9,17 +9,26 @@ export class DocumentManipulationService {
|
|||||||
* Apply all DOM changes (rotations, splits, reordering) to document state
|
* Apply all DOM changes (rotations, splits, reordering) to document state
|
||||||
* Returns single document or multiple documents if splits are present
|
* Returns single document or multiple documents if splits are present
|
||||||
*/
|
*/
|
||||||
applyDOMChangesToDocument(pdfDocument: PDFDocument, currentDisplayOrder?: PDFDocument): PDFDocument | PDFDocument[] {
|
applyDOMChangesToDocument(pdfDocument: PDFDocument, currentDisplayOrder?: PDFDocument, splitPositions?: Set<number>): PDFDocument | PDFDocument[] {
|
||||||
console.log('DocumentManipulationService: Applying DOM changes to document');
|
console.log('DocumentManipulationService: Applying DOM changes to document');
|
||||||
console.log('Original document page order:', pdfDocument.pages.map(p => p.pageNumber));
|
console.log('Original document page order:', pdfDocument.pages.map(p => p.pageNumber));
|
||||||
console.log('Current display order:', currentDisplayOrder?.pages.map(p => p.pageNumber) || 'none provided');
|
console.log('Current display order:', currentDisplayOrder?.pages.map(p => p.pageNumber) || 'none provided');
|
||||||
|
console.log('Split positions:', splitPositions ? Array.from(splitPositions).sort() : 'none');
|
||||||
|
|
||||||
// Use current display order (from React state) if provided, otherwise use original order
|
// Use current display order (from React state) if provided, otherwise use original order
|
||||||
const baseDocument = currentDisplayOrder || pdfDocument;
|
const baseDocument = currentDisplayOrder || pdfDocument;
|
||||||
console.log('Using page order:', baseDocument.pages.map(p => p.pageNumber));
|
console.log('Using page order:', baseDocument.pages.map(p => p.pageNumber));
|
||||||
|
|
||||||
// Apply DOM changes to each page (rotation, split markers)
|
// Apply DOM changes to each page (rotation only now, splits are position-based)
|
||||||
const updatedPages = baseDocument.pages.map(page => this.applyPageChanges(page));
|
let updatedPages = baseDocument.pages.map(page => this.applyPageChanges(page));
|
||||||
|
|
||||||
|
// Convert position-based splits to page-based splits for export
|
||||||
|
if (splitPositions && splitPositions.size > 0) {
|
||||||
|
updatedPages = updatedPages.map((page, index) => ({
|
||||||
|
...page,
|
||||||
|
splitAfter: splitPositions.has(index)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
// Create final document with reordered pages and applied changes
|
// Create final document with reordered pages and applied changes
|
||||||
const finalDocument = {
|
const finalDocument = {
|
||||||
@ -28,7 +37,7 @@ export class DocumentManipulationService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Check for splits and return multiple documents if needed
|
// Check for splits and return multiple documents if needed
|
||||||
if (this.hasSplitMarkers(finalDocument)) {
|
if (splitPositions && splitPositions.size > 0) {
|
||||||
return this.createSplitDocuments(finalDocument);
|
return this.createSplitDocuments(finalDocument);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user