mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-07-27 15:45:21 +00:00
Page editor updates
This commit is contained in:
parent
7fc850b138
commit
e7995a0256
@ -20,6 +20,8 @@ import ConstructionIcon from "@mui/icons-material/Construction";
|
|||||||
import EventListIcon from "@mui/icons-material/EventList";
|
import EventListIcon from "@mui/icons-material/EventList";
|
||||||
import DeselectIcon from "@mui/icons-material/Deselect";
|
import DeselectIcon from "@mui/icons-material/Deselect";
|
||||||
import SelectAllIcon from "@mui/icons-material/SelectAll";
|
import SelectAllIcon from "@mui/icons-material/SelectAll";
|
||||||
|
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
||||||
|
import ArrowForwardIcon from "@mui/icons-material/ArrowForward";
|
||||||
import { usePDFProcessor } from "../hooks/usePDFProcessor";
|
import { usePDFProcessor } from "../hooks/usePDFProcessor";
|
||||||
import { PDFDocument, PDFPage } from "../types/pageEditor";
|
import { PDFDocument, PDFPage } from "../types/pageEditor";
|
||||||
import { fileStorage } from "../services/fileStorage";
|
import { fileStorage } from "../services/fileStorage";
|
||||||
@ -62,6 +64,7 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
|||||||
const [exportLoading, setExportLoading] = useState(false);
|
const [exportLoading, setExportLoading] = useState(false);
|
||||||
const [showExportModal, setShowExportModal] = useState(false);
|
const [showExportModal, setShowExportModal] = useState(false);
|
||||||
const [exportPreview, setExportPreview] = useState<{pageCount: number; splitCount: number; estimatedSize: string} | null>(null);
|
const [exportPreview, setExportPreview] = useState<{pageCount: number; splitCount: number; estimatedSize: string} | null>(null);
|
||||||
|
const [movingPage, setMovingPage] = useState<string | null>(null);
|
||||||
const fileInputRef = useRef<() => void>(null);
|
const fileInputRef = useRef<() => void>(null);
|
||||||
|
|
||||||
// Undo/Redo system
|
// Undo/Redo system
|
||||||
@ -345,12 +348,7 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
|||||||
<Box pos="relative" h="100vh" style={{ overflow: 'auto' }}>
|
<Box pos="relative" h="100vh" style={{ overflow: 'auto' }}>
|
||||||
<LoadingOverlay visible={loading || pdfLoading} />
|
<LoadingOverlay visible={loading || pdfLoading} />
|
||||||
|
|
||||||
<Box p="xl">
|
<Container size="lg" p="xl" h="100%" style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||||
{error && (
|
|
||||||
<Alert color="red" mb="md" onClose={() => setError(null)}>
|
|
||||||
{error}
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Dropzone
|
<Dropzone
|
||||||
onDrop={(files) => files[0] && handleFileUpload(files[0])}
|
onDrop={(files) => files[0] && handleFileUpload(files[0])}
|
||||||
@ -371,7 +369,7 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
|||||||
</Stack>
|
</Stack>
|
||||||
</Center>
|
</Center>
|
||||||
</Dropzone>
|
</Dropzone>
|
||||||
</Box>
|
</Container>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -392,6 +390,14 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
|||||||
.page-container:hover {
|
.page-container:hover {
|
||||||
transform: scale(1.02);
|
transform: scale(1.02);
|
||||||
}
|
}
|
||||||
|
.page-move-animation {
|
||||||
|
transition: all 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||||
|
}
|
||||||
|
.page-moving {
|
||||||
|
z-index: 10;
|
||||||
|
transform: scale(1.05);
|
||||||
|
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
@keyframes pulse {
|
@keyframes pulse {
|
||||||
0%, 100% {
|
0%, 100% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@ -489,30 +495,15 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
|||||||
{page.splitBefore && index > 0 && (
|
{page.splitBefore && index > 0 && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: '4px',
|
width: '2px',
|
||||||
height: '15rem',
|
height: '20rem',
|
||||||
border: '2px dashed #3b82f6',
|
borderLeft: '2px dashed #3b82f6',
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
borderRadius: '2px',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
marginLeft: '-0.75rem',
|
marginLeft: '-0.75rem',
|
||||||
marginRight: '-0.75rem',
|
marginRight: '-0.75rem',
|
||||||
position: 'relative',
|
|
||||||
flexShrink: 0
|
flexShrink: 0
|
||||||
}}
|
}}
|
||||||
>
|
|
||||||
<ContentCutIcon
|
|
||||||
style={{
|
|
||||||
fontSize: 18,
|
|
||||||
color: '#3b82f6',
|
|
||||||
backgroundColor: 'white',
|
|
||||||
borderRadius: '50%',
|
|
||||||
padding: '3px'
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
data-page-id={page.id}
|
data-page-id={page.id}
|
||||||
@ -520,18 +511,20 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
|||||||
!rounded-lg
|
!rounded-lg
|
||||||
cursor-grab
|
cursor-grab
|
||||||
select-none
|
select-none
|
||||||
w-[15rem]
|
w-[20rem]
|
||||||
h-[15rem]
|
h-[20rem]
|
||||||
flex items-center justify-center
|
flex items-center justify-center
|
||||||
flex-shrink-0
|
flex-shrink-0
|
||||||
shadow-sm
|
shadow-sm
|
||||||
hover:shadow-md
|
hover:shadow-md
|
||||||
transition-all
|
transition-all
|
||||||
relative
|
relative
|
||||||
|
page-move-animation
|
||||||
${selectedPages.includes(page.id)
|
${selectedPages.includes(page.id)
|
||||||
? 'ring-2 ring-blue-500 bg-blue-50'
|
? 'ring-2 ring-blue-500 bg-blue-50'
|
||||||
: 'bg-white hover:bg-gray-50'}
|
: 'bg-white hover:bg-gray-50'}
|
||||||
${draggedPage === page.id ? 'opacity-50 scale-95' : ''}
|
${draggedPage === page.id ? 'opacity-50 scale-95' : ''}
|
||||||
|
${movingPage === page.id ? 'page-moving' : ''}
|
||||||
`}
|
`}
|
||||||
style={{
|
style={{
|
||||||
transform: (() => {
|
transform: (() => {
|
||||||
@ -606,6 +599,62 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
|||||||
whiteSpace: 'nowrap'
|
whiteSpace: 'nowrap'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<Tooltip label="Move Left">
|
||||||
|
<ActionIcon
|
||||||
|
size="md"
|
||||||
|
variant="subtle"
|
||||||
|
c="white"
|
||||||
|
disabled={index === 0}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (index > 0 && !movingPage) {
|
||||||
|
setMovingPage(page.id);
|
||||||
|
setTimeout(() => {
|
||||||
|
const command = new ReorderPageCommand(
|
||||||
|
pdfDocument,
|
||||||
|
setPdfDocument,
|
||||||
|
page.id,
|
||||||
|
index - 1
|
||||||
|
);
|
||||||
|
executeCommand(command);
|
||||||
|
setTimeout(() => setMovingPage(null), 100);
|
||||||
|
setStatus(`Moved page ${page.pageNumber} left`);
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ArrowBackIcon style={{ fontSize: 20 }} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip label="Move Right">
|
||||||
|
<ActionIcon
|
||||||
|
size="md"
|
||||||
|
variant="subtle"
|
||||||
|
c="white"
|
||||||
|
disabled={index === pdfDocument.pages.length - 1}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (index < pdfDocument.pages.length - 1 && !movingPage) {
|
||||||
|
setMovingPage(page.id);
|
||||||
|
setTimeout(() => {
|
||||||
|
const command = new ReorderPageCommand(
|
||||||
|
pdfDocument,
|
||||||
|
setPdfDocument,
|
||||||
|
page.id,
|
||||||
|
index + 1
|
||||||
|
);
|
||||||
|
executeCommand(command);
|
||||||
|
setTimeout(() => setMovingPage(null), 100);
|
||||||
|
setStatus(`Moved page ${page.pageNumber} right`);
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ArrowForwardIcon style={{ fontSize: 20 }} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
<Tooltip label="Rotate Left">
|
<Tooltip label="Rotate Left">
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
size="md"
|
size="md"
|
||||||
@ -668,6 +717,7 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
|||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
|
{index > 0 && (
|
||||||
<Tooltip label="Split Here">
|
<Tooltip label="Split Here">
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
size="md"
|
size="md"
|
||||||
@ -687,6 +737,7 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
|||||||
<ContentCutIcon style={{ fontSize: 20 }} />
|
<ContentCutIcon style={{ fontSize: 20 }} />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
|
||||||
<Tooltip label="Select Page">
|
<Tooltip label="Select Page">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@ -716,31 +767,24 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
|||||||
))}
|
))}
|
||||||
|
|
||||||
{/* Landing zone at the end */}
|
{/* Landing zone at the end */}
|
||||||
|
<div className="w-[20rem] h-[20rem] flex items-center justify-center flex-shrink-0">
|
||||||
<div
|
<div
|
||||||
data-drop-zone="end"
|
data-drop-zone="end"
|
||||||
|
className={`cursor-pointer select-none w-[15rem] h-[15rem] flex items-center justify-center flex-shrink-0 shadow-sm hover:shadow-md transition-all relative ${dropTarget === 'end' ? 'ring-2 ring-green-500 bg-green-50' : 'bg-white hover:bg-blue-50 border-2 border-dashed border-gray-300 hover:border-blue-400'}`}
|
||||||
style={{
|
style={{
|
||||||
width: '15rem',
|
borderRadius: '12px'
|
||||||
height: '15rem',
|
|
||||||
border: '2px dashed #9ca3af',
|
|
||||||
borderRadius: '8px',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
flexShrink: 0,
|
|
||||||
backgroundColor: dropTarget === 'end' ? '#ecfdf5' : 'transparent',
|
|
||||||
borderColor: dropTarget === 'end' ? '#10b981' : '#9ca3af',
|
|
||||||
transition: 'all 0.2s ease-in-out'
|
|
||||||
}}
|
}}
|
||||||
onDragOver={handleDragOver}
|
onDragOver={handleDragOver}
|
||||||
onDragEnter={handleEndZoneDragEnter}
|
onDragEnter={handleEndZoneDragEnter}
|
||||||
onDragLeave={handleDragLeave}
|
onDragLeave={handleDragLeave}
|
||||||
onDrop={(e) => handleDrop(e, 'end')}
|
onDrop={(e) => handleDrop(e, 'end')}
|
||||||
>
|
>
|
||||||
<Text c="dimmed" size="sm" ta="center">
|
<Text c="dimmed" size="sm" ta="center" fw={500}>
|
||||||
Drop here to<br />move to end
|
Drop here to<br />move to end
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Group justify="space-between" mt="md">
|
<Group justify="space-between" mt="md">
|
||||||
<Button
|
<Button
|
||||||
|
Loading…
x
Reference in New Issue
Block a user