Added zoom function to product detail
This commit is contained in:
@@ -10,14 +10,13 @@
|
|||||||
width: 99%;
|
width: 99%;
|
||||||
height: auto;
|
height: auto;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
// transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fixed frame container to prevent layout shifts */
|
/* Fixed frame container to prevent layout shifts */
|
||||||
.fixedFrameContainer {
|
.fixedFrameContainer {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
aspect-ratio: 1; /* Square container */
|
aspect-ratio: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -28,7 +27,7 @@
|
|||||||
/* Style for product card (non-detail view) images */
|
/* Style for product card (non-detail view) images */
|
||||||
.cardImage {
|
.cardImage {
|
||||||
width: auto;
|
width: auto;
|
||||||
height: 300px; /* Increased height for card view */
|
height: 300px;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
@@ -64,11 +63,6 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
// .transitioning img {
|
|
||||||
// opacity: 0.8;
|
|
||||||
// transition: opacity 0.3s ease-in-out;
|
|
||||||
// }
|
|
||||||
|
|
||||||
.arrowButton {
|
.arrowButton {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
@@ -96,7 +90,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fixed positions for arrows - they won't move with image size changes */
|
|
||||||
.leftArrow {
|
.leftArrow {
|
||||||
left: 8px;
|
left: 8px;
|
||||||
}
|
}
|
||||||
@@ -105,7 +98,6 @@
|
|||||||
right: 8px;
|
right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure the carousel wrapper has a defined position */
|
|
||||||
.carouselWrapper {
|
.carouselWrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -134,6 +126,7 @@
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-color: rgba(200, 200, 200, 0.7);
|
background-color: rgba(200, 200, 200, 0.7);
|
||||||
transition: background-color 0.2s ease;
|
transition: background-color 0.2s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgba(255, 255, 255, 0.8);
|
background-color: rgba(255, 255, 255, 0.8);
|
||||||
@@ -180,6 +173,125 @@
|
|||||||
border: 2px solid #ff6b00;
|
border: 2px solid #ff6b00;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Modal Styles */
|
||||||
|
.modalOverlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.75);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 9999;
|
||||||
|
animation: fadeIn 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modalContent {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.closeButton {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-size: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 10001;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
color: #333;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: grey;
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modalImageContainer {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modalImage {
|
||||||
|
max-width: 90%;
|
||||||
|
max-height: 80vh;
|
||||||
|
object-fit: contain;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-drag: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modalControls {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 20px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 50px;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
z-index: 10001;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controlButton {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.15);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.25);
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Responsive styling */
|
/* Responsive styling */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.thumbnailContainer {
|
.thumbnailContainer {
|
||||||
@@ -202,6 +314,37 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.cardImage {
|
.cardImage {
|
||||||
height: 180px; /* Adjusted height for mobile view */
|
height: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.closeButton {
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modalControls {
|
||||||
|
bottom: 10px;
|
||||||
|
padding: 8px;
|
||||||
|
gap: 6px;
|
||||||
|
// flex-wrap: wrap;
|
||||||
|
max-width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controlButton {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modalImage {
|
||||||
|
max-width: 95%;
|
||||||
|
max-height: 70vh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,54 +5,65 @@ const ImageCarousel = ({
|
|||||||
images,
|
images,
|
||||||
altText,
|
altText,
|
||||||
showThumbnails = false,
|
showThumbnails = false,
|
||||||
isDetailView = false, // Prop to differentiate between card and detail view
|
isDetailView = false,
|
||||||
}) => {
|
}) => {
|
||||||
const [currentIndex, setCurrentIndex] = useState(0);
|
const [currentIndex, setCurrentIndex] = useState(0);
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
const [scale, setScale] = 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 touchStartX = useRef(0);
|
const touchStartX = useRef(0);
|
||||||
const touchEndX = useRef(0);
|
const touchEndX = useRef(0);
|
||||||
const carouselRef = useRef(null);
|
const carouselRef = useRef(null);
|
||||||
|
const modalImageRef = useRef(null);
|
||||||
|
|
||||||
// Check if there are multiple images
|
|
||||||
const hasMultipleImages = Array.isArray(images) && images.length > 1;
|
const hasMultipleImages = Array.isArray(images) && images.length > 1;
|
||||||
|
|
||||||
// Get current image URL
|
|
||||||
const currentImage =
|
const currentImage =
|
||||||
hasMultipleImages && images[currentIndex]
|
hasMultipleImages && images[currentIndex]
|
||||||
? images[currentIndex].images_400x400
|
? images[currentIndex].images_400x400
|
||||||
: images[0]?.images_400x400 || "";
|
: images[0]?.images_400x400 || "";
|
||||||
|
|
||||||
// Auto-slide functionality - every 9 seconds
|
// Auto-slide functionality
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!hasMultipleImages) return;
|
if (!hasMultipleImages || isModalOpen) return;
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
setCurrentIndex((prev) => (prev === images.length - 1 ? 0 : prev + 1));
|
setCurrentIndex((prev) => (prev === images.length - 1 ? 0 : prev + 1));
|
||||||
}, 9000);
|
}, 9000);
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [hasMultipleImages, images]);
|
}, [hasMultipleImages, images, isModalOpen]);
|
||||||
|
|
||||||
|
// Reset zoom/rotation when modal closes or image changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isModalOpen) {
|
||||||
|
setScale(1);
|
||||||
|
setRotation(0);
|
||||||
|
setPosition({ x: 0, y: 0 });
|
||||||
|
}
|
||||||
|
}, [isModalOpen, currentIndex]);
|
||||||
|
|
||||||
// Navigate to previous image
|
|
||||||
const handlePrev = (e) => {
|
const handlePrev = (e) => {
|
||||||
if (e) e.stopPropagation();
|
if (e) e.stopPropagation();
|
||||||
if (!hasMultipleImages) return;
|
if (!hasMultipleImages) return;
|
||||||
setCurrentIndex((prev) => (prev === 0 ? images.length - 1 : prev - 1));
|
setCurrentIndex((prev) => (prev === 0 ? images.length - 1 : prev - 1));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Navigate to next image
|
|
||||||
const handleNext = (e) => {
|
const handleNext = (e) => {
|
||||||
if (e) e.stopPropagation();
|
if (e) e.stopPropagation();
|
||||||
if (!hasMultipleImages) return;
|
if (!hasMultipleImages) return;
|
||||||
setCurrentIndex((prev) => (prev === images.length - 1 ? 0 : prev + 1));
|
setCurrentIndex((prev) => (prev === images.length - 1 ? 0 : prev + 1));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle thumbnail click
|
|
||||||
const handleThumbnailClick = (index, e) => {
|
const handleThumbnailClick = (index, e) => {
|
||||||
if (e) e.stopPropagation();
|
if (e) e.stopPropagation();
|
||||||
setCurrentIndex(index);
|
setCurrentIndex(index);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Touch event handlers
|
|
||||||
const handleTouchStart = (e) => {
|
const handleTouchStart = (e) => {
|
||||||
touchStartX.current = e.touches[0].clientX;
|
touchStartX.current = e.touches[0].clientX;
|
||||||
};
|
};
|
||||||
@@ -66,19 +77,15 @@ const ImageCarousel = ({
|
|||||||
|
|
||||||
const touchDiff = touchStartX.current - touchEndX.current;
|
const touchDiff = touchStartX.current - touchEndX.current;
|
||||||
|
|
||||||
// Swipe threshold - only respond to intentional swipes
|
|
||||||
if (Math.abs(touchDiff) > 50) {
|
if (Math.abs(touchDiff) > 50) {
|
||||||
if (touchDiff > 0) {
|
if (touchDiff > 0) {
|
||||||
// Swipe left -> Next image
|
|
||||||
handleNext();
|
handleNext();
|
||||||
} else {
|
} else {
|
||||||
// Swipe right -> Previous image
|
|
||||||
handlePrev();
|
handlePrev();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Apply transition effect using CSS
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (carouselRef.current) {
|
if (carouselRef.current) {
|
||||||
carouselRef.current.classList.add(styles.transitioning);
|
carouselRef.current.classList.add(styles.transitioning);
|
||||||
@@ -87,13 +94,122 @@ const ImageCarousel = ({
|
|||||||
if (carouselRef.current) {
|
if (carouselRef.current) {
|
||||||
carouselRef.current.classList.remove(styles.transitioning);
|
carouselRef.current.classList.remove(styles.transitioning);
|
||||||
}
|
}
|
||||||
}, 900); // Match this timing with CSS transition duration
|
}, 900);
|
||||||
|
|
||||||
return () => clearTimeout(timer);
|
return () => clearTimeout(timer);
|
||||||
}
|
}
|
||||||
}, [currentIndex]);
|
}, [currentIndex]);
|
||||||
|
|
||||||
// If there's only one image, just show it - applying different classes based on view
|
// Modal zoom functions
|
||||||
|
const handleZoomIn = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setScale((prev) => Math.min(prev + 0.5, 5));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleZoomOut = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setScale((prev) => Math.max(prev - 0.5, 0.5));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRotateLeft = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setRotation((prev) => prev - 90);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRotateRight = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setRotation((prev) => prev + 90);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReset = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setScale(1);
|
||||||
|
setRotation(0);
|
||||||
|
setPosition({ x: 0, y: 0 });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFullscreen = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (modalImageRef.current) {
|
||||||
|
if (document.fullscreenElement) {
|
||||||
|
document.exitFullscreen();
|
||||||
|
} else {
|
||||||
|
modalImageRef.current.requestFullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Dragging functions
|
||||||
|
const handleMouseDown = (e) => {
|
||||||
|
if (scale > 1) {
|
||||||
|
setIsDragging(true);
|
||||||
|
setDragStart({
|
||||||
|
x: e.clientX - position.x,
|
||||||
|
y: e.clientY - position.y,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseMove = (e) => {
|
||||||
|
if (isDragging && scale > 1) {
|
||||||
|
setPosition({
|
||||||
|
x: e.clientX - dragStart.x,
|
||||||
|
y: e.clientY - dragStart.y,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseUp = () => {
|
||||||
|
setIsDragging(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const openModal = (e) => {
|
||||||
|
if (e) e.stopPropagation();
|
||||||
|
if (isDetailView) {
|
||||||
|
setIsModalOpen(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeModal = (e) => {
|
||||||
|
if (e) e.stopPropagation();
|
||||||
|
setIsModalOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Keyboard controls
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isModalOpen) return;
|
||||||
|
|
||||||
|
const handleKeyDown = (e) => {
|
||||||
|
switch (e.key) {
|
||||||
|
case "Escape":
|
||||||
|
closeModal();
|
||||||
|
break;
|
||||||
|
case "ArrowLeft":
|
||||||
|
handlePrev();
|
||||||
|
break;
|
||||||
|
case "ArrowRight":
|
||||||
|
handleNext();
|
||||||
|
break;
|
||||||
|
case "+":
|
||||||
|
case "=":
|
||||||
|
handleZoomIn(e);
|
||||||
|
break;
|
||||||
|
case "-":
|
||||||
|
handleZoomOut(e);
|
||||||
|
break;
|
||||||
|
case "r":
|
||||||
|
case "R":
|
||||||
|
handleRotateRight(e);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("keydown", handleKeyDown);
|
||||||
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||||
|
}, [isModalOpen]);
|
||||||
|
|
||||||
if (!hasMultipleImages) {
|
if (!hasMultipleImages) {
|
||||||
return (
|
return (
|
||||||
<div className={isDetailView ? styles.fixedFrameContainer : undefined}>
|
<div className={isDetailView ? styles.fixedFrameContainer : undefined}>
|
||||||
@@ -103,7 +219,208 @@ const ImageCarousel = ({
|
|||||||
className={`${styles.productImage} ${
|
className={`${styles.productImage} ${
|
||||||
isDetailView ? styles.detailImage : styles.cardImage
|
isDetailView ? styles.detailImage : styles.cardImage
|
||||||
}`}
|
}`}
|
||||||
|
onClick={isDetailView ? openModal : undefined}
|
||||||
|
style={{ cursor: isDetailView ? "pointer" : "default" }}
|
||||||
/>
|
/>
|
||||||
|
{isDetailView && renderModal()}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderModal() {
|
||||||
|
if (!isModalOpen) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.modalOverlay} onClick={closeModal}>
|
||||||
|
<div
|
||||||
|
className={styles.modalContent}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
onMouseDown={handleMouseDown}
|
||||||
|
onMouseMove={handleMouseMove}
|
||||||
|
onMouseUp={handleMouseUp}
|
||||||
|
onMouseLeave={handleMouseUp}
|
||||||
|
>
|
||||||
|
<button className={styles.closeButton} onClick={closeModal}>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
height="24px"
|
||||||
|
viewBox="0 -960 960 960"
|
||||||
|
width="24px"
|
||||||
|
fill="#e3e3e3"
|
||||||
|
>
|
||||||
|
<path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div className={styles.modalImageContainer} ref={modalImageRef}>
|
||||||
|
<img
|
||||||
|
src={currentImage || "/placeholder.svg"}
|
||||||
|
alt={altText || "Ürün resmi"}
|
||||||
|
className={styles.modalImage}
|
||||||
|
style={{
|
||||||
|
transform: `scale(${scale}) rotate(${rotation}deg) translate(${position.x}px, ${position.y}px)`,
|
||||||
|
cursor:
|
||||||
|
scale > 1 ? (isDragging ? "grabbing" : "grab") : "default",
|
||||||
|
}}
|
||||||
|
draggable={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Control buttons */}
|
||||||
|
<div className={styles.modalControls}>
|
||||||
|
<button
|
||||||
|
className={styles.controlButton}
|
||||||
|
onClick={handlePrev}
|
||||||
|
title="Önceki (←)"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
>
|
||||||
|
<polyline points="15 18 9 12 15 6"></polyline>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className={styles.controlButton}
|
||||||
|
onClick={handleZoomOut}
|
||||||
|
title="Uzaklaştır (-)"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
>
|
||||||
|
<circle cx="11" cy="11" r="8"></circle>
|
||||||
|
<line x1="8" y1="11" x2="14" y2="11"></line>
|
||||||
|
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className={styles.controlButton}
|
||||||
|
onClick={handleZoomIn}
|
||||||
|
title="Yakınlaştır (+)"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
>
|
||||||
|
<circle cx="11" cy="11" r="8"></circle>
|
||||||
|
<line x1="11" y1="8" x2="11" y2="14"></line>
|
||||||
|
<line x1="8" y1="11" x2="14" y2="11"></line>
|
||||||
|
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className={styles.controlButton}
|
||||||
|
onClick={handleFullscreen}
|
||||||
|
title="Tam ekran"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
>
|
||||||
|
<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className={styles.controlButton}
|
||||||
|
onClick={handleRotateLeft}
|
||||||
|
title="Sola döndür"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
>
|
||||||
|
<path d="M2.5 2v6h6M2.66 15.57a10 10 0 1 0 .57-8.38"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className={styles.controlButton}
|
||||||
|
onClick={handleRotateRight}
|
||||||
|
title="Sağa döndür (R)"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
>
|
||||||
|
<path d="M21.5 2v6h-6M21.34 15.57a10 10 0 1 1-.57-8.38"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className={styles.controlButton}
|
||||||
|
onClick={handleReset}
|
||||||
|
title="Sıfırla"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
>
|
||||||
|
<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"></path>
|
||||||
|
<path d="M21 3v5h-5"></path>
|
||||||
|
<path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"></path>
|
||||||
|
<path d="M3 21v-5h5"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className={styles.controlButton}
|
||||||
|
onClick={handleNext}
|
||||||
|
title="Sonraki (→)"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
>
|
||||||
|
<polyline points="9 18 15 12 9 6"></polyline>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -125,6 +442,8 @@ const ImageCarousel = ({
|
|||||||
className={`${styles.imageWrapper} ${
|
className={`${styles.imageWrapper} ${
|
||||||
isDetailView ? styles.fixedFrameContainer : ""
|
isDetailView ? styles.fixedFrameContainer : ""
|
||||||
}`}
|
}`}
|
||||||
|
onClick={isDetailView ? openModal : undefined}
|
||||||
|
style={{ cursor: isDetailView ? "pointer" : "default" }}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={currentImage || "/placeholder.svg"}
|
src={currentImage || "/placeholder.svg"}
|
||||||
@@ -135,7 +454,6 @@ const ImageCarousel = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Navigation arrows */}
|
|
||||||
<button
|
<button
|
||||||
onClick={handlePrev}
|
onClick={handlePrev}
|
||||||
className={`${styles.arrowButton} ${styles.leftArrow}`}
|
className={`${styles.arrowButton} ${styles.leftArrow}`}
|
||||||
@@ -176,7 +494,6 @@ const ImageCarousel = ({
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Indicators (dots) */}
|
|
||||||
<div className={styles.indicators}>
|
<div className={styles.indicators}>
|
||||||
{images.map((_, idx) => (
|
{images.map((_, idx) => (
|
||||||
<span
|
<span
|
||||||
@@ -190,7 +507,6 @@ const ImageCarousel = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Thumbnails - only show if showThumbnails is true */}
|
|
||||||
{showThumbnails && (
|
{showThumbnails && (
|
||||||
<div className={styles.thumbnailContainer}>
|
<div className={styles.thumbnailContainer}>
|
||||||
{images.map((image, idx) => (
|
{images.map((image, idx) => (
|
||||||
@@ -205,12 +521,14 @@ const ImageCarousel = ({
|
|||||||
src={
|
src={
|
||||||
image.thumbnail || image.images_400x400 || "/placeholder.svg"
|
image.thumbnail || image.images_400x400 || "/placeholder.svg"
|
||||||
}
|
}
|
||||||
alt={`${altText || "Ürün"} thumbnail ${idx + 1}`}
|
alt={`${altText || "Product"} thumbnail ${idx + 1}`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{isDetailView && renderModal()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
width: 99%;
|
width: 99%;
|
||||||
height: auto;
|
height: auto;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
border: 1px solid #eee;
|
// border: 1px solid #eee;
|
||||||
@media screen and (max-width: 900px) {
|
@media screen and (max-width: 900px) {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user