yet more mobile improvements.

This commit is contained in:
Hunter W.
2026-06-20 18:07:14 -04:00
parent 7169f76ddc
commit c09094b968
6 changed files with 93 additions and 87 deletions

View File

@@ -5,7 +5,7 @@ import type { Project } from "@/data/content";
import SimpleGallery from "./SimpleGallery";
import Link from "next/link";
import { relative } from "path";
import "../styles/project-card.css";
type Props = {
project: Project;
@@ -114,7 +114,7 @@ export default function ProjectCard({ project, visible, selected = false, setSel
style={{
minWidth: "10em",
maxWidth: "90vw",
overflow: "hidden",
textDecoration: "none",
padding: 28,
marginTop: 3,
@@ -130,9 +130,7 @@ export default function ProjectCard({ project, visible, selected = false, setSel
pointerEvents: "auto",
cursor: "pointer"
}}>
<div
style={{display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 12
}}>
<div className="project-card-title-year-box">
<div
style={{fontFamily: "var(--mono)", fontSize: 18, fontWeight: 700, color: hovered ? "var(--accent)" : "var(--fg)", transition: "color 0.2s"}}>
{project.title}
@@ -142,7 +140,8 @@ export default function ProjectCard({ project, visible, selected = false, setSel
</div>
</div>
{projectTagsComponent(project)}
<p style={{fontFamily: "var(--sans)", fontSize: 14, lineHeight: 1.6, color: "var(--fg-secondary)", margin: "0 0 16px 0"}}>
<p
style={{fontFamily: "var(--sans)", fontSize: 14, lineHeight: 1.6, color: "var(--fg-secondary)", margin: "0 0 16px 0"}}>
{project.description}
</p>
@@ -158,15 +157,11 @@ export default function ProjectCard({ project, visible, selected = false, setSel
border: `1px solid ${hovered ? "var(--accent)" : "var(--border)"}`,
backgroundColor: "var(--bg)",
height: "3em",
display: "block",
alignContent: "center",
alignItems: "center",
textAlign: "center",
width: "10em",
borderRadius: "var(--radius-md)",
position: "absolute",
right: "1em",
bottom: "1em"
}} href={"/project/"+project.slug} >See More</Link>
</div>
);

View File

@@ -1,6 +1,6 @@
import Image from "next/image";
import { useState, useEffect, useRef, useCallback } from "react";
import { useState, useEffect, useRef } from "react";
import { createPortal } from "react-dom";
import '@/styles/gallery.css';
@@ -10,9 +10,6 @@ type Props = {
title: string;
};
// 0 = inline default, 1 = inline enlarged, 2 = fullscreen overlay
type EnlargeLevel = 0 | 1 | 2;
export default function SimpleGallery({
images,
videos = [],
@@ -20,9 +17,10 @@ export default function SimpleGallery({
}: Props) {
const [active, setActive] = useState(0);
const [enlargeLevel, setEnlargeLevel] = useState<EnlargeLevel>(0);
const [isEnlarged, setIsEnlarged] = useState<Boolean>(false);
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const videoRef = useRef<HTMLVideoElement | null>(null);
const fsPortalRef = useRef<HTMLDivElement | null>(null);
const totalMedia = images.length + videos.length;
const isVideo = active >= images.length;
@@ -31,24 +29,8 @@ export default function SimpleGallery({
if (totalMedia === 0) return null;
function handleEnlarge() {
if (enlargeLevel === 0) {
setEnlargeLevel(1);
} else if (enlargeLevel === 1) {
setEnlargeLevel(2);
}
}
function handleShrink() {
if (enlargeLevel === 2) {
setEnlargeLevel(1);
} else if (enlargeLevel === 1) {
setEnlargeLevel(0);
}
}
function handleFullscreenClose() {
setEnlargeLevel(1);
setIsEnlarged(false);
}
function handleThumbnailClick(index: number) {
@@ -57,14 +39,22 @@ export default function SimpleGallery({
function expandButton() {
return (
<button
className="gallery-action-button"
style={{
<div
className="gallery-action-button"
style={{
top: 8,
right: 8
right: 8,
fontFamily: 'var(--mono)',
fontSize: 'var(--text-xs)',
color: 'var(--muted)',
textWrap: 'nowrap',
cursor: 'pointer',
verticalAlign: 'middle'
}}
onClick={handleEnlarge}
title="Enlarge image"
>
<button
onClick={()=>setIsEnlarged(true)}
title="Expand media"
>
<svg className="action-button-svg" stroke="current" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 5V1H5" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
@@ -73,6 +63,8 @@ export default function SimpleGallery({
<path d="M13 13L8.5 8.5" strokeWidth="1.5" strokeLinecap="round" />
</svg>
</button>
Expand
</div>
);
}
@@ -84,7 +76,7 @@ export default function SimpleGallery({
bottom: 8,
right: 8
}}
onClick={handleShrink}
onClick={()=>setIsEnlarged(false)}
title="Shrink image"
>
<svg viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg" width="14" height="14">
@@ -96,17 +88,8 @@ export default function SimpleGallery({
}
function inlineMedia() {
const enlarged = enlargeLevel === 1;
const imageStyle: React.CSSProperties = enlarged ? {
maxWidth: "100%",
maxHeight: "90vh",
height: "auto",
borderRadius: 6,
objectFit: "contain",
boxShadow: "0 8px 40px rgba(0,0,0,0.6)",
transition: "all 0.3s ease",
} : {
const imageStyle: React.CSSProperties = {
width: "100%",
borderRadius: 4,
objectFit: "cover",
@@ -116,14 +99,7 @@ export default function SimpleGallery({
zIndex: 1,
};
const videoStyle: React.CSSProperties = enlarged ? {
maxWidth: "100%",
maxHeight: "90vh",
objectFit: "contain" as const,
borderRadius: 6,
boxShadow: "0 8px 40px rgba(0,0,0,0.6)",
transition: "all 0.3s ease",
} : {
const videoStyle: React.CSSProperties = {
width: "100%",
borderRadius: 4,
maxHeight: 250,
@@ -155,22 +131,24 @@ export default function SimpleGallery({
/>
)}
{enlargeLevel === 0 && expandButton()}
{enlargeLevel === 1 && (
<>
{shrinkButton()}
{expandButton()}
</>
)}
{!isEnlarged && expandButton()}
{isEnlarged && shrinkButton()}
</div>
);
}
function fullscreenOverlay() {
if (enlargeLevel !== 2) return null;
if (!isEnlarged) return null;
setTimeout(()=>{
if(!fsPortalRef.current) return
fsPortalRef.current.style.opacity = "1"
}, 50);
return createPortal(
<div
ref={fsPortalRef}
className="gallery-full-screen-portal"
onClick={handleFullscreenClose}
style={{
position: "fixed",
@@ -180,7 +158,7 @@ export default function SimpleGallery({
display: "flex",
alignItems: "center",
justifyContent: "center",
padding: 24,
padding: 24
}}
>
@@ -258,7 +236,11 @@ export default function SimpleGallery({
if (totalMedia <= 1) return null;
return (
<div style={{ display: "flex", gap: 6, marginTop: 8, flexWrap: "wrap" }}>
<div style={{
display: "flex",
gap: 6,
marginTop: 8,
flexWrap: "wrap" }}>
{images.map((src, i) => (
<Image
@@ -323,14 +305,13 @@ export default function SimpleGallery({
// close fullscreen on Escape
useEffect(() => {
if (enlargeLevel === 0) return;
if (!isEnlarged) return;
function onKeyDown(e: KeyboardEvent) {
if (e.key === "Escape" && enlargeLevel === 2) handleFullscreenClose();
if (e.key === "Escape" && enlargeLevel === 1) handleShrink();
if (e.key === "Escape" && isEnlarged) handleFullscreenClose();
}
window.addEventListener("keydown", onKeyDown);
return () => window.removeEventListener("keydown", onKeyDown);
}, [enlargeLevel]);
}, [isEnlarged]);
return (
<>