mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-07-27 07:35:22 +00:00
Refactor
This commit is contained in:
parent
d981968e0f
commit
868969192b
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Button, Stack, Text, Group } from '@mantine/core';
|
||||
|
||||
const DeepLinks: React.FC = () => {
|
||||
const DeepLinks = () => {
|
||||
const commonLinks = [
|
||||
{
|
||||
name: "Split PDF Pages 1-5",
|
||||
|
@ -14,7 +14,7 @@ interface FileCardProps {
|
||||
onDoubleClick?: () => void;
|
||||
}
|
||||
|
||||
const FileCard: React.FC<FileCardProps> = ({ file, onRemove, onDoubleClick }) => {
|
||||
const FileCard = ({ file, onRemove, onDoubleClick }: FileCardProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { thumbnail: thumb, isGenerating } = useIndexedDBThumbnail(file);
|
||||
|
||||
|
@ -23,13 +23,13 @@ interface FileManagerProps {
|
||||
setCurrentView?: (view: string) => void;
|
||||
}
|
||||
|
||||
const FileManager: React.FC<FileManagerProps> = ({
|
||||
const FileManager = ({
|
||||
files = [],
|
||||
setFiles,
|
||||
allowMultiple = true,
|
||||
setPdfFile,
|
||||
setCurrentView,
|
||||
}) => {
|
||||
}: FileManagerProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [storageStats, setStorageStats] = useState<StorageStats | null>(null);
|
||||
|
@ -5,7 +5,7 @@ import { supportedLanguages } from '../i18n';
|
||||
import LanguageIcon from '@mui/icons-material/Language';
|
||||
import styles from './LanguageSelector.module.css';
|
||||
|
||||
const LanguageSelector: React.FC = () => {
|
||||
const LanguageSelector = () => {
|
||||
const { i18n } = useTranslation();
|
||||
const theme = useMantineTheme();
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
|
@ -42,14 +42,31 @@ export interface PageEditorProps {
|
||||
setFile?: (file: { file: File; url: string } | null) => void;
|
||||
downloadUrl?: string | null;
|
||||
setDownloadUrl?: (url: string | null) => void;
|
||||
|
||||
// Optional callbacks to expose internal functions
|
||||
onFunctionsReady?: (functions: {
|
||||
handleUndo: () => void;
|
||||
handleRedo: () => void;
|
||||
canUndo: boolean;
|
||||
canRedo: boolean;
|
||||
handleRotate: (direction: 'left' | 'right') => void;
|
||||
handleDelete: () => void;
|
||||
handleSplit: () => void;
|
||||
showExportPreview: (selectedOnly: boolean) => void;
|
||||
exportLoading: boolean;
|
||||
selectionMode: boolean;
|
||||
selectedPages: string[];
|
||||
closePdf: () => void;
|
||||
}) => void;
|
||||
}
|
||||
|
||||
const PageEditor: React.FC<PageEditorProps> = ({
|
||||
const PageEditor = ({
|
||||
file,
|
||||
setFile,
|
||||
downloadUrl,
|
||||
setDownloadUrl,
|
||||
}) => {
|
||||
onFunctionsReady,
|
||||
}: PageEditorProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { processPDFFile, loading: pdfLoading } = usePDFProcessor();
|
||||
|
||||
@ -207,7 +224,7 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
|
||||
const handleDragStart = useCallback((pageId: string) => {
|
||||
setDraggedPage(pageId);
|
||||
|
||||
|
||||
// Check if this is a multi-page drag in selection mode
|
||||
if (selectionMode && selectedPages.includes(pageId) && selectedPages.length > 1) {
|
||||
setMultiPageDrag({
|
||||
@ -231,7 +248,7 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
e.preventDefault();
|
||||
|
||||
if (!draggedPage) return;
|
||||
|
||||
|
||||
// Update drag position for multi-page indicator
|
||||
if (multiPageDrag) {
|
||||
setDragPosition({ x: e.clientX, y: e.clientY });
|
||||
@ -274,12 +291,12 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
|
||||
const animateReorder = useCallback((pageId: string, targetIndex: number) => {
|
||||
if (!pdfDocument || isAnimating) return;
|
||||
|
||||
|
||||
// In selection mode, if the dragged page is selected, move all selected pages
|
||||
const pagesToMove = selectionMode && selectedPages.includes(pageId)
|
||||
? selectedPages
|
||||
const pagesToMove = selectionMode && selectedPages.includes(pageId)
|
||||
? selectedPages
|
||||
: [pageId];
|
||||
|
||||
|
||||
const originalIndex = pdfDocument.pages.findIndex(p => p.id === pageId);
|
||||
if (originalIndex === -1 || originalIndex === targetIndex) return;
|
||||
|
||||
@ -310,11 +327,11 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
const newPositions = new Map<string, { x: number; y: number }>();
|
||||
|
||||
|
||||
// Get the updated document from the state after command execution
|
||||
// The command has already updated the document, so we need to get the new order
|
||||
const currentDoc = pdfDocument; // This should be the updated version after command
|
||||
|
||||
|
||||
currentDoc.pages.forEach((page) => {
|
||||
const element = pageRefs.current.get(page.id);
|
||||
if (element) {
|
||||
@ -328,18 +345,18 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
const element = pageRefs.current.get(page.id);
|
||||
const currentPos = currentPositions.get(page.id);
|
||||
const newPos = newPositions.get(page.id);
|
||||
|
||||
|
||||
if (element && currentPos && newPos) {
|
||||
const deltaX = currentPos.x - newPos.x;
|
||||
const deltaY = currentPos.y - newPos.y;
|
||||
|
||||
|
||||
// Apply initial transform (from new position back to old position)
|
||||
element.style.transform = `translate(${deltaX}px, ${deltaY}px)`;
|
||||
element.style.transition = 'none';
|
||||
|
||||
|
||||
// Force reflow
|
||||
element.offsetHeight;
|
||||
|
||||
|
||||
// Animate to final position
|
||||
element.style.transition = 'transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94)';
|
||||
element.style.transform = 'translate(0px, 0px)';
|
||||
@ -374,12 +391,12 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
}
|
||||
|
||||
animateReorder(draggedPage, targetIndex);
|
||||
|
||||
|
||||
setDraggedPage(null);
|
||||
setDropTarget(null);
|
||||
setMultiPageDrag(null);
|
||||
setDragPosition(null);
|
||||
|
||||
|
||||
const moveCount = multiPageDrag ? multiPageDrag.count : 1;
|
||||
setStatus(`${moveCount > 1 ? `${moveCount} pages` : 'Page'} reordered`);
|
||||
}, [draggedPage, pdfDocument, animateReorder, multiPageDrag]);
|
||||
@ -394,8 +411,8 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
if (!pdfDocument) return;
|
||||
|
||||
const rotation = direction === 'left' ? -90 : 90;
|
||||
const pagesToRotate = selectionMode
|
||||
? selectedPages
|
||||
const pagesToRotate = selectionMode
|
||||
? selectedPages
|
||||
: pdfDocument.pages.map(p => p.id);
|
||||
|
||||
if (selectionMode && selectedPages.length === 0) return;
|
||||
@ -415,8 +432,8 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
const handleDelete = useCallback(() => {
|
||||
if (!pdfDocument) return;
|
||||
|
||||
const pagesToDelete = selectionMode
|
||||
? selectedPages
|
||||
const pagesToDelete = selectionMode
|
||||
? selectedPages
|
||||
: pdfDocument.pages.map(p => p.id);
|
||||
|
||||
if (selectionMode && selectedPages.length === 0) return;
|
||||
@ -438,8 +455,8 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
const handleSplit = useCallback(() => {
|
||||
if (!pdfDocument) return;
|
||||
|
||||
const pagesToSplit = selectionMode
|
||||
? selectedPages
|
||||
const pagesToSplit = selectionMode
|
||||
? selectedPages
|
||||
: pdfDocument.pages.map(p => p.id);
|
||||
|
||||
if (selectionMode && selectedPages.length === 0) return;
|
||||
@ -521,6 +538,45 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
}
|
||||
}, [redo]);
|
||||
|
||||
const closePdf = useCallback(() => {
|
||||
setPdfDocument(null);
|
||||
setFile && setFile(null);
|
||||
}, [setFile]);
|
||||
|
||||
// Expose functions to parent component
|
||||
useEffect(() => {
|
||||
if (onFunctionsReady) {
|
||||
onFunctionsReady({
|
||||
handleUndo,
|
||||
handleRedo,
|
||||
canUndo,
|
||||
canRedo,
|
||||
handleRotate,
|
||||
handleDelete,
|
||||
handleSplit,
|
||||
showExportPreview,
|
||||
exportLoading,
|
||||
selectionMode,
|
||||
selectedPages,
|
||||
closePdf,
|
||||
});
|
||||
}
|
||||
}, [
|
||||
onFunctionsReady,
|
||||
handleUndo,
|
||||
handleRedo,
|
||||
canUndo,
|
||||
canRedo,
|
||||
handleRotate,
|
||||
handleDelete,
|
||||
handleSplit,
|
||||
showExportPreview,
|
||||
exportLoading,
|
||||
selectionMode,
|
||||
selectedPages,
|
||||
closePdf
|
||||
]);
|
||||
|
||||
if (!pdfDocument) {
|
||||
return (
|
||||
<Box pos="relative" h="100vh" style={{ overflow: 'auto' }}>
|
||||
@ -580,7 +636,7 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
|
||||
.multi-drag-indicator {
|
||||
position: fixed;
|
||||
background: rgba(59, 130, 246, 0.9);
|
||||
@ -595,7 +651,7 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
transform: translate(-50%, -50%);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
@ -616,7 +672,7 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
placeholder="Enter filename"
|
||||
style={{ minWidth: 200 }}
|
||||
/>
|
||||
<Button
|
||||
<Button
|
||||
onClick={toggleSelectionMode}
|
||||
variant={selectionMode ? "filled" : "outline"}
|
||||
color={selectionMode ? "blue" : "gray"}
|
||||
@ -708,7 +764,7 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
hover:shadow-md
|
||||
transition-all
|
||||
relative
|
||||
${selectionMode
|
||||
${selectionMode
|
||||
? 'bg-white hover:bg-gray-50'
|
||||
: 'bg-white hover:bg-gray-50'}
|
||||
${draggedPage === page.id ? 'opacity-50 scale-95' : ''}
|
||||
@ -768,20 +824,35 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
<div className="page-container w-[90%] h-[90%]">
|
||||
<img
|
||||
src={page.thumbnail}
|
||||
alt={`Page ${page.pageNumber}`}
|
||||
{/* Image wrapper with simulated border */}
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'contain',
|
||||
borderRadius: 4,
|
||||
transform: `rotate(${page.rotation}deg)`,
|
||||
transition: 'transform 0.3s ease-in-out'
|
||||
backgroundColor: 'var(--mantine-color-gray-1)',
|
||||
borderRadius: 6,
|
||||
border: '1px solid var(--mantine-color-gray-3)',
|
||||
padding: 4,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<img
|
||||
src={page.thumbnail}
|
||||
alt={`Page ${page.pageNumber}`}
|
||||
style={{
|
||||
maxWidth: '100%',
|
||||
maxHeight: '100%',
|
||||
objectFit: 'contain',
|
||||
borderRadius: 2,
|
||||
transform: `rotate(${page.rotation}deg)`,
|
||||
transition: 'transform 0.3s ease-in-out'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Page number overlay - shows on hover */}
|
||||
<Text
|
||||
@ -985,141 +1056,6 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Floating control bar */}
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
left: '50%',
|
||||
bottom: '20px',
|
||||
transform: 'translateX(-50%)',
|
||||
zIndex: 50,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
pointerEvents: 'none',
|
||||
background: 'transparent',
|
||||
}}
|
||||
>
|
||||
<Paper
|
||||
radius="xl"
|
||||
shadow="lg"
|
||||
p={16}
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 12,
|
||||
borderRadius: 32,
|
||||
boxShadow: '0 8px 32px rgba(0,0,0,0.12)',
|
||||
pointerEvents: 'auto',
|
||||
minWidth: 400,
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
{/* Close PDF */}
|
||||
<Tooltip label="Close PDF">
|
||||
<ActionIcon
|
||||
onClick={() => {
|
||||
setPdfDocument(null);
|
||||
setFile && setFile(null);
|
||||
}}
|
||||
color="red"
|
||||
variant="light"
|
||||
size="lg"
|
||||
>
|
||||
<CloseIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
|
||||
<div style={{ width: 1, height: 28, backgroundColor: 'var(--mantine-color-gray-3)', margin: '0 8px' }} />
|
||||
|
||||
{/* Undo/Redo */}
|
||||
<Tooltip label="Undo">
|
||||
<ActionIcon onClick={handleUndo} disabled={!canUndo} size="lg">
|
||||
<UndoIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label="Redo">
|
||||
<ActionIcon onClick={handleRedo} disabled={!canRedo} size="lg">
|
||||
<RedoIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
|
||||
<div style={{ width: 1, height: 28, backgroundColor: 'var(--mantine-color-gray-3)', margin: '0 8px' }} />
|
||||
|
||||
{/* Page Operations */}
|
||||
<Tooltip label={selectionMode ? "Rotate Selected Left" : "Rotate All Left"}>
|
||||
<ActionIcon
|
||||
onClick={() => handleRotate('left')}
|
||||
disabled={selectionMode && selectedPages.length === 0}
|
||||
variant={selectionMode && selectedPages.length > 0 ? "light" : "default"}
|
||||
color={selectionMode && selectedPages.length > 0 ? "blue" : undefined}
|
||||
size="lg"
|
||||
>
|
||||
<RotateLeftIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={selectionMode ? "Rotate Selected Right" : "Rotate All Right"}>
|
||||
<ActionIcon
|
||||
onClick={() => handleRotate('right')}
|
||||
disabled={selectionMode && selectedPages.length === 0}
|
||||
variant={selectionMode && selectedPages.length > 0 ? "light" : "default"}
|
||||
color={selectionMode && selectedPages.length > 0 ? "blue" : undefined}
|
||||
size="lg"
|
||||
>
|
||||
<RotateRightIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={selectionMode ? "Delete Selected" : "Delete All"}>
|
||||
<ActionIcon
|
||||
onClick={handleDelete}
|
||||
disabled={selectionMode && selectedPages.length === 0}
|
||||
color="red"
|
||||
variant={selectionMode && selectedPages.length > 0 ? "light" : "default"}
|
||||
size="lg"
|
||||
>
|
||||
<DeleteIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={selectionMode ? "Split Selected" : "Split All"}>
|
||||
<ActionIcon
|
||||
onClick={handleSplit}
|
||||
disabled={selectionMode && selectedPages.length === 0}
|
||||
variant={selectionMode && selectedPages.length > 0 ? "light" : "default"}
|
||||
color={selectionMode && selectedPages.length > 0 ? "blue" : undefined}
|
||||
size="lg"
|
||||
>
|
||||
<ContentCutIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
|
||||
<div style={{ width: 1, height: 28, backgroundColor: 'var(--mantine-color-gray-3)', margin: '0 8px' }} />
|
||||
|
||||
{/* Export Controls */}
|
||||
{selectionMode && selectedPages.length > 0 && (
|
||||
<Tooltip label="Export Selected">
|
||||
<ActionIcon
|
||||
onClick={() => showExportPreview(true)}
|
||||
disabled={exportLoading}
|
||||
color="blue"
|
||||
variant="light"
|
||||
size="lg"
|
||||
>
|
||||
<DownloadIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Tooltip label="Export All">
|
||||
<ActionIcon
|
||||
onClick={() => showExportPreview(false)}
|
||||
disabled={exportLoading}
|
||||
color="green"
|
||||
variant="light"
|
||||
size="lg"
|
||||
>
|
||||
<DownloadIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Paper>
|
||||
</div>
|
||||
|
||||
</Box>
|
||||
|
||||
|
191
frontend/src/components/PageEditorControls.tsx
Normal file
191
frontend/src/components/PageEditorControls.tsx
Normal file
@ -0,0 +1,191 @@
|
||||
import React from "react";
|
||||
import {
|
||||
Tooltip,
|
||||
ActionIcon,
|
||||
Paper
|
||||
} from "@mantine/core";
|
||||
import UndoIcon from "@mui/icons-material/Undo";
|
||||
import RedoIcon from "@mui/icons-material/Redo";
|
||||
import ContentCutIcon from "@mui/icons-material/ContentCut";
|
||||
import DownloadIcon from "@mui/icons-material/Download";
|
||||
import RotateLeftIcon from "@mui/icons-material/RotateLeft";
|
||||
import RotateRightIcon from "@mui/icons-material/RotateRight";
|
||||
import DeleteIcon from "@mui/icons-material/Delete";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
|
||||
interface PageEditorControlsProps {
|
||||
// Close/Reset functions
|
||||
onClosePdf: () => void;
|
||||
|
||||
// Undo/Redo
|
||||
onUndo: () => void;
|
||||
onRedo: () => void;
|
||||
canUndo: boolean;
|
||||
canRedo: boolean;
|
||||
|
||||
// Page operations
|
||||
onRotate: (direction: 'left' | 'right') => void;
|
||||
onDelete: () => void;
|
||||
onSplit: () => void;
|
||||
|
||||
// Export functions
|
||||
onExportSelected: () => void;
|
||||
onExportAll: () => void;
|
||||
exportLoading: boolean;
|
||||
|
||||
// Selection state
|
||||
selectionMode: boolean;
|
||||
selectedPages: string[];
|
||||
}
|
||||
|
||||
const PageEditorControls = ({
|
||||
onClosePdf,
|
||||
onUndo,
|
||||
onRedo,
|
||||
canUndo,
|
||||
canRedo,
|
||||
onRotate,
|
||||
onDelete,
|
||||
onSplit,
|
||||
onExportSelected,
|
||||
onExportAll,
|
||||
exportLoading,
|
||||
selectionMode,
|
||||
selectedPages
|
||||
}: PageEditorControlsProps) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
left: '50%',
|
||||
bottom: '20px',
|
||||
transform: 'translateX(-50%)',
|
||||
zIndex: 50,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
pointerEvents: 'none',
|
||||
background: 'transparent',
|
||||
}}
|
||||
>
|
||||
<Paper
|
||||
radius="xl"
|
||||
shadow="lg"
|
||||
p={16}
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 12,
|
||||
borderRadius: 32,
|
||||
boxShadow: '0 8px 32px rgba(0,0,0,0.12)',
|
||||
pointerEvents: 'auto',
|
||||
minWidth: 400,
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
{/* Close PDF */}
|
||||
<Tooltip label="Close PDF">
|
||||
<ActionIcon
|
||||
onClick={onClosePdf}
|
||||
color="red"
|
||||
variant="light"
|
||||
size="lg"
|
||||
>
|
||||
<CloseIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
|
||||
<div style={{ width: 1, height: 28, backgroundColor: 'var(--mantine-color-gray-3)', margin: '0 8px' }} />
|
||||
|
||||
{/* Undo/Redo */}
|
||||
<Tooltip label="Undo">
|
||||
<ActionIcon onClick={onUndo} disabled={!canUndo} size="lg">
|
||||
<UndoIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label="Redo">
|
||||
<ActionIcon onClick={onRedo} disabled={!canRedo} size="lg">
|
||||
<RedoIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
|
||||
<div style={{ width: 1, height: 28, backgroundColor: 'var(--mantine-color-gray-3)', margin: '0 8px' }} />
|
||||
|
||||
{/* Page Operations */}
|
||||
<Tooltip label={selectionMode ? "Rotate Selected Left" : "Rotate All Left"}>
|
||||
<ActionIcon
|
||||
onClick={() => onRotate('left')}
|
||||
disabled={selectionMode && selectedPages.length === 0}
|
||||
variant={selectionMode && selectedPages.length > 0 ? "light" : "default"}
|
||||
color={selectionMode && selectedPages.length > 0 ? "blue" : undefined}
|
||||
size="lg"
|
||||
>
|
||||
<RotateLeftIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={selectionMode ? "Rotate Selected Right" : "Rotate All Right"}>
|
||||
<ActionIcon
|
||||
onClick={() => onRotate('right')}
|
||||
disabled={selectionMode && selectedPages.length === 0}
|
||||
variant={selectionMode && selectedPages.length > 0 ? "light" : "default"}
|
||||
color={selectionMode && selectedPages.length > 0 ? "blue" : undefined}
|
||||
size="lg"
|
||||
>
|
||||
<RotateRightIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={selectionMode ? "Delete Selected" : "Delete All"}>
|
||||
<ActionIcon
|
||||
onClick={onDelete}
|
||||
disabled={selectionMode && selectedPages.length === 0}
|
||||
color="red"
|
||||
variant={selectionMode && selectedPages.length > 0 ? "light" : "default"}
|
||||
size="lg"
|
||||
>
|
||||
<DeleteIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={selectionMode ? "Split Selected" : "Split All"}>
|
||||
<ActionIcon
|
||||
onClick={onSplit}
|
||||
disabled={selectionMode && selectedPages.length === 0}
|
||||
variant={selectionMode && selectedPages.length > 0 ? "light" : "default"}
|
||||
color={selectionMode && selectedPages.length > 0 ? "blue" : undefined}
|
||||
size="lg"
|
||||
>
|
||||
<ContentCutIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
|
||||
<div style={{ width: 1, height: 28, backgroundColor: 'var(--mantine-color-gray-3)', margin: '0 8px' }} />
|
||||
|
||||
{/* Export Controls */}
|
||||
{selectionMode && selectedPages.length > 0 && (
|
||||
<Tooltip label="Export Selected">
|
||||
<ActionIcon
|
||||
onClick={onExportSelected}
|
||||
disabled={exportLoading}
|
||||
color="blue"
|
||||
variant="light"
|
||||
size="lg"
|
||||
>
|
||||
<DownloadIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Tooltip label="Export All">
|
||||
<ActionIcon
|
||||
onClick={onExportAll}
|
||||
disabled={exportLoading}
|
||||
color="green"
|
||||
variant="light"
|
||||
size="lg"
|
||||
>
|
||||
<DownloadIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Paper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageEditorControls;
|
@ -17,14 +17,14 @@ interface QuickAccessBarProps {
|
||||
readerMode: boolean;
|
||||
}
|
||||
|
||||
const QuickAccessBar: React.FC<QuickAccessBarProps> = ({
|
||||
const QuickAccessBar = ({
|
||||
onToolsClick,
|
||||
onReaderToggle,
|
||||
selectedToolKey,
|
||||
toolRegistry,
|
||||
leftPanelView,
|
||||
readerMode,
|
||||
}) => {
|
||||
}: QuickAccessBarProps) => {
|
||||
const { isRainbowMode } = useRainbowThemeContext();
|
||||
|
||||
return (
|
||||
|
@ -14,12 +14,12 @@ interface StorageStatsCardProps {
|
||||
onReloadFiles: () => void;
|
||||
}
|
||||
|
||||
const StorageStatsCard: React.FC<StorageStatsCardProps> = ({
|
||||
const StorageStatsCard = ({
|
||||
storageStats,
|
||||
filesCount,
|
||||
onClearAll,
|
||||
onReloadFiles,
|
||||
}) => {
|
||||
}: StorageStatsCardProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!storageStats) return null;
|
||||
|
@ -17,7 +17,7 @@ interface ToolPickerProps {
|
||||
toolRegistry: ToolRegistry;
|
||||
}
|
||||
|
||||
const ToolPicker: React.FC<ToolPickerProps> = ({ selectedToolKey, onSelect, toolRegistry }) => {
|
||||
const ToolPicker = ({ selectedToolKey, onSelect, toolRegistry }: ToolPickerProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
|
@ -12,7 +12,7 @@ interface ToolRendererProps {
|
||||
updateParams: (params: any) => void;
|
||||
}
|
||||
|
||||
const ToolRenderer: React.FC<ToolRendererProps> = ({
|
||||
const ToolRenderer = ({
|
||||
selectedToolKey,
|
||||
selectedTool,
|
||||
pdfFile,
|
||||
@ -21,7 +21,7 @@ const ToolRenderer: React.FC<ToolRendererProps> = ({
|
||||
setDownloadUrl,
|
||||
toolParams,
|
||||
updateParams,
|
||||
}) => {
|
||||
}: ToolRendererProps) => {
|
||||
if (!selectedTool || !selectedTool.component) {
|
||||
return <div>Tool not found</div>;
|
||||
}
|
||||
|
@ -43,10 +43,10 @@ interface TopControlsProps {
|
||||
setCurrentView: (view: string) => void;
|
||||
}
|
||||
|
||||
const TopControls: React.FC<TopControlsProps> = ({
|
||||
const TopControls = ({
|
||||
currentView,
|
||||
setCurrentView,
|
||||
}) => {
|
||||
}: TopControlsProps) => {
|
||||
const { themeMode, isRainbowMode, isToggleDisabled, toggleTheme } = useRainbowThemeContext();
|
||||
|
||||
const getThemeIcon = () => {
|
||||
|
@ -25,9 +25,9 @@ interface LazyPageImageProps {
|
||||
setPageRef: (index: number, ref: HTMLImageElement | null) => void;
|
||||
}
|
||||
|
||||
const LazyPageImage: React.FC<LazyPageImageProps> = ({
|
||||
const LazyPageImage = ({
|
||||
pageIndex, zoom, theme, isFirst, renderPage, pageImages, setPageRef
|
||||
}) => {
|
||||
}: LazyPageImageProps) => {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const [imageUrl, setImageUrl] = useState<string | null>(pageImages[pageIndex]);
|
||||
const imgRef = useRef<HTMLImageElement>(null);
|
||||
@ -129,12 +129,12 @@ export interface ViewerProps {
|
||||
setSidebarsVisible: (v: boolean) => void;
|
||||
}
|
||||
|
||||
const Viewer: React.FC<ViewerProps> = ({
|
||||
const Viewer = ({
|
||||
pdfFile,
|
||||
setPdfFile,
|
||||
sidebarsVisible,
|
||||
setSidebarsVisible,
|
||||
}) => {
|
||||
}: ViewerProps) => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useMantineTheme();
|
||||
const [numPages, setNumPages] = useState<number>(0);
|
||||
|
@ -15,6 +15,7 @@ import SplitPdfPanel from "../tools/Split";
|
||||
import CompressPdfPanel from "../tools/Compress";
|
||||
import MergePdfPanel from "../tools/Merge";
|
||||
import PageEditor from "../components/PageEditor";
|
||||
import PageEditorControls from "../components/PageEditorControls";
|
||||
import Viewer from "../components/Viewer";
|
||||
import TopControls from "../components/TopControls";
|
||||
import ToolRenderer from "../components/ToolRenderer";
|
||||
@ -55,6 +56,9 @@ export default function HomePage() {
|
||||
const [sidebarsVisible, setSidebarsVisible] = useState(true);
|
||||
const [leftPanelView, setLeftPanelView] = useState<'toolPicker' | 'toolContent'>('toolPicker');
|
||||
const [readerMode, setReaderMode] = useState(false);
|
||||
|
||||
// Page editor functions
|
||||
const [pageEditorFunctions, setPageEditorFunctions] = useState<any>(null);
|
||||
|
||||
// URL parameter management
|
||||
const { toolParams, updateParams } = useToolParams(selectedToolKey, currentView);
|
||||
@ -204,12 +208,32 @@ export default function HomePage() {
|
||||
setSidebarsVisible={setSidebarsVisible}
|
||||
/>
|
||||
) : currentView === "pageEditor" ? (
|
||||
<PageEditor
|
||||
file={pdfFile}
|
||||
setFile={setPdfFile}
|
||||
downloadUrl={downloadUrl}
|
||||
setDownloadUrl={setDownloadUrl}
|
||||
/>
|
||||
<>
|
||||
<PageEditor
|
||||
file={pdfFile}
|
||||
setFile={setPdfFile}
|
||||
downloadUrl={downloadUrl}
|
||||
setDownloadUrl={setDownloadUrl}
|
||||
onFunctionsReady={setPageEditorFunctions}
|
||||
/>
|
||||
{pdfFile && pageEditorFunctions && (
|
||||
<PageEditorControls
|
||||
onClosePdf={pageEditorFunctions.closePdf}
|
||||
onUndo={pageEditorFunctions.handleUndo}
|
||||
onRedo={pageEditorFunctions.handleRedo}
|
||||
canUndo={pageEditorFunctions.canUndo}
|
||||
canRedo={pageEditorFunctions.canRedo}
|
||||
onRotate={pageEditorFunctions.handleRotate}
|
||||
onDelete={pageEditorFunctions.handleDelete}
|
||||
onSplit={pageEditorFunctions.handleSplit}
|
||||
onExportSelected={() => pageEditorFunctions.showExportPreview(true)}
|
||||
onExportAll={() => pageEditorFunctions.showExportPreview(false)}
|
||||
exportLoading={pageEditorFunctions.exportLoading}
|
||||
selectionMode={pageEditorFunctions.selectionMode}
|
||||
selectedPages={pageEditorFunctions.selectedPages}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<FileManager
|
||||
files={files}
|
||||
|
Loading…
x
Reference in New Issue
Block a user