Initial commit
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
"use client";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect } from "react";
|
||||
import { ChevronLeft, ChevronRight, Shuffle, Undo2 } from "lucide-react";
|
||||
import { cn, coverHref } from "@/lib/utils";
|
||||
|
||||
type Neighbor = { id: number; code: string | null } | null;
|
||||
|
||||
export function ImageNav({
|
||||
prev,
|
||||
next,
|
||||
randomEndpoint,
|
||||
}: {
|
||||
prev: Neighbor;
|
||||
next: Neighbor;
|
||||
randomEndpoint: string;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const prevHref = prev ? coverHref(prev) : null;
|
||||
const nextHref = next ? coverHref(next) : null;
|
||||
|
||||
useEffect(() => {
|
||||
const onKey = (e: KeyboardEvent) => {
|
||||
const t = e.target as HTMLElement | null;
|
||||
if (t && (t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.isContentEditable)) return;
|
||||
if (e.metaKey || e.ctrlKey || e.altKey) return;
|
||||
if (e.key === "ArrowLeft") {
|
||||
if (prevHref) { e.preventDefault(); router.push(prevHref); }
|
||||
} else if (e.key === "ArrowRight") {
|
||||
if (nextHref) { e.preventDefault(); router.push(nextHref); }
|
||||
} else if (e.key === "ArrowUp") {
|
||||
e.preventDefault();
|
||||
router.push(randomEndpoint);
|
||||
} else if (e.key === "ArrowDown") {
|
||||
e.preventDefault();
|
||||
router.back();
|
||||
}
|
||||
};
|
||||
window.addEventListener("keydown", onKey);
|
||||
return () => window.removeEventListener("keydown", onKey);
|
||||
}, [prevHref, nextHref, randomEndpoint, router]);
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-1.5">
|
||||
<NavBtn href={prevHref} label="Previous (←)">
|
||||
<ChevronLeft className="w-4 h-4" />
|
||||
</NavBtn>
|
||||
<NavBtn href={randomEndpoint} label="Random (↑)">
|
||||
<Shuffle className="w-3.5 h-3.5" />
|
||||
</NavBtn>
|
||||
<button
|
||||
onClick={() => router.back()}
|
||||
title="Last viewed (↓)"
|
||||
aria-label="Last viewed"
|
||||
className="w-8 h-8 grid place-items-center rounded-lg border border-[var(--color-glass-border)] text-[var(--color-fg-dim)] hover:text-[var(--color-fg)] hover:bg-[var(--color-glass)] transition-colors"
|
||||
>
|
||||
<Undo2 className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
<NavBtn href={nextHref} label="Next (→)">
|
||||
<ChevronRight className="w-4 h-4" />
|
||||
</NavBtn>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function NavBtn({ href, label, children }: { href: string | null; label: string; children: React.ReactNode }) {
|
||||
const className = cn(
|
||||
"w-8 h-8 grid place-items-center rounded-lg border transition-colors",
|
||||
href
|
||||
? "border-[var(--color-glass-border)] text-[var(--color-fg-dim)] hover:text-[var(--color-fg)] hover:bg-[var(--color-glass)]"
|
||||
: "border-[var(--color-glass-border)]/40 text-[var(--color-fg-muted)]/40 cursor-not-allowed",
|
||||
);
|
||||
if (!href) {
|
||||
return (
|
||||
<span aria-disabled title={label} className={className}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Link href={href} title={label} aria-label={label} className={className}>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user