mirror of
https://github.com/hoornet/vega.git
synced 2026-05-07 04:39:12 -07:00
Bump to v0.4.0 — Phase 3: image lightbox, bookmarks, discover, language filter, UI polish
This commit is contained in:
75
src/components/shared/ImageLightbox.tsx
Normal file
75
src/components/shared/ImageLightbox.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import { useEffect, useCallback } from "react";
|
||||
|
||||
interface ImageLightboxProps {
|
||||
images: string[];
|
||||
index: number;
|
||||
onClose: () => void;
|
||||
onNavigate: (index: number) => void;
|
||||
}
|
||||
|
||||
export function ImageLightbox({ images, index, onClose, onNavigate }: ImageLightboxProps) {
|
||||
const hasPrev = index > 0;
|
||||
const hasNext = index < images.length - 1;
|
||||
|
||||
const handleKeyDown = useCallback((e: KeyboardEvent) => {
|
||||
if (e.key === "Escape") onClose();
|
||||
if (e.key === "ArrowLeft" && hasPrev) onNavigate(index - 1);
|
||||
if (e.key === "ArrowRight" && hasNext) onNavigate(index + 1);
|
||||
}, [onClose, onNavigate, index, hasPrev, hasNext]);
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
return () => document.removeEventListener("keydown", handleKeyDown);
|
||||
}, [handleKeyDown]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm"
|
||||
onClick={onClose}
|
||||
>
|
||||
{/* Close button */}
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="absolute top-4 right-4 text-white/70 hover:text-white text-[20px] w-8 h-8 flex items-center justify-center transition-colors z-10"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
|
||||
{/* Counter */}
|
||||
{images.length > 1 && (
|
||||
<div className="absolute top-4 left-4 text-white/50 text-[12px] z-10">
|
||||
{index + 1} / {images.length}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Prev arrow */}
|
||||
{hasPrev && (
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); onNavigate(index - 1); }}
|
||||
className="absolute left-4 top-1/2 -translate-y-1/2 text-white/50 hover:text-white text-[28px] w-10 h-10 flex items-center justify-center transition-colors z-10"
|
||||
>
|
||||
‹
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* Image */}
|
||||
<img
|
||||
src={images[index]}
|
||||
alt=""
|
||||
className="max-w-[90vw] max-h-[90vh] object-contain select-none"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
draggable={false}
|
||||
/>
|
||||
|
||||
{/* Next arrow */}
|
||||
{hasNext && (
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); onNavigate(index + 1); }}
|
||||
className="absolute right-4 top-1/2 -translate-y-1/2 text-white/50 hover:text-white text-[28px] w-10 h-10 flex items-center justify-center transition-colors z-10"
|
||||
>
|
||||
›
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
46
src/components/shared/Skeleton.tsx
Normal file
46
src/components/shared/Skeleton.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
export function SkeletonNote() {
|
||||
return (
|
||||
<div className="px-4 py-3 border-b border-border animate-pulse">
|
||||
<div className="flex gap-3">
|
||||
<div className="w-9 h-9 rounded-sm bg-bg-raised shrink-0" />
|
||||
<div className="flex-1 space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="h-3 w-24 bg-bg-raised rounded-sm" />
|
||||
<div className="h-3 w-12 bg-bg-raised rounded-sm" />
|
||||
</div>
|
||||
<div className="h-3 w-full bg-bg-raised rounded-sm" />
|
||||
<div className="h-3 w-3/4 bg-bg-raised rounded-sm" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function SkeletonNoteList({ count = 5 }: { count?: number }) {
|
||||
return (
|
||||
<>
|
||||
{Array.from({ length: count }, (_, i) => (
|
||||
<SkeletonNote key={i} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function SkeletonProfile() {
|
||||
return (
|
||||
<div className="animate-pulse">
|
||||
<div className="h-32 bg-bg-raised" />
|
||||
<div className="px-4 py-3 space-y-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-16 h-16 rounded-sm bg-bg-raised border-2 border-bg -mt-10" />
|
||||
<div className="space-y-2 flex-1">
|
||||
<div className="h-4 w-32 bg-bg-raised rounded-sm" />
|
||||
<div className="h-3 w-20 bg-bg-raised rounded-sm" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-3 w-full bg-bg-raised rounded-sm" />
|
||||
<div className="h-3 w-2/3 bg-bg-raised rounded-sm" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user