feat(gallery): image detail view
This commit is contained in:
1 parent
bca93f6c6c
commit
c62153ecf6
5 files changed
+96
-53
No files matched your search
+61
-40
@@ -9,21 +9,37 @@ import (
|
||||
|
||||
const detailsID = "imgs-details"
|
||||
|
||||
// detailsContentID is the inner element of the modal that htmx swaps into.
|
||||
// It is defined by components.Modal and must not be the dialog itself.
|
||||
const detailsContentID = detailsID + "-content"
|
||||
|
||||
templ GallerySet(g gallery.Set) {
|
||||
<div class="p-5">
|
||||
@components.Modal(detailsID) {
|
||||
@GalleryImage(g.SM.Images[0].UID, &gallery.Image{}, &gallery.Image{})
|
||||
// Placeholder: only visible until the first image is swapped in.
|
||||
// prev/next are nil because relative links would be resolved
|
||||
// against the gallery index, not the image page.
|
||||
if len(g.SM.Images) > 0 {
|
||||
@GalleryImage(g.SM.Images[0].UID, nil, nil)
|
||||
}
|
||||
}
|
||||
<div
|
||||
class="grid gap-5 grid-cols-1 lg:hidden"
|
||||
>
|
||||
for _,img := range g.SM.Images {
|
||||
<img
|
||||
src={ "/images/" + img.UID + "?w=600" }
|
||||
loading="lazy"
|
||||
class="w-full"
|
||||
alt="Gallery item"
|
||||
/>
|
||||
<button
|
||||
hx-get={ templer.RelativePath(ctx, "./"+img.UID) }
|
||||
hx-target={ "#" + detailsContentID }
|
||||
hx-swap="innerHTML"
|
||||
class="block w-full cursor-pointer"
|
||||
>
|
||||
<img
|
||||
src={ "/images/" + img.UID + "?w=600" }
|
||||
loading="lazy"
|
||||
class="w-full"
|
||||
alt="Gallery item"
|
||||
/>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
<div class="hidden lg:block 3xl:hidden">
|
||||
@@ -39,37 +55,43 @@ templ GallerySet(g gallery.Set) {
|
||||
}
|
||||
|
||||
templ GalleryImage(image string, next *gallery.Image, prev *gallery.Image) {
|
||||
if next != nil {
|
||||
}
|
||||
<div>
|
||||
<div class="relative inline-block w-full overflow-hidden">
|
||||
<!-- 1. Preloaded Base Image (w=800) -->
|
||||
<img
|
||||
src={ "/images/" + image + "?w=800" }
|
||||
alt=""
|
||||
class="block w-full h-auto"
|
||||
/>
|
||||
<!-- 2. Responsive Image Layer (Overlaid on top) -->
|
||||
<img
|
||||
src={ "/images/" + image + "?w=800" }
|
||||
srcset={ "/images/" + image + "?w=400 400w, /images/" + image + "?w=800 800w, /images/" + image + "?w=1200 1200w, /images/" + image + "?w=1600 1600w" }
|
||||
sizes="(max-width: 600px) 400px, (max-width: 1200px) 800px, 1200px"
|
||||
alt="Image description"
|
||||
onload="this.classList.remove('opacity-0')"
|
||||
class="absolute inset-0 w-full h-full object-cover opacity-0 transition-opacity duration-300"
|
||||
/>
|
||||
<!-- 3. Navigation Controls -->
|
||||
if prev != nil {
|
||||
<button class="absolute left-4 top-1/2 -translate-y-1/2 z-10 p-2 text-white">
|
||||
<
|
||||
</button>
|
||||
}
|
||||
if next != nil {
|
||||
<button class="absolute right-4 top-1/2 -translate-y-1/2 z-10 text-white">
|
||||
>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
<div class="relative w-10/12 h-10/12 flex items-center justify-center overflow-hidden ">
|
||||
<!-- 1. Preloaded Base Image (w=800) -->
|
||||
<img
|
||||
src={ "/images/" + image + "?w=800" }
|
||||
alt=""
|
||||
class="absolute inset-0 w-full h-full object-contain"
|
||||
/>
|
||||
<!-- 2. Responsive Image Layer (Overlaid on top) -->
|
||||
<img
|
||||
src={ "/images/" + image + "?w=800" }
|
||||
srcset={ "/images/" + image + "?w=400 400w, /images/" + image + "?w=800 800w, /images/" + image + "?w=1200 1200w, /images/" + image + "?w=1600 1600w" }
|
||||
sizes="(max-width: 600px) 400px, (max-width: 1200px) 800px, 1200px"
|
||||
alt="Image description"
|
||||
onload="this.classList.remove('opacity-0')"
|
||||
class="absolute inset-0 w-full h-full object-contain transition-opacity duration-300"
|
||||
/>
|
||||
<!-- 3. Navigation Controls -->
|
||||
if prev != nil {
|
||||
<button
|
||||
hx-get={ templer.RelativePath(ctx, "../"+prev.UID) }
|
||||
hx-target={ "#" + detailsContentID }
|
||||
hx-swap="innerHTML"
|
||||
class="absolute left-4 top-1/2 -translate-y-1/2 z-10 p-2 text-white"
|
||||
>
|
||||
<
|
||||
</button>
|
||||
}
|
||||
if next != nil {
|
||||
<button
|
||||
hx-get={ templer.RelativePath(ctx, "../"+next.UID) }
|
||||
hx-target={ "#" + detailsContentID }
|
||||
hx-swap="innerHTML"
|
||||
class="absolute right-4 top-1/2 -translate-y-1/2 z-10 p-2 text-white"
|
||||
>
|
||||
>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -84,9 +106,8 @@ templ Gallery(g gallery.Gallery) {
|
||||
class="overflow-hidden shadow-sm"
|
||||
>
|
||||
<button
|
||||
popovertarget={ detailsID }
|
||||
hx-get={ templer.RelativePath(ctx, "./"+img.UID) }
|
||||
hx-target={ "#" + detailsID }
|
||||
hx-target={ "#" + detailsContentID }
|
||||
hx-swap="innerHTML"
|
||||
>
|
||||
<img
|
||||
|
||||
Reference in new issue
Block a user