added zoom function, fixed cart quantitu bug
This commit is contained in:
@@ -48,6 +48,7 @@ export default function CartItemCard({ item, onUpdate }: CartItemCardProps) {
|
||||
const pendingQuantityRef = useRef<number | null>(null);
|
||||
const retryCountRef = useRef(0);
|
||||
const retryTimerRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
||||
const isInitializedRef = useRef(false); // Track if component has been initialized
|
||||
|
||||
// Function refs to solve circular dependency
|
||||
const syncToServerRef = useRef<((quantity: number) => void) | null>(null);
|
||||
@@ -62,6 +63,10 @@ export default function CartItemCard({ item, onUpdate }: CartItemCardProps) {
|
||||
// Initialize from server state
|
||||
useEffect(() => {
|
||||
setLocalQuantity(item.quantity);
|
||||
// Mark as initialized after first render
|
||||
if (!isInitializedRef.current) {
|
||||
isInitializedRef.current = true;
|
||||
}
|
||||
}, [item.quantity]);
|
||||
|
||||
// Save to sessionStorage
|
||||
@@ -81,13 +86,13 @@ export default function CartItemCard({ item, onUpdate }: CartItemCardProps) {
|
||||
|
||||
sessionStorage.setItem(
|
||||
PENDING_CART_UPDATES_KEY,
|
||||
JSON.stringify(pending)
|
||||
JSON.stringify(pending),
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Failed to save pending update:", error);
|
||||
}
|
||||
},
|
||||
[item.product_id]
|
||||
[item.product_id],
|
||||
);
|
||||
|
||||
// Remove from sessionStorage
|
||||
@@ -103,7 +108,7 @@ export default function CartItemCard({ item, onUpdate }: CartItemCardProps) {
|
||||
} else {
|
||||
sessionStorage.setItem(
|
||||
PENDING_CART_UPDATES_KEY,
|
||||
JSON.stringify(pending)
|
||||
JSON.stringify(pending),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -200,7 +205,7 @@ export default function CartItemCard({ item, onUpdate }: CartItemCardProps) {
|
||||
|
||||
retrySyncRef.current?.(quantity);
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -211,13 +216,16 @@ export default function CartItemCard({ item, onUpdate }: CartItemCardProps) {
|
||||
removeItem,
|
||||
onUpdate,
|
||||
clearPendingUpdate,
|
||||
]
|
||||
],
|
||||
);
|
||||
|
||||
// Update ref
|
||||
syncToServerRef.current = syncToServer;
|
||||
|
||||
// Load pending updates from sessionStorage on mount
|
||||
// DISABLED: This was causing automatic sync on mount, sending 0 quantity to server
|
||||
// Users should manually refresh or re-add items if there were pending updates
|
||||
/*
|
||||
useEffect(() => {
|
||||
const loadPendingUpdates = () => {
|
||||
try {
|
||||
@@ -246,9 +254,15 @@ export default function CartItemCard({ item, onUpdate }: CartItemCardProps) {
|
||||
|
||||
loadPendingUpdates();
|
||||
}, [item.product_id, item.quantity]);
|
||||
*/
|
||||
|
||||
// Debounced sync
|
||||
useEffect(() => {
|
||||
// Don't sync on initial mount - only sync after user interaction
|
||||
if (!isInitializedRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear existing timers
|
||||
if (debounceTimerRef.current) {
|
||||
clearTimeout(debounceTimerRef.current);
|
||||
@@ -259,6 +273,14 @@ export default function CartItemCard({ item, onUpdate }: CartItemCardProps) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't sync if quantity is 0 or invalid (unless it's a delete operation)
|
||||
if (localQuantity <= 0 && item.quantity > 0) {
|
||||
// This is a delete operation, allow it
|
||||
} else if (localQuantity <= 0) {
|
||||
// Invalid state, don't sync
|
||||
return;
|
||||
}
|
||||
|
||||
// Save to sessionStorage immediately
|
||||
savePendingUpdate(localQuantity);
|
||||
|
||||
@@ -336,14 +358,14 @@ export default function CartItemCard({ item, onUpdate }: CartItemCardProps) {
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<h3 className="font-semibold text-base">{item.product.name}</h3>
|
||||
<p className="text-sm text-gray-600">
|
||||
{/* <p className="text-sm text-gray-600">
|
||||
{item.seller?.name || "Store"}
|
||||
</p>
|
||||
{availableStock <= 5 && (
|
||||
</p> */}
|
||||
{/* {availableStock <= 5 && (
|
||||
<p className="text-xs text-orange-600 font-medium">
|
||||
{t("only_left", { count: availableStock })}
|
||||
</p>
|
||||
)}
|
||||
)} */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect, useRef, useCallback } from "react";
|
||||
import Image from "next/image";
|
||||
|
||||
import { X, ZoomIn, ZoomOut, RotateCw, RotateCcw, Maximize2, ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
interface ProductImageGalleryProps {
|
||||
images: string[];
|
||||
productName: string;
|
||||
@@ -13,10 +14,18 @@ export function ProductImageGallery({
|
||||
noImageText,
|
||||
}: ProductImageGalleryProps) {
|
||||
const [selectedImage, setSelectedImage] = useState(0);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [zoom, setZoom] = useState(1);
|
||||
const [rotation, setRotation] = useState(0);
|
||||
const [position, setPosition] = useState({ x: 0, y: 0 });
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [dragStart, setDragStart] = useState({ x: 0, y: 0 });
|
||||
const t = useTranslations();
|
||||
const autoplayTimerRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
||||
const modalImageRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (images.length <= 1) return;
|
||||
if (images.length <= 1 || isModalOpen) return;
|
||||
|
||||
const startAutoplay = () => {
|
||||
autoplayTimerRef.current = setInterval(() => {
|
||||
@@ -28,42 +37,142 @@ export function ProductImageGallery({
|
||||
return () => {
|
||||
if (autoplayTimerRef.current) clearInterval(autoplayTimerRef.current);
|
||||
};
|
||||
}, [images.length]);
|
||||
}, [images.length, isModalOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isModalOpen) {
|
||||
document.body.style.overflow = "hidden";
|
||||
if (autoplayTimerRef.current) clearInterval(autoplayTimerRef.current);
|
||||
} else {
|
||||
document.body.style.overflow = "unset";
|
||||
}
|
||||
return () => {
|
||||
document.body.style.overflow = "unset";
|
||||
};
|
||||
}, [isModalOpen]);
|
||||
|
||||
const handleImageSelect = useCallback(
|
||||
(index: number) => {
|
||||
setSelectedImage(index);
|
||||
if (autoplayTimerRef.current) clearInterval(autoplayTimerRef.current);
|
||||
if (images.length > 1) {
|
||||
if (images.length > 1 && !isModalOpen) {
|
||||
autoplayTimerRef.current = setInterval(() => {
|
||||
setSelectedImage((prev) => (prev + 1) % images.length);
|
||||
}, 3000);
|
||||
}
|
||||
},
|
||||
[images.length]
|
||||
[images.length, isModalOpen]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="contents max-w-2xl">
|
||||
<div className="relative">
|
||||
<div className="relative aspect-square w-full rounded-2xl overflow-hidden bg-white">
|
||||
{images.length > 0 ? (
|
||||
<Image
|
||||
src={images[selectedImage]}
|
||||
alt={productName}
|
||||
fill
|
||||
className="object-contain"
|
||||
priority
|
||||
/>
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-full text-gray-400">
|
||||
{noImageText}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
const openModal = () => {
|
||||
setIsModalOpen(true);
|
||||
resetTransform();
|
||||
};
|
||||
|
||||
{images.length > 1 && (
|
||||
<div className="mt-4 flex gap-2 overflow-x-auto pb-2">
|
||||
const closeModal = () => {
|
||||
setIsModalOpen(false);
|
||||
resetTransform();
|
||||
};
|
||||
|
||||
const resetTransform = () => {
|
||||
setZoom(1);
|
||||
setRotation(0);
|
||||
setPosition({ x: 0, y: 0 });
|
||||
};
|
||||
|
||||
const handleZoomIn = () => {
|
||||
setZoom((prev) => Math.min(prev + 0.25, 5));
|
||||
};
|
||||
|
||||
const handleZoomOut = () => {
|
||||
setZoom((prev) => Math.max(prev - 0.25, 0.5));
|
||||
};
|
||||
|
||||
const handleRotateClockwise = () => {
|
||||
setRotation((prev) => (prev + 90) % 360);
|
||||
};
|
||||
|
||||
const handleRotateCounterClockwise = () => {
|
||||
setRotation((prev) => (prev - 90 + 360) % 360);
|
||||
};
|
||||
|
||||
const handleMouseDown = (e: React.MouseEvent | React.TouchEvent) => {
|
||||
if (zoom > 1) {
|
||||
setIsDragging(true);
|
||||
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
|
||||
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
|
||||
setDragStart({
|
||||
x: clientX - position.x,
|
||||
y: clientY - position.y,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseMove = (e: React.MouseEvent | React.TouchEvent) => {
|
||||
if (isDragging && zoom > 1) {
|
||||
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
|
||||
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
|
||||
setPosition({
|
||||
x: clientX - dragStart.x,
|
||||
y: clientY - dragStart.y,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseUp = () => {
|
||||
setIsDragging(false);
|
||||
};
|
||||
|
||||
const handleWheel = (e: React.WheelEvent) => {
|
||||
e.preventDefault();
|
||||
if (e.deltaY < 0) {
|
||||
handleZoomIn();
|
||||
} else {
|
||||
handleZoomOut();
|
||||
}
|
||||
};
|
||||
|
||||
const handleModalImageChange = (direction: "prev" | "next") => {
|
||||
if (direction === "next") {
|
||||
setSelectedImage((prev) => (prev + 1) % images.length);
|
||||
} else {
|
||||
setSelectedImage((prev) => (prev - 1 + images.length) % images.length);
|
||||
}
|
||||
resetTransform();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="contents max-w-2xl">
|
||||
<div className="relative">
|
||||
<div
|
||||
className="relative aspect-square w-full rounded-xl md:rounded-2xl overflow-hidden bg-gradient-to-br from-gray-50 to-gray-100 cursor-pointer group shadow-sm hover:shadow-md transition-all"
|
||||
onClick={openModal}
|
||||
>
|
||||
{images.length > 0 ? (
|
||||
<>
|
||||
<Image
|
||||
src={images[selectedImage]}
|
||||
alt={productName}
|
||||
fill
|
||||
className="object-contain transition-transform group-hover:scale-105"
|
||||
priority
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center">
|
||||
<div className="bg-white/90 backdrop-blur-sm rounded-full p-2 md:p-3 transform translate-y-4 group-hover:translate-y-0 transition-transform">
|
||||
<Maximize2 className="w-4 h-4 md:w-5 md:h-5 text-gray-800" />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-full text-gray-400 text-sm md:text-base">
|
||||
{noImageText}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{images.length > 1 && (
|
||||
<div className="mt-4 flex gap-2 overflow-x-auto pb-2">
|
||||
{images.map((image, index) => (
|
||||
<button
|
||||
key={index}
|
||||
@@ -83,8 +192,250 @@ export function ProductImageGallery({
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Modal */}
|
||||
{isModalOpen && (
|
||||
<div className="fixed inset-0 z-99 bg-gradient-to-br from-gray-900/95 via-gray-800/95 to-gray-900/95 backdrop-blur-xl flex flex-col">
|
||||
{/* Top Bar */}
|
||||
<div className="absolute top-0 left-0 right-0 p-3 md:p-4 z-20 bg-gradient-to-b from-black/20 to-transparent">
|
||||
<div className="max-w-7xl mx-auto flex items-center justify-between">
|
||||
<div className="flex items-center gap-2 md:gap-3 min-w-0 flex-1">
|
||||
<div className="w-1 h-4 md:h-6 bg-blue-500 rounded-full shrink-0" />
|
||||
<span className="text-white font-medium text-sm md:text-base truncate">{productName}</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={closeModal}
|
||||
className="p-2 md:p-2.5 bg-white/10 hover:bg-white/20 rounded-lg md:rounded-xl transition-all backdrop-blur-sm border border-white/10 shrink-0 ml-2"
|
||||
aria-label="Close"
|
||||
>
|
||||
<X className="w-4 h-4 md:w-5 md:h-5 text-white" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Image Area */}
|
||||
<div className="flex-1 flex items-center justify-center relative px-2 md:px-16 lg:px-20">
|
||||
{/* Left Arrow - Desktop */}
|
||||
{images.length > 1 && (
|
||||
<button
|
||||
onClick={() => handleModalImageChange("prev")}
|
||||
className="hidden md:flex absolute left-3 lg:left-6 p-2.5 md:p-3 bg-white/10 hover:bg-white/20 rounded-xl md:rounded-2xl transition-all backdrop-blur-md border border-white/10 hover:scale-110 z-10 group"
|
||||
aria-label="Previous image"
|
||||
>
|
||||
<ChevronLeft className="w-5 h-5 md:w-6 md:h-6 text-white" />
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* Image Container */}
|
||||
<div
|
||||
ref={modalImageRef}
|
||||
className="w-full h-full flex items-center justify-center overflow-hidden"
|
||||
onMouseDown={handleMouseDown}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseUp={handleMouseUp}
|
||||
onMouseLeave={handleMouseUp}
|
||||
onTouchStart={handleMouseDown}
|
||||
onTouchMove={handleMouseMove}
|
||||
onTouchEnd={handleMouseUp}
|
||||
onWheel={handleWheel}
|
||||
style={{ cursor: zoom > 1 ? (isDragging ? "grabbing" : "grab") : "default" }}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
transform: `translate(${position.x}px, ${position.y}px) scale(${zoom}) rotate(${rotation}deg)`,
|
||||
transition: isDragging ? "none" : "transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
|
||||
}}
|
||||
className="relative w-[90vw] h-[60vh] md:w-[75vw] md:h-[70vh]"
|
||||
>
|
||||
<Image
|
||||
src={images[selectedImage]}
|
||||
alt={productName}
|
||||
fill
|
||||
className="object-contain pointer-events-none select-none"
|
||||
priority
|
||||
draggable={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Arrow - Desktop */}
|
||||
{images.length > 1 && (
|
||||
<button
|
||||
onClick={() => handleModalImageChange("next")}
|
||||
className="hidden md:flex absolute right-3 lg:right-6 p-2.5 md:p-3 bg-white/10 hover:bg-white/20 rounded-xl md:rounded-2xl transition-all backdrop-blur-md border border-white/10 hover:scale-110 z-10 group"
|
||||
aria-label="Next image"
|
||||
>
|
||||
<ChevronRight className="w-5 h-5 md:w-6 md:h-6 text-white" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Bottom Control Bar */}
|
||||
<div className="bg-gradient-to-t from-black/40 via-black/20 to-transparent backdrop-blur-xl border-t border-white/10">
|
||||
<div className="max-w-7xl mx-auto px-3 md:px-6 py-3 md:py-4">
|
||||
{/* Mobile Layout */}
|
||||
<div className="md:hidden flex flex-col gap-2">
|
||||
{/* Row 1: Navigation */}
|
||||
{images.length > 1 && (
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<button
|
||||
onClick={() => handleModalImageChange("prev")}
|
||||
className="flex-1 p-2 bg-white/10 hover:bg-white/20 rounded-lg transition-all border border-white/10"
|
||||
aria-label="Previous"
|
||||
>
|
||||
<ChevronLeft className="w-5 h-5 text-white mx-auto" />
|
||||
</button>
|
||||
<div className="px-4 py-2 bg-white/10 backdrop-blur-md rounded-lg border border-white/10">
|
||||
<span className="text-white text-sm font-medium whitespace-nowrap">
|
||||
{selectedImage + 1} / {images.length}
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => handleModalImageChange("next")}
|
||||
className="flex-1 p-2 bg-white/10 hover:bg-white/20 rounded-lg transition-all border border-white/10"
|
||||
aria-label="Next"
|
||||
>
|
||||
<ChevronRight className="w-5 h-5 text-white mx-auto" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Row 2: Zoom & Rotate */}
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex items-center gap-1 bg-white/10 backdrop-blur-md rounded-lg p-1 border border-white/10 flex-1">
|
||||
<button
|
||||
onClick={handleZoomOut}
|
||||
className="p-2 hover:bg-white/20 rounded-md transition-all flex-1"
|
||||
aria-label="Zoom out"
|
||||
>
|
||||
<ZoomOut className="w-4 h-4 text-white mx-auto" />
|
||||
</button>
|
||||
<div className="px-2 py-1 bg-white/10 rounded text-center min-w-[50px]">
|
||||
<span className="text-white text-xs font-medium">{Math.round(zoom * 100)}%</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleZoomIn}
|
||||
className="p-2 hover:bg-white/20 rounded-md transition-all flex-1"
|
||||
aria-label="Zoom in"
|
||||
>
|
||||
<ZoomIn className="w-4 h-4 text-white mx-auto" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1 bg-white/10 backdrop-blur-md rounded-lg p-1 border border-white/10">
|
||||
<button
|
||||
onClick={handleRotateCounterClockwise}
|
||||
className="p-2 hover:bg-white/20 rounded-md transition-all"
|
||||
aria-label="Rotate counter-clockwise"
|
||||
>
|
||||
<RotateCcw className="w-4 h-4 text-white" />
|
||||
</button>
|
||||
<button
|
||||
onClick={handleRotateClockwise}
|
||||
className="p-2 hover:bg-white/20 rounded-md transition-all"
|
||||
aria-label="Rotate clockwise"
|
||||
>
|
||||
<RotateCw className="w-4 h-4 text-white" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={resetTransform}
|
||||
className="px-3 py-2 bg-white/10 hover:bg-white/20 rounded-lg transition-all text-white text-xs font-medium border border-white/10"
|
||||
aria-label="Reset view"
|
||||
>
|
||||
{t("reset")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Desktop Layout */}
|
||||
<div className="hidden md:flex items-center justify-center gap-2">
|
||||
<button
|
||||
onClick={() => handleModalImageChange("prev")}
|
||||
disabled={images.length <= 1}
|
||||
className="p-2 bg-white/10 hover:bg-white/20 rounded-lg transition-all disabled:opacity-30 disabled:cursor-not-allowed border border-white/10"
|
||||
aria-label="Previous"
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4 text-white" />
|
||||
</button>
|
||||
|
||||
<div className="flex items-center gap-1.5 bg-white/10 backdrop-blur-md rounded-lg p-1 border border-white/10">
|
||||
<button
|
||||
onClick={handleZoomOut}
|
||||
className="p-2 hover:bg-white/20 rounded-md transition-all"
|
||||
aria-label="Zoom out"
|
||||
>
|
||||
<ZoomOut className="w-4 h-4 text-white" />
|
||||
</button>
|
||||
<div className="px-3 py-1 bg-white/10 rounded min-w-[60px] text-center">
|
||||
<span className="text-white text-sm font-medium">{Math.round(zoom * 100)}%</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleZoomIn}
|
||||
className="p-2 hover:bg-white/20 rounded-md transition-all"
|
||||
aria-label="Zoom in"
|
||||
>
|
||||
<ZoomIn className="w-4 h-4 text-white" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="w-px h-8 bg-white/20" />
|
||||
|
||||
<div className="flex items-center gap-1.5 bg-white/10 backdrop-blur-md rounded-lg p-1 border border-white/10">
|
||||
<button
|
||||
onClick={handleRotateCounterClockwise}
|
||||
className="p-2 hover:bg-white/20 rounded-md transition-all"
|
||||
aria-label="Rotate counter-clockwise"
|
||||
>
|
||||
<RotateCcw className="w-4 h-4 text-white" />
|
||||
</button>
|
||||
<button
|
||||
onClick={handleRotateClockwise}
|
||||
className="p-2 hover:bg-white/20 rounded-md transition-all"
|
||||
aria-label="Rotate clockwise"
|
||||
>
|
||||
<RotateCw className="w-4 h-4 text-white" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={resetTransform}
|
||||
className="px-4 py-2 bg-white/10 hover:bg-white/20 rounded-lg transition-all text-white text-sm font-medium border border-white/10"
|
||||
aria-label="Reset view"
|
||||
>
|
||||
Reset
|
||||
</button>
|
||||
|
||||
<div className="w-px h-8 bg-white/20" />
|
||||
|
||||
<button
|
||||
onClick={() => handleModalImageChange("next")}
|
||||
disabled={images.length <= 1}
|
||||
className="p-2 bg-white/10 hover:bg-white/20 rounded-lg transition-all disabled:opacity-30 disabled:cursor-not-allowed border border-white/10"
|
||||
aria-label="Next"
|
||||
>
|
||||
<ChevronRight className="w-4 h-4 text-white" />
|
||||
</button>
|
||||
|
||||
{images.length > 1 && (
|
||||
<>
|
||||
<div className="w-px h-8 bg-white/20" />
|
||||
<div className="px-4 py-2 bg-white/10 backdrop-blur-md rounded-lg border border-white/10">
|
||||
<span className="text-white text-sm font-medium">
|
||||
{selectedImage + 1} / {images.length}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user