added deploy watch, pull script. minor refactors
This commit is contained in:
@@ -3,17 +3,21 @@ import Image from "next/image";
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import '@/styles/gallery.css';
|
||||
import { type GalleryImage, imageSrc, imageDescription } from "@/data/content";
|
||||
|
||||
type GallerySize = "small" | "medium" | "large";
|
||||
type Props = {
|
||||
images: string[];
|
||||
images: GalleryImage[];
|
||||
videos?: string[];
|
||||
title: string;
|
||||
gallerySize?: GallerySize;
|
||||
};
|
||||
|
||||
export default function SimpleGallery({
|
||||
images,
|
||||
videos = [],
|
||||
title
|
||||
title,
|
||||
gallerySize = "medium"
|
||||
}: Props) {
|
||||
|
||||
const [active, setActive] = useState(0);
|
||||
@@ -28,10 +32,24 @@ export default function SimpleGallery({
|
||||
const totalMedia = images.length + videos.length;
|
||||
const isVideo = active >= images.length;
|
||||
const videoIndex = active - images.length;
|
||||
const currentSrc = isVideo ? videos[videoIndex] : images[active];
|
||||
const currentSrc = isVideo ? videos[videoIndex] : imageSrc(images[active]);
|
||||
const currentDescription = isVideo ? undefined : imageDescription(images[active]);
|
||||
|
||||
if (totalMedia === 0) return null;
|
||||
|
||||
function getGallerySize() {
|
||||
switch (gallerySize) {
|
||||
case "small":
|
||||
return { maxHeight: 150 };
|
||||
case "medium":
|
||||
return { maxHeight: 250 };
|
||||
case "large":
|
||||
return { maxHeight: 500 };
|
||||
default:
|
||||
return { maxHeight: 250 };
|
||||
}
|
||||
}
|
||||
|
||||
function handleFullscreenClose() {
|
||||
setIsEnlarged(false);
|
||||
}
|
||||
@@ -94,8 +112,8 @@ export default function SimpleGallery({
|
||||
const imageStyle: React.CSSProperties = {
|
||||
width: "100%",
|
||||
borderRadius: 4,
|
||||
objectFit: "cover",
|
||||
maxHeight: 250,
|
||||
objectFit: "contain",
|
||||
maxHeight: getGallerySize().maxHeight,
|
||||
display: "block",
|
||||
transition: "all 0.8s ease",
|
||||
zIndex: 1,
|
||||
@@ -104,7 +122,7 @@ export default function SimpleGallery({
|
||||
const videoStyle: React.CSSProperties = {
|
||||
width: "100%",
|
||||
borderRadius: 4,
|
||||
maxHeight: 250,
|
||||
maxHeight: getGallerySize().maxHeight,
|
||||
display: "block",
|
||||
transition: "all 0.8s ease",
|
||||
};
|
||||
@@ -126,7 +144,8 @@ export default function SimpleGallery({
|
||||
<Image
|
||||
id="gallery-main-image"
|
||||
src={`/${currentSrc}`}
|
||||
alt={`${title} screenshot ${active + 1}`}
|
||||
alt={currentDescription ?? `${title} screenshot ${active + 1}`}
|
||||
title={currentDescription}
|
||||
width={800}
|
||||
height={600}
|
||||
onClick={() => setIsEnlarged(true)}
|
||||
@@ -216,11 +235,17 @@ export default function SimpleGallery({
|
||||
) : (
|
||||
<Image
|
||||
src={`/${currentSrc}`}
|
||||
alt={`${title} fullscreen ${active + 1}`}
|
||||
alt={currentDescription ?? `${title} fullscreen ${active + 1}`}
|
||||
title={currentDescription}
|
||||
width={1600}
|
||||
height={1200}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
style={{
|
||||
// Auto sizing keeps the element's box tight to the visible image;
|
||||
// otherwise the letterboxed area inside the box swallows taps
|
||||
// meant for the darkened background.
|
||||
width: "auto",
|
||||
height: "auto",
|
||||
maxWidth: "95vw",
|
||||
maxHeight: "90vh",
|
||||
borderRadius: 8,
|
||||
@@ -245,27 +270,40 @@ export default function SimpleGallery({
|
||||
marginTop: 8,
|
||||
flexWrap: "wrap" }}>
|
||||
|
||||
{images.map((src, i) => (
|
||||
<Image
|
||||
key={`thumb-img-${i}`}
|
||||
src={`/${src}`}
|
||||
alt={`${title} thumbnail ${i + 1}`}
|
||||
width={48}
|
||||
height={36}
|
||||
onClick={() => handleThumbnailClick(i)}
|
||||
style={{
|
||||
width: 48,
|
||||
height: 36,
|
||||
objectFit: "cover",
|
||||
borderRadius: 3,
|
||||
cursor: "pointer",
|
||||
border: `2px solid ${i === active ? "var(--accent)" : "var(--border)"}`,
|
||||
opacity: i === active ? 1 : 0.6,
|
||||
transition: "all 0.15s ease",
|
||||
pointerEvents: "auto",
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
{images.map((image, i) => {
|
||||
const description = imageDescription(image);
|
||||
const isActive = i === active;
|
||||
return (
|
||||
<button
|
||||
key={`thumb-img-${i}`}
|
||||
className={`gallery-thumb${isActive ? " is-active" : ""}`}
|
||||
onClick={() => handleThumbnailClick(i)}
|
||||
title={description ?? `Show ${title} screenshot ${i + 1}`}
|
||||
>
|
||||
<Image
|
||||
src={`/${imageSrc(image)}`}
|
||||
alt={description ?? `${title} thumbnail ${i + 1}`}
|
||||
width={48}
|
||||
height={36}
|
||||
style={{
|
||||
width: 48,
|
||||
height: 36,
|
||||
objectFit: "cover",
|
||||
borderRadius: 3,
|
||||
border: `2px solid ${isActive ? "var(--accent)" : "var(--border)"}`,
|
||||
opacity: isActive ? 1 : 0.6,
|
||||
transition: "all 0.15s ease",
|
||||
}}
|
||||
/>
|
||||
<span className="gallery-thumb-magnifier" aria-hidden="true">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="7" cy="7" r="4.5" strokeWidth="1.75" />
|
||||
<path d="M10.5 10.5L14 14" strokeWidth="1.75" strokeLinecap="round" />
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
|
||||
{videos.map((_, i) => {
|
||||
const mediaIndex = images.length + i;
|
||||
@@ -332,6 +370,8 @@ export default function SimpleGallery({
|
||||
borderRadius: 7,
|
||||
border: `1px solid ${mediaHovered ? "var(--border-strong)" : "transparent"}`,
|
||||
transition: "border-color 0.15s ease",
|
||||
backgroundColor: "var(--overlay, #1a1a1a)",
|
||||
boxShadow: "var(--shadow-sm)"
|
||||
}}
|
||||
>
|
||||
{inlineMedia()}
|
||||
|
||||
Reference in New Issue
Block a user