mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-07-29 08:35:30 +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 { Link } from 'react-router-dom';
|
||||||
import { Button, Stack, Text, Group } from '@mantine/core';
|
import { Button, Stack, Text, Group } from '@mantine/core';
|
||||||
|
|
||||||
const DeepLinks: React.FC = () => {
|
const DeepLinks = () => {
|
||||||
const commonLinks = [
|
const commonLinks = [
|
||||||
{
|
{
|
||||||
name: "Split PDF Pages 1-5",
|
name: "Split PDF Pages 1-5",
|
||||||
|
@ -14,7 +14,7 @@ interface FileCardProps {
|
|||||||
onDoubleClick?: () => void;
|
onDoubleClick?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FileCard: React.FC<FileCardProps> = ({ file, onRemove, onDoubleClick }) => {
|
const FileCard = ({ file, onRemove, onDoubleClick }: FileCardProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { thumbnail: thumb, isGenerating } = useIndexedDBThumbnail(file);
|
const { thumbnail: thumb, isGenerating } = useIndexedDBThumbnail(file);
|
||||||
|
|
||||||
|
@ -23,13 +23,13 @@ interface FileManagerProps {
|
|||||||
setCurrentView?: (view: string) => void;
|
setCurrentView?: (view: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FileManager: React.FC<FileManagerProps> = ({
|
const FileManager = ({
|
||||||
files = [],
|
files = [],
|
||||||
setFiles,
|
setFiles,
|
||||||
allowMultiple = true,
|
allowMultiple = true,
|
||||||
setPdfFile,
|
setPdfFile,
|
||||||
setCurrentView,
|
setCurrentView,
|
||||||
}) => {
|
}: FileManagerProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [storageStats, setStorageStats] = useState<StorageStats | null>(null);
|
const [storageStats, setStorageStats] = useState<StorageStats | null>(null);
|
||||||
|
@ -5,7 +5,7 @@ import { supportedLanguages } from '../i18n';
|
|||||||
import LanguageIcon from '@mui/icons-material/Language';
|
import LanguageIcon from '@mui/icons-material/Language';
|
||||||
import styles from './LanguageSelector.module.css';
|
import styles from './LanguageSelector.module.css';
|
||||||
|
|
||||||
const LanguageSelector: React.FC = () => {
|
const LanguageSelector = () => {
|
||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
const theme = useMantineTheme();
|
const theme = useMantineTheme();
|
||||||
const { colorScheme } = useMantineColorScheme();
|
const { colorScheme } = useMantineColorScheme();
|
||||||
|
@ -42,14 +42,31 @@ export interface PageEditorProps {
|
|||||||
setFile?: (file: { file: File; url: string } | null) => void;
|
setFile?: (file: { file: File; url: string } | null) => void;
|
||||||
downloadUrl?: string | null;
|
downloadUrl?: string | null;
|
||||||
setDownloadUrl?: (url: string | null) => void;
|
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,
|
file,
|
||||||
setFile,
|
setFile,
|
||||||
downloadUrl,
|
downloadUrl,
|
||||||
setDownloadUrl,
|
setDownloadUrl,
|
||||||
}) => {
|
onFunctionsReady,
|
||||||
|
}: PageEditorProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { processPDFFile, loading: pdfLoading } = usePDFProcessor();
|
const { processPDFFile, loading: pdfLoading } = usePDFProcessor();
|
||||||
|
|
||||||
@ -521,6 +538,45 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
|||||||
}
|
}
|
||||||
}, [redo]);
|
}, [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) {
|
if (!pdfDocument) {
|
||||||
return (
|
return (
|
||||||
<Box pos="relative" h="100vh" style={{ overflow: 'auto' }}>
|
<Box pos="relative" h="100vh" style={{ overflow: 'auto' }}>
|
||||||
@ -770,18 +826,33 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="page-container w-[90%] h-[90%]">
|
<div className="page-container w-[90%] h-[90%]">
|
||||||
|
{/* Image wrapper with simulated border */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
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
|
<img
|
||||||
src={page.thumbnail}
|
src={page.thumbnail}
|
||||||
alt={`Page ${page.pageNumber}`}
|
alt={`Page ${page.pageNumber}`}
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
maxWidth: '100%',
|
||||||
height: '100%',
|
maxHeight: '100%',
|
||||||
objectFit: 'contain',
|
objectFit: 'contain',
|
||||||
borderRadius: 4,
|
borderRadius: 2,
|
||||||
transform: `rotate(${page.rotation}deg)`,
|
transform: `rotate(${page.rotation}deg)`,
|
||||||
transition: 'transform 0.3s ease-in-out'
|
transition: 'transform 0.3s ease-in-out'
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Page number overlay - shows on hover */}
|
{/* Page number overlay - shows on hover */}
|
||||||
<Text
|
<Text
|
||||||
@ -985,141 +1056,6 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</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>
|
</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;
|
readerMode: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QuickAccessBar: React.FC<QuickAccessBarProps> = ({
|
const QuickAccessBar = ({
|
||||||
onToolsClick,
|
onToolsClick,
|
||||||
onReaderToggle,
|
onReaderToggle,
|
||||||
selectedToolKey,
|
selectedToolKey,
|
||||||
toolRegistry,
|
toolRegistry,
|
||||||
leftPanelView,
|
leftPanelView,
|
||||||
readerMode,
|
readerMode,
|
||||||
}) => {
|
}: QuickAccessBarProps) => {
|
||||||
const { isRainbowMode } = useRainbowThemeContext();
|
const { isRainbowMode } = useRainbowThemeContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -14,12 +14,12 @@ interface StorageStatsCardProps {
|
|||||||
onReloadFiles: () => void;
|
onReloadFiles: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StorageStatsCard: React.FC<StorageStatsCardProps> = ({
|
const StorageStatsCard = ({
|
||||||
storageStats,
|
storageStats,
|
||||||
filesCount,
|
filesCount,
|
||||||
onClearAll,
|
onClearAll,
|
||||||
onReloadFiles,
|
onReloadFiles,
|
||||||
}) => {
|
}: StorageStatsCardProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
if (!storageStats) return null;
|
if (!storageStats) return null;
|
||||||
|
@ -17,7 +17,7 @@ interface ToolPickerProps {
|
|||||||
toolRegistry: ToolRegistry;
|
toolRegistry: ToolRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ToolPicker: React.FC<ToolPickerProps> = ({ selectedToolKey, onSelect, toolRegistry }) => {
|
const ToolPicker = ({ selectedToolKey, onSelect, toolRegistry }: ToolPickerProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ interface ToolRendererProps {
|
|||||||
updateParams: (params: any) => void;
|
updateParams: (params: any) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ToolRenderer: React.FC<ToolRendererProps> = ({
|
const ToolRenderer = ({
|
||||||
selectedToolKey,
|
selectedToolKey,
|
||||||
selectedTool,
|
selectedTool,
|
||||||
pdfFile,
|
pdfFile,
|
||||||
@ -21,7 +21,7 @@ const ToolRenderer: React.FC<ToolRendererProps> = ({
|
|||||||
setDownloadUrl,
|
setDownloadUrl,
|
||||||
toolParams,
|
toolParams,
|
||||||
updateParams,
|
updateParams,
|
||||||
}) => {
|
}: ToolRendererProps) => {
|
||||||
if (!selectedTool || !selectedTool.component) {
|
if (!selectedTool || !selectedTool.component) {
|
||||||
return <div>Tool not found</div>;
|
return <div>Tool not found</div>;
|
||||||
}
|
}
|
||||||
|
@ -43,10 +43,10 @@ interface TopControlsProps {
|
|||||||
setCurrentView: (view: string) => void;
|
setCurrentView: (view: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TopControls: React.FC<TopControlsProps> = ({
|
const TopControls = ({
|
||||||
currentView,
|
currentView,
|
||||||
setCurrentView,
|
setCurrentView,
|
||||||
}) => {
|
}: TopControlsProps) => {
|
||||||
const { themeMode, isRainbowMode, isToggleDisabled, toggleTheme } = useRainbowThemeContext();
|
const { themeMode, isRainbowMode, isToggleDisabled, toggleTheme } = useRainbowThemeContext();
|
||||||
|
|
||||||
const getThemeIcon = () => {
|
const getThemeIcon = () => {
|
||||||
|
@ -25,9 +25,9 @@ interface LazyPageImageProps {
|
|||||||
setPageRef: (index: number, ref: HTMLImageElement | null) => void;
|
setPageRef: (index: number, ref: HTMLImageElement | null) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LazyPageImage: React.FC<LazyPageImageProps> = ({
|
const LazyPageImage = ({
|
||||||
pageIndex, zoom, theme, isFirst, renderPage, pageImages, setPageRef
|
pageIndex, zoom, theme, isFirst, renderPage, pageImages, setPageRef
|
||||||
}) => {
|
}: LazyPageImageProps) => {
|
||||||
const [isVisible, setIsVisible] = useState(false);
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
const [imageUrl, setImageUrl] = useState<string | null>(pageImages[pageIndex]);
|
const [imageUrl, setImageUrl] = useState<string | null>(pageImages[pageIndex]);
|
||||||
const imgRef = useRef<HTMLImageElement>(null);
|
const imgRef = useRef<HTMLImageElement>(null);
|
||||||
@ -129,12 +129,12 @@ export interface ViewerProps {
|
|||||||
setSidebarsVisible: (v: boolean) => void;
|
setSidebarsVisible: (v: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Viewer: React.FC<ViewerProps> = ({
|
const Viewer = ({
|
||||||
pdfFile,
|
pdfFile,
|
||||||
setPdfFile,
|
setPdfFile,
|
||||||
sidebarsVisible,
|
sidebarsVisible,
|
||||||
setSidebarsVisible,
|
setSidebarsVisible,
|
||||||
}) => {
|
}: ViewerProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const theme = useMantineTheme();
|
const theme = useMantineTheme();
|
||||||
const [numPages, setNumPages] = useState<number>(0);
|
const [numPages, setNumPages] = useState<number>(0);
|
||||||
|
@ -15,6 +15,7 @@ import SplitPdfPanel from "../tools/Split";
|
|||||||
import CompressPdfPanel from "../tools/Compress";
|
import CompressPdfPanel from "../tools/Compress";
|
||||||
import MergePdfPanel from "../tools/Merge";
|
import MergePdfPanel from "../tools/Merge";
|
||||||
import PageEditor from "../components/PageEditor";
|
import PageEditor from "../components/PageEditor";
|
||||||
|
import PageEditorControls from "../components/PageEditorControls";
|
||||||
import Viewer from "../components/Viewer";
|
import Viewer from "../components/Viewer";
|
||||||
import TopControls from "../components/TopControls";
|
import TopControls from "../components/TopControls";
|
||||||
import ToolRenderer from "../components/ToolRenderer";
|
import ToolRenderer from "../components/ToolRenderer";
|
||||||
@ -56,6 +57,9 @@ export default function HomePage() {
|
|||||||
const [leftPanelView, setLeftPanelView] = useState<'toolPicker' | 'toolContent'>('toolPicker');
|
const [leftPanelView, setLeftPanelView] = useState<'toolPicker' | 'toolContent'>('toolPicker');
|
||||||
const [readerMode, setReaderMode] = useState(false);
|
const [readerMode, setReaderMode] = useState(false);
|
||||||
|
|
||||||
|
// Page editor functions
|
||||||
|
const [pageEditorFunctions, setPageEditorFunctions] = useState<any>(null);
|
||||||
|
|
||||||
// URL parameter management
|
// URL parameter management
|
||||||
const { toolParams, updateParams } = useToolParams(selectedToolKey, currentView);
|
const { toolParams, updateParams } = useToolParams(selectedToolKey, currentView);
|
||||||
|
|
||||||
@ -204,12 +208,32 @@ export default function HomePage() {
|
|||||||
setSidebarsVisible={setSidebarsVisible}
|
setSidebarsVisible={setSidebarsVisible}
|
||||||
/>
|
/>
|
||||||
) : currentView === "pageEditor" ? (
|
) : currentView === "pageEditor" ? (
|
||||||
|
<>
|
||||||
<PageEditor
|
<PageEditor
|
||||||
file={pdfFile}
|
file={pdfFile}
|
||||||
setFile={setPdfFile}
|
setFile={setPdfFile}
|
||||||
downloadUrl={downloadUrl}
|
downloadUrl={downloadUrl}
|
||||||
setDownloadUrl={setDownloadUrl}
|
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
|
<FileManager
|
||||||
files={files}
|
files={files}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user