- Change from hash fragments (#file=2) to URL parameters (?idx=2) for server-side prerendering support - Simplify React component by removing complex client-side hydration logic - Remove unnecessary props: totalFiles, hasMultipleFiles (derive from files.length) - Remove redundant useCallback and popstate handlers - Update Rails helper to read URL parameter and pass correct initialSelectedIndex - Maintain all functionality: carousel, keyboard navigation, URL state management
100 lines
2.6 KiB
TypeScript
100 lines
2.6 KiB
TypeScript
import * as React from 'react';
|
|
import { useState, useEffect, useCallback } from 'react';
|
|
import { FileCarousel } from './FileCarousel';
|
|
import { DisplayedFile } from './DisplayedFile';
|
|
|
|
export interface FileData {
|
|
id: number;
|
|
thumbnailPath?: string;
|
|
hasContent: boolean;
|
|
index: number;
|
|
contentHtml?: string;
|
|
fileDetailsHtml?: string;
|
|
}
|
|
|
|
interface PostFilesProps {
|
|
files: FileData[];
|
|
initialSelectedIndex?: number;
|
|
}
|
|
|
|
export const PostFiles: React.FC<PostFilesProps> = ({
|
|
files,
|
|
initialSelectedIndex = 0,
|
|
}) => {
|
|
const [selectedIndex, setSelectedIndex] = useState(initialSelectedIndex);
|
|
|
|
// Update URL parameter when selected file changes
|
|
const updateUrlWithFileIndex = (index: number) => {
|
|
if (typeof window === 'undefined' || files.length <= 1) return;
|
|
|
|
const url = new URL(window.location.href);
|
|
url.searchParams.set('idx', index.toString());
|
|
window.history.replaceState({}, '', url.toString());
|
|
};
|
|
|
|
const handleFileSelect = (fileId: number, index: number) => {
|
|
setSelectedIndex(index);
|
|
updateUrlWithFileIndex(index);
|
|
};
|
|
|
|
const navigateToNextFile = () => {
|
|
if (files.length > 1) {
|
|
const nextIndex = (selectedIndex + 1) % files.length;
|
|
handleFileSelect(files[nextIndex].id, nextIndex);
|
|
}
|
|
};
|
|
|
|
const navigateToPreviousFile = () => {
|
|
if (files.length > 1) {
|
|
const prevIndex = (selectedIndex - 1 + files.length) % files.length;
|
|
handleFileSelect(files[prevIndex].id, prevIndex);
|
|
}
|
|
};
|
|
|
|
// Add keyboard navigation
|
|
useEffect(() => {
|
|
const handleKeyDown = (event: KeyboardEvent) => {
|
|
// Only handle arrow keys if we have multiple files
|
|
if (files.length <= 1) return;
|
|
|
|
switch (event.key) {
|
|
case 'ArrowLeft':
|
|
event.preventDefault();
|
|
navigateToPreviousFile();
|
|
break;
|
|
case 'ArrowRight':
|
|
event.preventDefault();
|
|
navigateToNextFile();
|
|
break;
|
|
}
|
|
};
|
|
|
|
// Add event listener to document
|
|
document.addEventListener('keydown', handleKeyDown);
|
|
|
|
// Cleanup event listener on unmount
|
|
return () => {
|
|
document.removeEventListener('keydown', handleKeyDown);
|
|
};
|
|
}, [selectedIndex, files.length]);
|
|
|
|
const selectedFile = files[selectedIndex];
|
|
|
|
return (
|
|
<section id="file-display-section">
|
|
{files.length > 1 && (
|
|
<FileCarousel
|
|
files={files}
|
|
totalFiles={files.length}
|
|
selectedIndex={selectedIndex}
|
|
onFileSelect={handleFileSelect}
|
|
/>
|
|
)}
|
|
|
|
{selectedFile && <DisplayedFile file={selectedFile} />}
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export default PostFiles;
|