slideshare/src/components/slide-editor/SlideEditor.tsx
Michael Mainguy 127b0fe96a Refactor SlideEditor component and consolidate CSS
- Move SlideEditor to dedicated slide-editor directory structure
- Break down monolithic 471-line component into smaller, focused modules:
  - SlideEditor.tsx (127 lines) - main component using composition
  - useSlideEditor.ts (235 lines) - custom hook for state management
  - ContentEditor.tsx - focused content editing component
  - SlidePreviewModal.tsx - modal for fullscreen preview
- Consolidate CSS from 838+132 lines to 731 lines with:
  - Comprehensive CSS variables system for consistent theming
  - Remove duplicate .slide-preview-wrapper rules and conflicts
  - Clean aspect ratio handling with clear separation of contexts
- Follow project standards: direct imports, error boundaries, under 200 lines per component
- Maintain all existing functionality while improving code organization

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-21 11:25:59 -05:00

128 lines
3.7 KiB
TypeScript

import React from 'react';
import { useParams, Link } from 'react-router-dom';
import { SlideEditorErrorBoundary } from './SlideEditorErrorBoundary.tsx';
import { LoadingState } from './LoadingState.tsx';
import { ErrorState } from './ErrorState.tsx';
import { LayoutSelection } from './LayoutSelection.tsx';
import { ContentEditor } from './ContentEditor.tsx';
import { SlidePreviewModal } from './SlidePreviewModal.tsx';
import { useSlideEditor } from './useSlideEditor.ts';
import './SlideEditor.css';
export const SlideEditor: React.FC = () => {
const { presentationId, slideId } = useParams<{
presentationId: string;
slideId: string;
}>();
const {
// Data
presentation,
theme,
selectedLayout,
slideContent,
slideNotes,
currentStep,
// States
loading,
error,
saving,
showPreview,
// Computed
isEditingExisting,
// Actions
updateSlotContent,
setSlideNotes,
setShowPreview,
selectLayout,
saveSlide,
cancelEditing,
} = useSlideEditor({ presentationId, slideId });
if (loading) {
return <LoadingState presentationId={presentationId} />;
}
if (error) {
return <ErrorState error={error} presentationId={presentationId} />;
}
if (!presentation || !theme) {
return <ErrorState error="Presentation or theme not found" presentationId={presentationId} />;
}
return (
<SlideEditorErrorBoundary presentationId={presentationId}>
<div className="slide-editor">
<header className="slide-editor-header">
<div className="editor-info">
<Link
to={`/presentations/${presentationId}/edit/slides/1`}
className="back-button"
>
Back to Presentation
</Link>
<div className="editor-title">
<h1>{isEditingExisting ? 'Edit Slide' : 'New Slide'}</h1>
<span className="presentation-context">
in "{presentation.metadata.name}"
</span>
</div>
</div>
<div className="editor-actions">
{currentStep === 'content' && selectedLayout && (
<button
type="button"
className="action-button secondary"
onClick={() => setShowPreview(true)}
>
Full Preview
</button>
)}
</div>
</header>
<main className="slide-editor-content">
{currentStep === 'layout' && (
<LayoutSelection
theme={theme}
selectedLayout={selectedLayout}
onLayoutSelect={selectLayout}
/>
)}
{currentStep === 'content' && selectedLayout && (
<ContentEditor
presentation={presentation}
selectedLayout={selectedLayout}
slideContent={slideContent}
slideNotes={slideNotes}
isEditingExisting={isEditingExisting}
saving={saving}
onSlotContentChange={updateSlotContent}
onNotesChange={setSlideNotes}
onSave={saveSlide}
onCancel={cancelEditing}
/>
)}
</main>
{/* Preview Modal */}
{selectedLayout && presentation && (
<SlidePreviewModal
isOpen={showPreview}
onClose={() => setShowPreview(false)}
layout={selectedLayout}
content={slideContent}
aspectRatio={presentation.metadata.aspectRatio || '16:9'}
themeName={theme?.name || 'Unknown Theme'}
/>
)}
</div>
</SlideEditorErrorBoundary>
);
};