diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..9cca0c1 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: npm run build && npm preview \ No newline at end of file diff --git a/public/themes-manifest.json b/public/themes-manifest.json index 1a6fd53..1a35452 100644 --- a/public/themes-manifest.json +++ b/public/themes-manifest.json @@ -15,5 +15,5 @@ "hasMasterSlide": true } }, - "generated": "2025-09-12T14:15:01.593Z" + "generated": "2025-09-24T21:37:00.344Z" } \ No newline at end of file diff --git a/src/components/presentations/PresentationEditor.tsx b/src/components/presentations/PresentationEditor.tsx index b1b8788..5fc75b4 100644 --- a/src/components/presentations/PresentationEditor.tsx +++ b/src/components/presentations/PresentationEditor.tsx @@ -40,7 +40,7 @@ export const PresentationEditor: React.FC = () => { confirmDelete } = useDialog(); - const { duplicateSlide, deleteSlide, saving } = useSlideOperations({ + const { duplicateSlide, deleteSlide, reorderSlides, saving } = useSlideOperations({ presentation, presentationId: presentationId || '', onPresentationUpdate: setPresentation, @@ -176,6 +176,7 @@ export const PresentationEditor: React.FC = () => { onEditSlide={(slideId) => navigate(`/presentations/${presentationId}/slide/${slideId}/edit`)} onDuplicateSlide={duplicateSlide} onDeleteSlide={deleteSlide} + onReorderSlides={reorderSlides} />
diff --git a/src/components/presentations/SlidesSidebar.tsx b/src/components/presentations/SlidesSidebar.tsx index 0395033..8e56a54 100644 --- a/src/components/presentations/SlidesSidebar.tsx +++ b/src/components/presentations/SlidesSidebar.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import type { Slide } from '../../types/presentation.ts'; import { SlideThumbnail } from './shared/SlideThumbnail.tsx'; import './SlidesSidebar.css'; @@ -13,6 +13,7 @@ interface SlidesSidebarProps { onEditSlide: (slideId: string) => void; onDuplicateSlide: (index: number) => void; onDeleteSlide: (index: number) => void; + onReorderSlides: (fromIndex: number, toIndex: number) => void; } export const SlidesSidebar: React.FC = ({ @@ -24,8 +25,58 @@ export const SlidesSidebar: React.FC = ({ onAddSlide, onEditSlide, onDuplicateSlide, - onDeleteSlide + onDeleteSlide, + onReorderSlides }) => { + const [draggedIndex, setDraggedIndex] = useState(null); + const [dragOverIndex, setDragOverIndex] = useState(null); + + const handleDragStart = (e: React.DragEvent, index: number) => { + if (saving) return; + + setDraggedIndex(index); + e.dataTransfer.effectAllowed = 'move'; + e.dataTransfer.setData('text/plain', index.toString()); + + // Add drag image + if (e.currentTarget instanceof HTMLElement) { + e.currentTarget.style.opacity = '0.5'; + } + }; + + const handleDragEnd = (e: React.DragEvent) => { + if (e.currentTarget instanceof HTMLElement) { + e.currentTarget.style.opacity = ''; + } + setDraggedIndex(null); + setDragOverIndex(null); + }; + + const handleDragOver = (e: React.DragEvent, index: number) => { + e.preventDefault(); + e.dataTransfer.dropEffect = 'move'; + + if (draggedIndex !== null && index !== draggedIndex) { + setDragOverIndex(index); + } + }; + + const handleDragLeave = () => { + setDragOverIndex(null); + }; + + const handleDrop = (e: React.DragEvent, toIndex: number) => { + e.preventDefault(); + + const fromIndex = draggedIndex; + if (fromIndex !== null && fromIndex !== toIndex && !saving) { + onReorderSlides(fromIndex, toIndex); + } + + setDraggedIndex(null); + setDragOverIndex(null); + }; + return (