Gallery View
The Gallery View is a core feature of our Tauri application, providing users with an intuitive interface to browse, sort, and interact with their media items (images and videos).
Components Overview
- MediaGallery
- MediaGrid
- MediaCard
- MediaView
- SortingControls
- PaginationControls
MediaGallery
The main container component for the gallery view.
Key Features
- Manages sorting and pagination state
- Renders the grid of media items
- Handles opening and closing of full-screen media view
Usage
import MediaGallery from './MediaGallery';
<MediaGallery
mediaItems={items}
title="My Gallery"
type="image"
/>
Implementation Details
export default function MediaGallery({
mediaItems,
title,
type,
}: MediaGalleryProps) {
const [sortBy, setSortBy] = useState<string>("date");
const [currentPage, setCurrentPage] = useState<number>(1);
const [showMediaViewer, setShowMediaViewer] = useState<boolean>(false);
const [selectedMediaIndex, setSelectedMediaIndex] = useState<number>(0);
// Memoized sorted and paginated media items
const sortedMedia = useMemo(() => sortMedia(mediaItems, sortBy), [mediaItems, sortBy]);
const currentItems = useMemo(() => {
const indexOfLastItem = currentPage * itemsPerPage;
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
return sortedMedia.slice(indexOfFirstItem, indexOfLastItem);
}, [sortedMedia, currentPage]);
// ... other memoized values and callback functions
return (
<div className="container">
<div className="dark:bg-background dark:text-foreground max-w-6xl mx-auto px-4 md:px-6 py-8">
{/* Title and SortingControls */}
<MediaGrid
mediaItems={currentItems}
itemsPerRow={itemsPerRow}
openMediaViewer={openMediaViewer}
type={type}
/>
<PaginationControls
currentPage={currentPage}
totalPages={totalPages}
onPageChange={setCurrentPage}
/>
{showMediaViewer && (
<MediaView
initialIndex={selectedMediaIndex}
onClose={closeMediaViewer}
allMedia={sortedMedia.map((item) => item.src)}
currentPage={currentPage}
itemsPerPage={itemsPerPage}
type={type}
/>
)}
</div>
</div>
);
}
MediaGrid
Renders a grid of MediaCard components.
Usage
<MediaGrid
mediaItems={currentItems}
itemsPerRow={3}
openMediaViewer={openMediaViewer}
type="image"
/>
Implementation
export default function MediaGrid({
mediaItems,
itemsPerRow,
openMediaViewer,
type,
}: MediaGridProps) {
if (mediaItems.length === 0) {
return <div className="flex justify-center items-center h-96">
<h1 className="text-2xl font-bold">No media found</h1>
</div>;
}
return (
<div className={`grid gap-4 md:gap-6 ${/* grid classes based on itemsPerRow */}`}>
{mediaItems.map((item, index) => (
<div key={index} onClick={() => openMediaViewer(index)} className="cursor-pointer">
<MediaCard item={item} type={type} />
</div>
))}
</div>
);
}
MediaCard
Represents an individual media item in the grid.
Usage
Implementation
export default function MediaCard({ item, type }: MediaCardProps) {
return (
<div className="relative overflow-hidden rounded-lg shadow-lg group hover:shadow-xl hover:-translate-y-2 transition-transform duration-300 ease-in-out dark:bg-card dark:text-card-foreground">
<a href="#" className="absolute inset-0 z-10">
<span className="sr-only">View</span>
</a>
{type === "image" ? (
<img
src={item.src}
alt={item.title}
className="object-cover w-full h-64 transition-opacity duration-300"
/>
) : (
<video
controls
src={item.src}
className="object-cover w-full h-64 transition-opacity duration-300"
/>
)}
</div>
);
}
MediaView
Provides a full-screen view of media items with navigation.
Usage
<MediaView
initialIndex={selectedMediaIndex}
onClose={closeMediaViewer}
allMedia={sortedMedia.map((item) => item.src)}
currentPage={currentPage}
itemsPerPage={itemsPerPage}
type="image"
/>
Implementation
const MediaView: React.FC<MediaViewProps> = ({
initialIndex,
onClose,
allMedia,
currentPage,
itemsPerPage,
type,
}) => {
const [globalIndex, setGlobalIndex] = useState<number>(
(currentPage - 1) * itemsPerPage + initialIndex
);
// ... navigation handlers
return (
<div className="fixed top-0 left-0 w-full h-full flex justify-center items-center bg-black bg-opacity-90 z-50">
<button onClick={onClose} className="absolute z-0 top-4 left-4 px-4 py-2 rounded-md border border-black bg-white text-black text-sm hover:shadow-[4px_4px_0px_0px_rgba(0,0,0)] transition duration-200">
Back
</button>
{type === "image" ? (
<img
src={allMedia[globalIndex]}
alt={`image-${globalIndex}`}
className="max-h-full"
/>
) : (
<video
src={allMedia[globalIndex]}
className="max-h-full"
controls
autoPlay
/>
)}
{/* Navigation buttons */}
</div>
);
};
SortingControls
Provides sorting options for media items.
Usage
Implementation
const SortingControls: React.FC<SortingControlsProps> = ({
sortBy,
setSortBy,
mediaItems,
}) => {
// ... year options generation logic
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="flex items-center gap-2">
<ListOrderedIcon className="w-4 h-4" />
Sort by {sortBy}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-[200px] bg-white dark:text-foreground"
align="end"
>
<DropdownMenuRadioGroup value={sortBy} onValueChange={setSortBy}>
<DropdownMenuRadioItem value="date">Date</DropdownMenuRadioItem>
{yearOptions.map((option) => (
<DropdownMenuRadioItem key={option.value} value={option.value}>
{option.label}
</DropdownMenuRadioItem>
))}
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
);
};
Best Practices
- Use TypeScript for type safety and better developer experience.
- Implement proper error handling and loading states.
- Optimize performance using React hooks like
useMemo
anduseCallback
. - Ensure accessibility in all components, especially in the MediaView for keyboard navigation.
- Use Tailwind CSS for consistent and responsive styling.
This documentation provides an overview of the Gallery View components in your Tauri application. For more detailed information on specific components or functionalities, refer to the individual component files